diff --git a/include/FreeRTOS.h b/include/FreeRTOS.h index 31eb4d7ae..db63be765 100644 --- a/include/FreeRTOS.h +++ b/include/FreeRTOS.h @@ -3291,6 +3291,11 @@ typedef struct xSTATIC_TCB #if ( configUSE_POSIX_ERRNO == 1 ) int iDummy22; #endif + + #if ( configQUEUE_DIRECT_TRANSFER == 1 ) + void * pvDummyDirectTransferBuffer; + BaseType_t xDummyDirectTransferPosition; + #endif } StaticTask_t; /* @@ -3337,11 +3342,6 @@ typedef struct xSTATIC_QUEUE #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) portSPINLOCK_TYPE xDummySpinlock[ 2 ]; #endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */ - - #if ( configQUEUE_DIRECT_TRANSFER == 1 ) - void * pvDummyDirectTransferBuffer; - BaseType_t xDummyDirectTransferPosition; - #endif } StaticQueue_t; typedef StaticQueue_t StaticSemaphore_t; diff --git a/include/task.h b/include/task.h index 7622cbcb3..9539ed60e 100644 --- a/include/task.h +++ b/include/task.h @@ -3758,6 +3758,68 @@ BaseType_t xTaskRemoveFromEventListFromISR( const List_t * const pxEventList ) P void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) PRIVILEGED_FUNCTION; +#if ( configQUEUE_DIRECT_TRANSFER == 1 ) + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE FOR THE EXCLUSIVE USE OF THE QUEUE IMPLEMENTATION. + * + * Set the direct transfer buffer for the current task. + * Called when a task is about to block on a queue operation. + */ + void vTaskSetDirectTransferBuffer( void * pvBuffer, + BaseType_t xPosition, + TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE FOR THE EXCLUSIVE USE OF THE QUEUE IMPLEMENTATION. + * + * Clear the direct transfer buffer for a task. + * @param xTask The task handle + */ + void vTaskClearDirectTransferBuffer( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE FOR THE EXCLUSIVE USE OF THE QUEUE IMPLEMENTATION. + * + * Get the direct transfer buffer pointer from a task. + * @param xTask The task handle + * @return The buffer pointer, or NULL if not set + * + */ + void * pvTaskGetDirectTransferBuffer( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE FOR THE EXCLUSIVE USE OF THE QUEUE IMPLEMENTATION. + * + * Get the direct transfer position from a task. + * @param xTask The task handle + * @return The position, or -1 if not set + * + */ + BaseType_t xTaskGetDirectTransferPosition( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE FOR THE EXCLUSIVE USE OF THE QUEUE IMPLEMENTATION. + * + * Get the highest priority task from an event list if it has armed direct transfer. + * Checks only the head of the event list (O(1) operation) for deterministic behavior. + * + * If the highest priority task hasn't armed direct transfer (e.g., using xQueuePeek()), + * returns NULL and direct transfer is skipped for this operation. This is acceptable since + * direct transfer is an optimization, not a requirement. + * + * @param pxEventList The event list to check + * @return Task handle of highest priority task if it has armed transfer, or NULL otherwise + */ + TaskHandle_t xTaskGetHighestPriorityTaskWithDirectTransferArmed( const List_t * const pxEventList ) PRIVILEGED_FUNCTION; + +#endif /* configQUEUE_DIRECT_TRANSFER */ + /* * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS diff --git a/queue.c b/queue.c index 6ac171619..96c27ac5a 100644 --- a/queue.c +++ b/queue.c @@ -143,14 +143,6 @@ typedef struct QueueDefinition /* The old naming convention is used to prevent b portSPINLOCK_TYPE xTaskSpinlock; portSPINLOCK_TYPE xISRSpinlock; #endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */ - - #if ( configQUEUE_DIRECT_TRANSFER == 1 ) - void * pvDirectTransferBuffer; /**< Direct transfer buffer pointer: - * - When queue is EMPTY: points to receiver's buffer - * - When queue is FULL: points to sender's data - * NULL when idle */ - BaseType_t xDirectTransferPosition; /**< Position for direct transfer (queueSEND_TO_BACK, queueSEND_TO_FRONT, queueOVERWRITE) */ - #endif } xQUEUE; /* The old xQUEUE name is maintained above then typedefed to the new Queue_t @@ -463,13 +455,6 @@ BaseType_t xQueueGenericReset( QueueHandle_t xQueue, pxQueue->cRxLock = queueUNLOCKED; pxQueue->cTxLock = queueUNLOCKED; - #if ( configQUEUE_DIRECT_TRANSFER == 1 ) - { - pxQueue->pvDirectTransferBuffer = NULL; - pxQueue->xDirectTransferPosition = queueDIRECT_TRANSFER_POSITION_INIT; - } - #endif - if( xNewQueue == pdFALSE ) { /* If there are tasks blocked waiting to read from the queue, then @@ -1102,6 +1087,9 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, Queue_t * const pxQueue = xQueue; #if ( configQUEUE_DIRECT_TRANSFER == 1 ) + + /* Track whether we've armed direct transfer in task's TCB. + * This prevents false positives on first loop iteration. */ BaseType_t xDirectTransferArmed = pdFALSE; #endif @@ -1122,29 +1110,33 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, { #if ( configQUEUE_DIRECT_TRANSFER == 1 ) { - /* Check if we were unblocked due to a direct transfer completion. */ - if( ( pxQueue->pvDirectTransferBuffer == NULL ) && ( xDirectTransferArmed == pdTRUE ) ) + /* Check if we were unblocked due to a direct transfer completion. + * Only check if we actually armed the buffer (to avoid false positives on first iteration). */ + if( xDirectTransferArmed == pdTRUE ) { - /* Buffer pointer is cleared and we armed it. The receiver executed the - * direct transfer and our data was copied to the queue. */ - xDirectTransferArmed = pdFALSE; - queueEXIT_CRITICAL( pxQueue ); + void * pvCurrentBuffer = pvTaskGetDirectTransferBuffer( NULL ); - traceQUEUE_SEND( pxQueue ); - traceRETURN_xQueueGenericSend( pdPASS ); - return pdPASS; - } - else if( pxQueue->pvDirectTransferBuffer == pvItemToQueue ) - { - /* We armed direct transfer but receiver didn't execute it - clear and try normal path. */ - pxQueue->pvDirectTransferBuffer = NULL; - xDirectTransferArmed = pdFALSE; - } - else - { - /* Buffer doesn't match our data (someone else armed it or it was cleared - * for another task) - just clear our flag and continue. */ - xDirectTransferArmed = pdFALSE; + if( pvCurrentBuffer == NULL ) + { + /* Our buffer was cleared - direct transfer completed successfully. */ + xDirectTransferArmed = pdFALSE; + queueEXIT_CRITICAL( pxQueue ); + + traceQUEUE_SEND( pxQueue ); + traceRETURN_xQueueGenericSend( pdPASS ); + return pdPASS; + } + else if( pvCurrentBuffer == pvItemToQueue ) + { + /* We armed direct transfer but receiver didn't use it - clear and try normal path. */ + vTaskClearDirectTransferBuffer( NULL ); + xDirectTransferArmed = pdFALSE; + } + else + { + /* Buffer doesn't match - shouldn't happen, but clear our flag. */ + xDirectTransferArmed = pdFALSE; + } } } #endif /* if ( configQUEUE_DIRECT_TRANSFER == 1 ) */ @@ -1221,34 +1213,37 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, { #if ( configQUEUE_DIRECT_TRANSFER == 1 ) { - /* Check if direct transfer is armed (receiver is waiting on empty queue). */ - if( ( pxQueue->pvDirectTransferBuffer != NULL ) && - ( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) && + /* Check if there's a receiver waiting with armed direct transfer. */ + if( ( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) && ( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) ) { - /* Direct copy to waiting receiver's buffer. */ - ( void ) memcpy( pxQueue->pvDirectTransferBuffer, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); + /* Find highest priority receiver with armed direct transfer. */ + TaskHandle_t xReceivingTask = xTaskGetHighestPriorityTaskWithDirectTransferArmed( &( pxQueue->xTasksWaitingToReceive ) ); - /* Clear buffer pointer - receiver will detect this. */ - pxQueue->pvDirectTransferBuffer = NULL; - - /* Unblock the waiting receiver. */ - if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + if( xReceivingTask != NULL ) { - queueYIELD_IF_USING_PREEMPTION(); - } - else - { - mtCOVERAGE_TEST_MARKER(); - } + void * pvReceiverBuffer = pvTaskGetDirectTransferBuffer( xReceivingTask ); - queueEXIT_CRITICAL( pxQueue ); - traceRETURN_xQueueGenericSend( pdPASS ); - return pdPASS; - } - else - { - mtCOVERAGE_TEST_MARKER(); + /* Direct copy to waiting receiver's buffer. */ + ( void ) memcpy( pvReceiverBuffer, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); + + /* Clear receiver's buffer pointer. */ + vTaskClearDirectTransferBuffer( xReceivingTask ); + + /* Unblock the waiting receiver. */ + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + queueEXIT_CRITICAL( pxQueue ); + traceRETURN_xQueueGenericSend( pdPASS ); + return pdPASS; + } } } #endif /* configQUEUE_DIRECT_TRANSFER */ @@ -1259,13 +1254,6 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, * queue then unblock it now. */ if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) { - #if ( configQUEUE_DIRECT_TRANSFER == 1 ) - { - /* Clear direct transfer state as task being unblocked. */ - pxQueue->pvDirectTransferBuffer = NULL; - } - #endif - if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) { /* The unblocked task has a priority higher than @@ -1345,21 +1333,9 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue, #if ( configQUEUE_DIRECT_TRANSFER == 1 ) { - /* Arm direct transfer only if we'll be the highest priority waiter. - * This happens when: (1) list is empty, OR (2) our priority is higher - * than the current highest priority waiting task. */ - const UBaseType_t uxCurrentPriority = uxTaskPriorityGet( NULL ); - const UBaseType_t uxHighestWaitingPriority = prvGetHighestPriorityOfWaitingTasks( &( pxQueue->xTasksWaitingToSend ) ); - - if( uxCurrentPriority > uxHighestWaitingPriority ) - { - /* We're higher priority than any current waiter (or list is empty, - * in which case uxHighestWaitingPriority is tskIDLE_PRIORITY). - * Arm direct transfer using pvDirectTransferBuffer (queue is full). */ - pxQueue->pvDirectTransferBuffer = ( void * ) pvItemToQueue; - pxQueue->xDirectTransferPosition = xCopyPosition; - xDirectTransferArmed = pdTRUE; - } + /* Arm direct transfer in current task's TCB. Any receiver that unblocks us can check our TCB for the buffer. */ + vTaskSetDirectTransferBuffer( ( void * ) pvItemToQueue, xCopyPosition, NULL ); + xDirectTransferArmed = pdTRUE; } #endif /* if ( configQUEUE_DIRECT_TRANSFER == 1 ) */ @@ -1442,35 +1418,42 @@ BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, #if ( configQUEUE_DIRECT_TRANSFER == 1 ) { - /* Check if direct transfer is armed (receiver is waiting on empty queue). */ - if( ( pxQueue->pvDirectTransferBuffer != NULL ) && - ( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) && + /* Check if there's a receiver waiting with armed direct transfer. */ + if( ( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) && ( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) && ( cTxLock == queueUNLOCKED ) ) { - /* Direct copy to waiting receiver's buffer. */ - ( void ) memcpy( pxQueue->pvDirectTransferBuffer, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); + /* Find highest priority receiver with armed direct transfer. */ + TaskHandle_t xReceivingTask = xTaskGetHighestPriorityTaskWithDirectTransferArmed( &( pxQueue->xTasksWaitingToReceive ) ); - /* Clear buffer pointer - receiver will detect this. */ - pxQueue->pvDirectTransferBuffer = NULL; - - /* Unblock the waiting receiver. */ - if( xTaskRemoveFromEventListFromISR( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + if( xReceivingTask != NULL ) { - if( pxHigherPriorityTaskWoken != NULL ) - { - *pxHigherPriorityTaskWoken = pdTRUE; - } - else - { - mtCOVERAGE_TEST_MARKER(); - } - } + void * pvReceiverBuffer = pvTaskGetDirectTransferBuffer( xReceivingTask ); - xReturn = pdPASS; - queueEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus, pxQueue ); - traceRETURN_xQueueGenericSendFromISR( xReturn ); - return xReturn; + /* Direct copy to waiting receiver's buffer. */ + ( void ) memcpy( pvReceiverBuffer, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); + + /* Clear receiver's buffer pointer. */ + vTaskClearDirectTransferBuffer( xReceivingTask ); + + /* Unblock the waiting receiver. */ + if( xTaskRemoveFromEventListFromISR( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + xReturn = pdPASS; + queueEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus, pxQueue ); + traceRETURN_xQueueGenericSendFromISR( xReturn ); + return xReturn; + } } } #endif /* configQUEUE_DIRECT_TRANSFER */ @@ -1543,13 +1526,6 @@ BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, { if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) { - #if ( configQUEUE_DIRECT_TRANSFER == 1 ) - { - /* Clear direct transfer state as task being unblocked. */ - pxQueue->pvDirectTransferBuffer = NULL; - } - #endif - if( xTaskRemoveFromEventListFromISR( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) { /* The task waiting has a higher priority so record that a @@ -1781,6 +1757,9 @@ BaseType_t xQueueReceive( QueueHandle_t xQueue, Queue_t * const pxQueue = xQueue; #if ( configQUEUE_DIRECT_TRANSFER == 1 ) + + /* Track whether we've armed direct transfer in task's TCB. + * This prevents false positives on first loop iteration. */ BaseType_t xDirectTransferArmed = pdFALSE; #endif @@ -1806,16 +1785,15 @@ BaseType_t xQueueReceive( QueueHandle_t xQueue, { #if ( configQUEUE_DIRECT_TRANSFER == 1 ) { - /* Check if we were unblocked due to direct transfer completion. */ - if( ( pxQueue->pvDirectTransferBuffer == NULL ) && ( xDirectTransferArmed == pdTRUE ) ) + /* Check if we were unblocked due to a direct transfer completion. + * Only check if we actually armed the buffer (to avoid false positives on first iteration). */ + if( xDirectTransferArmed == pdTRUE ) { - /* Buffer is cleared and we armed it. Verify this was actually a direct - * transfer to us by checking the queue is still empty. If queue has data, - * it means we were unblocked for normal receive (our buffer was overwritten - * by higher priority task, and data was queued normally). */ - if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) + void * pvCurrentBuffer = pvTaskGetDirectTransferBuffer( NULL ); + + if( pvCurrentBuffer == NULL ) { - /* Queue empty - direct transfer to us succeeded! */ + /* Our buffer was cleared - direct transfer completed successfully. */ xDirectTransferArmed = pdFALSE; queueEXIT_CRITICAL( pxQueue ); @@ -1823,24 +1801,17 @@ BaseType_t xQueueReceive( QueueHandle_t xQueue, traceRETURN_xQueueReceive( pdPASS ); return pdPASS; } - else + else if( pvCurrentBuffer == pvBuffer ) { - /* Queue has data - this wasn't a direct transfer to us. - * Continue to normal receive path. */ + /* We armed direct transfer but sender didn't use it - clear and try normal path. */ + vTaskClearDirectTransferBuffer( NULL ); + xDirectTransferArmed = pdFALSE; + } + else + { + /* Buffer doesn't match - shouldn't happen, but clear our flag. */ xDirectTransferArmed = pdFALSE; } - } - else if( pxQueue->pvDirectTransferBuffer == pvBuffer ) - { - /* We armed direct transfer but sender didn't deliver - clear and try normal path. */ - pxQueue->pvDirectTransferBuffer = NULL; - xDirectTransferArmed = pdFALSE; - } - else - { - /* Buffer doesn't match our buffer (someone else armed it or it was cleared - * for another task) - just clear our flag and continue. */ - xDirectTransferArmed = pdFALSE; } } #endif /* if ( configQUEUE_DIRECT_TRANSFER == 1 ) */ @@ -1863,20 +1834,26 @@ BaseType_t xQueueReceive( QueueHandle_t xQueue, { #if ( configQUEUE_DIRECT_TRANSFER == 1 ) { - /* Check if a direct transfer is armed (sender is waiting on a full queue). */ - if( pxQueue->pvDirectTransferBuffer != NULL ) + /* Check if there's a sender waiting with armed direct transfer. + * Find highest priority sender with armed direct transfer. */ + TaskHandle_t xSendingTask = xTaskGetHighestPriorityTaskWithDirectTransferArmed( &( pxQueue->xTasksWaitingToSend ) ); + + if( xSendingTask != NULL ) { + void * pvSenderBuffer = pvTaskGetDirectTransferBuffer( xSendingTask ); + BaseType_t xPosition = xTaskGetDirectTransferPosition( xSendingTask ); + /* Verify that position was set to a valid value */ - configASSERT( pxQueue->xDirectTransferPosition != queueDIRECT_TRANSFER_POSITION_INIT ); - configASSERT( ( pxQueue->xDirectTransferPosition == queueSEND_TO_BACK ) || - ( pxQueue->xDirectTransferPosition == queueSEND_TO_FRONT ) || - ( pxQueue->xDirectTransferPosition == queueOVERWRITE ) ); + configASSERT( xPosition != queueDIRECT_TRANSFER_POSITION_INIT ); + configASSERT( ( xPosition == queueSEND_TO_BACK ) || + ( xPosition == queueSEND_TO_FRONT ) || + ( xPosition == queueOVERWRITE ) ); /* Direct copy from waiting sender's buffer to the queue. */ - ( void ) prvCopyDataToQueue( pxQueue, pxQueue->pvDirectTransferBuffer, pxQueue->xDirectTransferPosition ); + ( void ) prvCopyDataToQueue( pxQueue, pvSenderBuffer, xPosition ); - /* Clear buffer pointer - sender will be unblocked below. */ - pxQueue->pvDirectTransferBuffer = NULL; + /* Clear sender's buffer pointer. */ + vTaskClearDirectTransferBuffer( xSendingTask ); } } #endif /* configQUEUE_DIRECT_TRANSFER */ @@ -1947,19 +1924,9 @@ BaseType_t xQueueReceive( QueueHandle_t xQueue, #if ( configQUEUE_DIRECT_TRANSFER == 1 ) { - /* Arm direct transfer only if we'll be the highest priority waiter. - * This happens when: (1) list is empty, OR (2) our priority is higher - * than the current highest waiting task. */ - const UBaseType_t uxCurrentPriority = uxTaskPriorityGet( NULL ); - const UBaseType_t uxHighestWaitingPriority = prvGetHighestPriorityOfWaitingTasks( &( pxQueue->xTasksWaitingToReceive ) ); - - if( uxCurrentPriority > uxHighestWaitingPriority ) - { - /* We're higher priority than any current waiter (or list is empty, - * in which case uxHighestWaitingPriority is tskIDLE_PRIORITY). */ - pxQueue->pvDirectTransferBuffer = pvBuffer; - xDirectTransferArmed = pdTRUE; - } + /* Arm direct transfer in current task's TCB. Any sender that unblocks us can check our TCB for the buffer. */ + vTaskSetDirectTransferBuffer( pvBuffer, queueDIRECT_TRANSFER_POSITION_INIT, NULL ); + xDirectTransferArmed = pdTRUE; } #endif /* if ( configQUEUE_DIRECT_TRANSFER == 1 ) */ @@ -2403,21 +2370,26 @@ BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, { #if ( configQUEUE_DIRECT_TRANSFER == 1 ) { - /* Check if a direct transfer is armed (sender is waiting on full queue). - * When queue is not empty, pvDirectTransferBuffer is used to point to sender's data. */ - if( pxQueue->pvDirectTransferBuffer != NULL ) + /* Check if there's a sender waiting with armed direct transfer. + * Find highest priority sender with armed direct transfer. */ + TaskHandle_t xSendingTask = xTaskGetHighestPriorityTaskWithDirectTransferArmed( &( pxQueue->xTasksWaitingToSend ) ); + + if( xSendingTask != NULL ) { + void * pvSenderBuffer = pvTaskGetDirectTransferBuffer( xSendingTask ); + BaseType_t xPosition = xTaskGetDirectTransferPosition( xSendingTask ); + /* Verify that position was set to a valid value. */ - configASSERT( pxQueue->xDirectTransferPosition != queueDIRECT_TRANSFER_POSITION_INIT ); - configASSERT( ( pxQueue->xDirectTransferPosition == queueSEND_TO_BACK ) || - ( pxQueue->xDirectTransferPosition == queueSEND_TO_FRONT ) || - ( pxQueue->xDirectTransferPosition == queueOVERWRITE ) ); + configASSERT( xPosition != queueDIRECT_TRANSFER_POSITION_INIT ); + configASSERT( ( xPosition == queueSEND_TO_BACK ) || + ( xPosition == queueSEND_TO_FRONT ) || + ( xPosition == queueOVERWRITE ) ); /* Direct copy from waiting sender's buffer to the queue. */ - ( void ) prvCopyDataToQueue( pxQueue, pxQueue->pvDirectTransferBuffer, pxQueue->xDirectTransferPosition ); + ( void ) prvCopyDataToQueue( pxQueue, pvSenderBuffer, xPosition ); - /* Clear buffer pointer - sender will be unblocked below. */ - pxQueue->pvDirectTransferBuffer = NULL; + /* Clear sender's buffer pointer. */ + vTaskClearDirectTransferBuffer( xSendingTask ); } } #endif /* configQUEUE_DIRECT_TRANSFER */ @@ -2596,13 +2568,6 @@ void vQueueDelete( QueueHandle_t xQueue ) configASSERT( pxQueue ); traceQUEUE_DELETE( pxQueue ); - #if ( configQUEUE_DIRECT_TRANSFER == 1 ) - { - /* Clear direct transfer state before deleting queue. */ - pxQueue->pvDirectTransferBuffer = NULL; - } - #endif - #if ( configQUEUE_REGISTRY_SIZE > 0 ) { vQueueUnregisterQueue( pxQueue ); @@ -2879,13 +2844,6 @@ static void prvUnlockQueue( Queue_t * const pxQueue ) * suspended. */ if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) { - #if ( configQUEUE_DIRECT_TRANSFER == 1 ) - { - /* Clear direct transfer state as task being unblocked. */ - pxQueue->pvDirectTransferBuffer = NULL; - } - #endif - if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) { /* The task waiting has a higher priority so record that a @@ -2909,13 +2867,6 @@ static void prvUnlockQueue( Queue_t * const pxQueue ) * the pending ready list as the scheduler is still suspended. */ if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) { - #if ( configQUEUE_DIRECT_TRANSFER == 1 ) - { - /* Clear direct transfer state as task being unblocked. */ - pxQueue->pvDirectTransferBuffer = NULL; - } - #endif - if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) { /* The task waiting has a higher priority so record that diff --git a/tasks.c b/tasks.c index 23ccd6b8e..cccbd3438 100644 --- a/tasks.c +++ b/tasks.c @@ -516,6 +516,14 @@ typedef struct tskTaskControlBlock /* The old naming convention is used to #if ( configUSE_POSIX_ERRNO == 1 ) int iTaskErrno; #endif + + #if ( configQUEUE_DIRECT_TRANSFER == 1 ) + void * pvDirectTransferBuffer; /**< Direct transfer buffer pointer for data group send/receive operations: + * - When waiting to RECEIVE: points to this task's receive buffer + * - When waiting to SEND: points to this task's send data + * NULL when not using direct transfer */ + BaseType_t xDirectTransferPosition; /**< Position for direct transfer (queueSEND_TO_BACK, queueSEND_TO_FRONT, queueOVERWRITE) */ + #endif } tskTCB; /* The old tskTCB name is maintained above then typedefed to the new TCB_t name @@ -717,6 +725,31 @@ static BaseType_t prvTaskRemoveFromEventList( const List_t * const pxEventList ) static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely ) PRIVILEGED_FUNCTION; +#if ( configQUEUE_DIRECT_TRANSFER == 1 ) + +/* + * Set the direct transfer buffer for a task. + * Called when a task is about to block on a data group operation. + */ + void vTaskSetDirectTransferBuffer( void * pvBuffer, + BaseType_t xPosition, + TaskHandle_t xTask ); + +/* + * Clear the direct transfer buffer for a task. + * This function should be called from data group send/receive operations when cleaning up. + */ + void vTaskClearDirectTransferBuffer( TaskHandle_t xTask ); + +/* + * Get the highest priority task from an event list that has armed direct transfer. + * Returns NULL if no task with armed direct transfer is found. + * This function should be called from data group send/receive operations to find a task with direct transfer armed. + */ + TaskHandle_t xTaskGetHighestPriorityTaskWithDirectTransferArmed( const List_t * const pxEventList ); + +#endif /* configQUEUE_DIRECT_TRANSFER */ + /* * Fills an TaskStatus_t structure with information on each task that is * referenced from the pxList list (which may be a ready list, a delayed list, @@ -9256,6 +9289,133 @@ TickType_t uxTaskResetEventItemValue( void ) #endif /* if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) */ /*-----------------------------------------------------------*/ +#if ( configQUEUE_DIRECT_TRANSFER == 1 ) + + void vTaskSetDirectTransferBuffer( void * pvBuffer, + BaseType_t xPosition, + TaskHandle_t xTask ) + { + TCB_t * pxTCB; + + /* With granular locks, we need kernel critical section for TCB access. */ + #if ( portUSING_GRANULAR_LOCKS == 1 ) + kernelENTER_CRITICAL(); + #endif + { + pxTCB = prvGetTCBFromHandle( xTask ); + + if( pxTCB != NULL ) + { + pxTCB->pvDirectTransferBuffer = pvBuffer; + pxTCB->xDirectTransferPosition = xPosition; + } + } + #if ( portUSING_GRANULAR_LOCKS == 1 ) + kernelEXIT_CRITICAL(); + #endif + } + + void vTaskClearDirectTransferBuffer( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + + /* With granular locks, we need kernel critical section for TCB access. */ + #if ( portUSING_GRANULAR_LOCKS == 1 ) + kernelENTER_CRITICAL(); + #endif + { + pxTCB = prvGetTCBFromHandle( xTask ); + + if( pxTCB != NULL ) + { + pxTCB->pvDirectTransferBuffer = NULL; + } + } + #if ( portUSING_GRANULAR_LOCKS == 1 ) + kernelEXIT_CRITICAL(); + #endif + } + + void * pvTaskGetDirectTransferBuffer( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + void * pvReturn = NULL; + + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + #if ( portUSING_GRANULAR_LOCKS == 1 ) + kernelENTER_CRITICAL(); + #endif + { + pvReturn = pxTCB->pvDirectTransferBuffer; + } + #if ( portUSING_GRANULAR_LOCKS == 1 ) + kernelEXIT_CRITICAL(); + #endif + + return pvReturn; + } + + BaseType_t xTaskGetDirectTransferPosition( TaskHandle_t xTask ) + { + TCB_t * pxTCB; + BaseType_t xReturn = ( ( BaseType_t ) -1 ); + + pxTCB = prvGetTCBFromHandle( xTask ); + configASSERT( pxTCB != NULL ); + + #if ( portUSING_GRANULAR_LOCKS == 1 ) + kernelENTER_CRITICAL(); + #endif + { + xReturn = pxTCB->xDirectTransferPosition; + } + #if ( portUSING_GRANULAR_LOCKS == 1 ) + kernelEXIT_CRITICAL(); + #endif + + return xReturn; + } + + TaskHandle_t xTaskGetHighestPriorityTaskWithDirectTransferArmed( const List_t * const pxEventList ) + { + TaskHandle_t xReturn = NULL; + + if( listCURRENT_LIST_LENGTH( pxEventList ) > 0U ) + { + TCB_t * pxTCB; + + /* Event lists are sorted by priority (highest first). + * Get the head (highest priority task) and check if it has armed direct transfer. + * This is O(1) operation, making it deterministic for RTOS requirements. + * + * If the highest priority task hasn't armed direct transfer (e.g., it's using + * xQueuePeek instead of xQueueReceive), we skip direct transfer for this operation. + * This is acceptable since direct transfer is an optimization, not a requirement. */ + pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); + + #if ( portUSING_GRANULAR_LOCKS == 1 ) + /* Check if armed within kernel critical section */ + kernelENTER_CRITICAL(); + #endif + { + if( pxTCB->pvDirectTransferBuffer != NULL ) + { + xReturn = ( TaskHandle_t ) pxTCB; + } + } + #if ( portUSING_GRANULAR_LOCKS == 1 ) + kernelEXIT_CRITICAL(); + #endif + } + + return xReturn; + } + +#endif /* configQUEUE_DIRECT_TRANSFER */ +/*-----------------------------------------------------------*/ + static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely ) {