feat(freertos-smp): Add support for queue direct transfer buffer in task TCB

This commit adds support for queue direct transfer buffer being owned by
the task's TCB. This inherently solves some design issues with the
buffer being owned by the queue's object. On the flip side, this adds
memory cost to the task's TCB.
This commit is contained in:
Sudeep Mohanty 2025-12-03 15:43:05 +05:30
parent 53a44909aa
commit e850728909
4 changed files with 365 additions and 192 deletions

View file

@ -3291,6 +3291,11 @@ typedef struct xSTATIC_TCB
#if ( configUSE_POSIX_ERRNO == 1 ) #if ( configUSE_POSIX_ERRNO == 1 )
int iDummy22; int iDummy22;
#endif #endif
#if ( configQUEUE_DIRECT_TRANSFER == 1 )
void * pvDummyDirectTransferBuffer;
BaseType_t xDummyDirectTransferPosition;
#endif
} StaticTask_t; } StaticTask_t;
/* /*
@ -3337,11 +3342,6 @@ typedef struct xSTATIC_QUEUE
#if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) )
portSPINLOCK_TYPE xDummySpinlock[ 2 ]; portSPINLOCK_TYPE xDummySpinlock[ 2 ];
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */ #endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */
#if ( configQUEUE_DIRECT_TRANSFER == 1 )
void * pvDummyDirectTransferBuffer;
BaseType_t xDummyDirectTransferPosition;
#endif
} StaticQueue_t; } StaticQueue_t;
typedef StaticQueue_t StaticSemaphore_t; typedef StaticQueue_t StaticSemaphore_t;

View file

@ -3758,6 +3758,68 @@ BaseType_t xTaskRemoveFromEventListFromISR( const List_t * const pxEventList ) P
void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem,
const TickType_t xItemValue ) PRIVILEGED_FUNCTION; 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 * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY
* INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS

325
queue.c
View file

@ -143,14 +143,6 @@ typedef struct QueueDefinition /* The old naming convention is used to prevent b
portSPINLOCK_TYPE xTaskSpinlock; portSPINLOCK_TYPE xTaskSpinlock;
portSPINLOCK_TYPE xISRSpinlock; portSPINLOCK_TYPE xISRSpinlock;
#endif /* #if ( ( portUSING_GRANULAR_LOCKS == 1 ) && ( configNUMBER_OF_CORES > 1 ) ) */ #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; } xQUEUE;
/* The old xQUEUE name is maintained above then typedefed to the new Queue_t /* 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->cRxLock = queueUNLOCKED;
pxQueue->cTxLock = queueUNLOCKED; pxQueue->cTxLock = queueUNLOCKED;
#if ( configQUEUE_DIRECT_TRANSFER == 1 )
{
pxQueue->pvDirectTransferBuffer = NULL;
pxQueue->xDirectTransferPosition = queueDIRECT_TRANSFER_POSITION_INIT;
}
#endif
if( xNewQueue == pdFALSE ) if( xNewQueue == pdFALSE )
{ {
/* If there are tasks blocked waiting to read from the queue, then /* 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; Queue_t * const pxQueue = xQueue;
#if ( configQUEUE_DIRECT_TRANSFER == 1 ) #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; BaseType_t xDirectTransferArmed = pdFALSE;
#endif #endif
@ -1122,29 +1110,33 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
{ {
#if ( configQUEUE_DIRECT_TRANSFER == 1 ) #if ( configQUEUE_DIRECT_TRANSFER == 1 )
{ {
/* Check if we were unblocked due to a direct transfer completion. */ /* Check if we were unblocked due to a direct transfer completion.
if( ( pxQueue->pvDirectTransferBuffer == NULL ) && ( xDirectTransferArmed == pdTRUE ) ) * 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 void * pvCurrentBuffer = pvTaskGetDirectTransferBuffer( NULL );
* direct transfer and our data was copied to the queue. */
xDirectTransferArmed = pdFALSE;
queueEXIT_CRITICAL( pxQueue );
traceQUEUE_SEND( pxQueue ); if( pvCurrentBuffer == NULL )
traceRETURN_xQueueGenericSend( pdPASS ); {
return pdPASS; /* Our buffer was cleared - direct transfer completed successfully. */
} xDirectTransferArmed = pdFALSE;
else if( pxQueue->pvDirectTransferBuffer == pvItemToQueue ) queueEXIT_CRITICAL( pxQueue );
{
/* We armed direct transfer but receiver didn't execute it - clear and try normal path. */ traceQUEUE_SEND( pxQueue );
pxQueue->pvDirectTransferBuffer = NULL; traceRETURN_xQueueGenericSend( pdPASS );
xDirectTransferArmed = pdFALSE; return pdPASS;
} }
else else if( pvCurrentBuffer == pvItemToQueue )
{ {
/* Buffer doesn't match our data (someone else armed it or it was cleared /* We armed direct transfer but receiver didn't use it - clear and try normal path. */
* for another task) - just clear our flag and continue. */ vTaskClearDirectTransferBuffer( NULL );
xDirectTransferArmed = pdFALSE; xDirectTransferArmed = pdFALSE;
}
else
{
/* Buffer doesn't match - shouldn't happen, but clear our flag. */
xDirectTransferArmed = pdFALSE;
}
} }
} }
#endif /* if ( configQUEUE_DIRECT_TRANSFER == 1 ) */ #endif /* if ( configQUEUE_DIRECT_TRANSFER == 1 ) */
@ -1221,34 +1213,37 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
{ {
#if ( configQUEUE_DIRECT_TRANSFER == 1 ) #if ( configQUEUE_DIRECT_TRANSFER == 1 )
{ {
/* Check if direct transfer is armed (receiver is waiting on empty queue). */ /* Check if there's a receiver waiting with armed direct transfer. */
if( ( pxQueue->pvDirectTransferBuffer != NULL ) && if( ( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) &&
( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) &&
( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) ) ( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) )
{ {
/* Direct copy to waiting receiver's buffer. */ /* Find highest priority receiver with armed direct transfer. */
( void ) memcpy( pxQueue->pvDirectTransferBuffer, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); TaskHandle_t xReceivingTask = xTaskGetHighestPriorityTaskWithDirectTransferArmed( &( pxQueue->xTasksWaitingToReceive ) );
/* Clear buffer pointer - receiver will detect this. */ if( xReceivingTask != NULL )
pxQueue->pvDirectTransferBuffer = NULL;
/* Unblock the waiting receiver. */
if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{ {
queueYIELD_IF_USING_PREEMPTION(); void * pvReceiverBuffer = pvTaskGetDirectTransferBuffer( xReceivingTask );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
queueEXIT_CRITICAL( pxQueue ); /* Direct copy to waiting receiver's buffer. */
traceRETURN_xQueueGenericSend( pdPASS ); ( void ) memcpy( pvReceiverBuffer, pvItemToQueue, ( size_t ) pxQueue->uxItemSize );
return pdPASS;
} /* Clear receiver's buffer pointer. */
else vTaskClearDirectTransferBuffer( xReceivingTask );
{
mtCOVERAGE_TEST_MARKER(); /* 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 */ #endif /* configQUEUE_DIRECT_TRANSFER */
@ -1259,13 +1254,6 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
* queue then unblock it now. */ * queue then unblock it now. */
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) 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 ) if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{ {
/* The unblocked task has a priority higher than /* The unblocked task has a priority higher than
@ -1345,21 +1333,9 @@ BaseType_t xQueueGenericSend( QueueHandle_t xQueue,
#if ( configQUEUE_DIRECT_TRANSFER == 1 ) #if ( configQUEUE_DIRECT_TRANSFER == 1 )
{ {
/* Arm direct transfer only if we'll be the highest priority waiter. /* Arm direct transfer in current task's TCB. Any receiver that unblocks us can check our TCB for the buffer. */
* This happens when: (1) list is empty, OR (2) our priority is higher vTaskSetDirectTransferBuffer( ( void * ) pvItemToQueue, xCopyPosition, NULL );
* than the current highest priority waiting task. */ xDirectTransferArmed = pdTRUE;
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;
}
} }
#endif /* if ( configQUEUE_DIRECT_TRANSFER == 1 ) */ #endif /* if ( configQUEUE_DIRECT_TRANSFER == 1 ) */
@ -1442,35 +1418,42 @@ BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue,
#if ( configQUEUE_DIRECT_TRANSFER == 1 ) #if ( configQUEUE_DIRECT_TRANSFER == 1 )
{ {
/* Check if direct transfer is armed (receiver is waiting on empty queue). */ /* Check if there's a receiver waiting with armed direct transfer. */
if( ( pxQueue->pvDirectTransferBuffer != NULL ) && if( ( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) &&
( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) &&
( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) && ( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) &&
( cTxLock == queueUNLOCKED ) ) ( cTxLock == queueUNLOCKED ) )
{ {
/* Direct copy to waiting receiver's buffer. */ /* Find highest priority receiver with armed direct transfer. */
( void ) memcpy( pxQueue->pvDirectTransferBuffer, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); TaskHandle_t xReceivingTask = xTaskGetHighestPriorityTaskWithDirectTransferArmed( &( pxQueue->xTasksWaitingToReceive ) );
/* Clear buffer pointer - receiver will detect this. */ if( xReceivingTask != NULL )
pxQueue->pvDirectTransferBuffer = NULL;
/* Unblock the waiting receiver. */
if( xTaskRemoveFromEventListFromISR( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{ {
if( pxHigherPriorityTaskWoken != NULL ) void * pvReceiverBuffer = pvTaskGetDirectTransferBuffer( xReceivingTask );
{
*pxHigherPriorityTaskWoken = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
xReturn = pdPASS; /* Direct copy to waiting receiver's buffer. */
queueEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus, pxQueue ); ( void ) memcpy( pvReceiverBuffer, pvItemToQueue, ( size_t ) pxQueue->uxItemSize );
traceRETURN_xQueueGenericSendFromISR( xReturn );
return xReturn; /* 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 */ #endif /* configQUEUE_DIRECT_TRANSFER */
@ -1543,13 +1526,6 @@ BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue,
{ {
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) 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 ) if( xTaskRemoveFromEventListFromISR( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{ {
/* The task waiting has a higher priority so record that a /* 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; Queue_t * const pxQueue = xQueue;
#if ( configQUEUE_DIRECT_TRANSFER == 1 ) #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; BaseType_t xDirectTransferArmed = pdFALSE;
#endif #endif
@ -1806,16 +1785,15 @@ BaseType_t xQueueReceive( QueueHandle_t xQueue,
{ {
#if ( configQUEUE_DIRECT_TRANSFER == 1 ) #if ( configQUEUE_DIRECT_TRANSFER == 1 )
{ {
/* Check if we were unblocked due to direct transfer completion. */ /* Check if we were unblocked due to a direct transfer completion.
if( ( pxQueue->pvDirectTransferBuffer == NULL ) && ( xDirectTransferArmed == pdTRUE ) ) * 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 void * pvCurrentBuffer = pvTaskGetDirectTransferBuffer( NULL );
* 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 if( pvCurrentBuffer == NULL )
* by higher priority task, and data was queued normally). */
if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 )
{ {
/* Queue empty - direct transfer to us succeeded! */ /* Our buffer was cleared - direct transfer completed successfully. */
xDirectTransferArmed = pdFALSE; xDirectTransferArmed = pdFALSE;
queueEXIT_CRITICAL( pxQueue ); queueEXIT_CRITICAL( pxQueue );
@ -1823,24 +1801,17 @@ BaseType_t xQueueReceive( QueueHandle_t xQueue,
traceRETURN_xQueueReceive( pdPASS ); traceRETURN_xQueueReceive( pdPASS );
return pdPASS; return pdPASS;
} }
else else if( pvCurrentBuffer == pvBuffer )
{ {
/* Queue has data - this wasn't a direct transfer to us. /* We armed direct transfer but sender didn't use it - clear and try normal path. */
* Continue to normal receive path. */ vTaskClearDirectTransferBuffer( NULL );
xDirectTransferArmed = pdFALSE;
}
else
{
/* Buffer doesn't match - shouldn't happen, but clear our flag. */
xDirectTransferArmed = pdFALSE; 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 ) */ #endif /* if ( configQUEUE_DIRECT_TRANSFER == 1 ) */
@ -1863,20 +1834,26 @@ BaseType_t xQueueReceive( QueueHandle_t xQueue,
{ {
#if ( configQUEUE_DIRECT_TRANSFER == 1 ) #if ( configQUEUE_DIRECT_TRANSFER == 1 )
{ {
/* Check if a direct transfer is armed (sender is waiting on a full queue). */ /* Check if there's a sender waiting with armed direct transfer.
if( pxQueue->pvDirectTransferBuffer != NULL ) * 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 */ /* Verify that position was set to a valid value */
configASSERT( pxQueue->xDirectTransferPosition != queueDIRECT_TRANSFER_POSITION_INIT ); configASSERT( xPosition != queueDIRECT_TRANSFER_POSITION_INIT );
configASSERT( ( pxQueue->xDirectTransferPosition == queueSEND_TO_BACK ) || configASSERT( ( xPosition == queueSEND_TO_BACK ) ||
( pxQueue->xDirectTransferPosition == queueSEND_TO_FRONT ) || ( xPosition == queueSEND_TO_FRONT ) ||
( pxQueue->xDirectTransferPosition == queueOVERWRITE ) ); ( xPosition == queueOVERWRITE ) );
/* Direct copy from waiting sender's buffer to the queue. */ /* 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. */ /* Clear sender's buffer pointer. */
pxQueue->pvDirectTransferBuffer = NULL; vTaskClearDirectTransferBuffer( xSendingTask );
} }
} }
#endif /* configQUEUE_DIRECT_TRANSFER */ #endif /* configQUEUE_DIRECT_TRANSFER */
@ -1947,19 +1924,9 @@ BaseType_t xQueueReceive( QueueHandle_t xQueue,
#if ( configQUEUE_DIRECT_TRANSFER == 1 ) #if ( configQUEUE_DIRECT_TRANSFER == 1 )
{ {
/* Arm direct transfer only if we'll be the highest priority waiter. /* Arm direct transfer in current task's TCB. Any sender that unblocks us can check our TCB for the buffer. */
* This happens when: (1) list is empty, OR (2) our priority is higher vTaskSetDirectTransferBuffer( pvBuffer, queueDIRECT_TRANSFER_POSITION_INIT, NULL );
* than the current highest waiting task. */ xDirectTransferArmed = pdTRUE;
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;
}
} }
#endif /* if ( configQUEUE_DIRECT_TRANSFER == 1 ) */ #endif /* if ( configQUEUE_DIRECT_TRANSFER == 1 ) */
@ -2403,21 +2370,26 @@ BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue,
{ {
#if ( configQUEUE_DIRECT_TRANSFER == 1 ) #if ( configQUEUE_DIRECT_TRANSFER == 1 )
{ {
/* Check if a direct transfer is armed (sender is waiting on full queue). /* Check if there's a sender waiting with armed direct transfer.
* When queue is not empty, pvDirectTransferBuffer is used to point to sender's data. */ * Find highest priority sender with armed direct transfer. */
if( pxQueue->pvDirectTransferBuffer != NULL ) 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. */ /* Verify that position was set to a valid value. */
configASSERT( pxQueue->xDirectTransferPosition != queueDIRECT_TRANSFER_POSITION_INIT ); configASSERT( xPosition != queueDIRECT_TRANSFER_POSITION_INIT );
configASSERT( ( pxQueue->xDirectTransferPosition == queueSEND_TO_BACK ) || configASSERT( ( xPosition == queueSEND_TO_BACK ) ||
( pxQueue->xDirectTransferPosition == queueSEND_TO_FRONT ) || ( xPosition == queueSEND_TO_FRONT ) ||
( pxQueue->xDirectTransferPosition == queueOVERWRITE ) ); ( xPosition == queueOVERWRITE ) );
/* Direct copy from waiting sender's buffer to the queue. */ /* 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. */ /* Clear sender's buffer pointer. */
pxQueue->pvDirectTransferBuffer = NULL; vTaskClearDirectTransferBuffer( xSendingTask );
} }
} }
#endif /* configQUEUE_DIRECT_TRANSFER */ #endif /* configQUEUE_DIRECT_TRANSFER */
@ -2596,13 +2568,6 @@ void vQueueDelete( QueueHandle_t xQueue )
configASSERT( pxQueue ); configASSERT( pxQueue );
traceQUEUE_DELETE( 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 ) #if ( configQUEUE_REGISTRY_SIZE > 0 )
{ {
vQueueUnregisterQueue( pxQueue ); vQueueUnregisterQueue( pxQueue );
@ -2879,13 +2844,6 @@ static void prvUnlockQueue( Queue_t * const pxQueue )
* suspended. */ * suspended. */
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) 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 ) if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{ {
/* The task waiting has a higher priority so record that a /* 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. */ * the pending ready list as the scheduler is still suspended. */
if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) 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 ) if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE )
{ {
/* The task waiting has a higher priority so record that /* The task waiting has a higher priority so record that

160
tasks.c
View file

@ -516,6 +516,14 @@ typedef struct tskTaskControlBlock /* The old naming convention is used to
#if ( configUSE_POSIX_ERRNO == 1 ) #if ( configUSE_POSIX_ERRNO == 1 )
int iTaskErrno; int iTaskErrno;
#endif #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; } tskTCB;
/* The old tskTCB name is maintained above then typedefed to the new TCB_t name /* 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, static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait,
const BaseType_t xCanBlockIndefinitely ) PRIVILEGED_FUNCTION; 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 * 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, * 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 ) ) */ #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, static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait,
const BaseType_t xCanBlockIndefinitely ) const BaseType_t xCanBlockIndefinitely )
{ {