Remove support for tmrCOMMAND_START_DONT_TRACE (#305)

* Remove support for tmrCOMMAND_START_DONT_TRACE

Prior to this commit, the timer task used tmrCOMMAND_START_DONT_TRACE
to reload a "backlogged" auto-reload timer -- one for which a reload
operation would have started a period that had already elapsed.  If the
command queue contained a stop or delete command when
tmrCOMMAND_START_DONT_TRACE was put into the queue for a reload, the
timer unexpectedly restarted when the timer task processed
tmrCOMMAND_START_DONT_TRACE.  This commit implements a new method of
reloading auto-reload timers and eliminates support for
tmrCOMMAND_START_DONT_TRACE.  No other code sends this private command.

However, the symbol tmrCOMMAND_START_DONT_TRACE remains defined, with
its original command value, so as not to impact trace applications.

Also fix one-shot timers that were not reliably being marked as not
active:
- when they expired before the start command could be processed
- when the expiration was processed during the timer list switch

Also improve consistency:
- Always reload auto-reload timers *before* calling the callback.
- Always call traceTIMER_EXPIRED() just prior to the callback.

* fix indent

* Revert unnecessary change to prvTimerTask()

Change was intended to faithfully work through a backlog that spanned a
list switch, and before processing a stop or delete command.  But, (1)
it isn't important to do that, and (2) the code didn't accomplish the
intention when *two* auto-reload timers were backlogged across a list
switch.  Best to simply leave this part of the code as it was before.

* fix style

Co-authored-by: Joseph Julicher <jjulicher@mac.com>
This commit is contained in:
Jeff Tenney 2021-04-13 11:05:46 -07:00 committed by GitHub
parent a22b438e60
commit 05ded5bd8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

113
timers.c
View File

@ -55,7 +55,8 @@
#if ( configUSE_TIMERS == 1 ) #if ( configUSE_TIMERS == 1 )
/* Misc definitions. */ /* Misc definitions. */
#define tmrNO_DELAY ( TickType_t ) 0U #define tmrNO_DELAY ( ( TickType_t ) 0U )
#define tmrMAX_TIME_BEFORE_OVERFLOW ( ( TickType_t ) -1 )
/* The name assigned to the timer service task. This can be overridden by /* The name assigned to the timer service task. This can be overridden by
* defining trmTIMER_SERVICE_TASK_NAME in FreeRTOSConfig.h. */ * defining trmTIMER_SERVICE_TASK_NAME in FreeRTOSConfig.h. */
@ -172,6 +173,15 @@
const TickType_t xTimeNow, const TickType_t xTimeNow,
const TickType_t xCommandTime ) PRIVILEGED_FUNCTION; const TickType_t xCommandTime ) PRIVILEGED_FUNCTION;
/*
* Reload the specified auto-reload timer. If the reloading is backlogged,
* clear the backlog, calling the callback for each additional reload. When
* this function returns, the next expiry time is after xTimeNow.
*/
static void prvReloadTimer( Timer_t * const pxTimer,
TickType_t xExpiredTime,
const TickType_t xTimeNow ) PRIVILEGED_FUNCTION;
/* /*
* An active timer has reached its expire time. Reload the timer if it is an * An active timer has reached its expire time. Reload the timer if it is an
* auto-reload timer, then call its callback. * auto-reload timer, then call its callback.
@ -502,45 +512,47 @@
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
static void prvReloadTimer( Timer_t * const pxTimer,
TickType_t xExpiredTime,
const TickType_t xTimeNow )
{
/* Insert the timer into the appropriate list for the next expiry time.
* If the next expiry time has already passed, advance the expiry time,
* call the callback function, and try again. */
while ( prvInsertTimerInActiveList( pxTimer, ( xExpiredTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xExpiredTime ) != pdFALSE )
{
/* Advance the expiry time. */
xExpiredTime += pxTimer->xTimerPeriodInTicks;
/* Call the timer callback. */
traceTIMER_EXPIRED( pxTimer );
pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );
}
}
/*-----------------------------------------------------------*/
static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, static void prvProcessExpiredTimer( const TickType_t xNextExpireTime,
const TickType_t xTimeNow ) const TickType_t xTimeNow )
{ {
BaseType_t xResult;
Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */
/* Remove the timer from the list of active timers. A check has already /* Remove the timer from the list of active timers. A check has already
* been performed to ensure the list is not empty. */ * been performed to ensure the list is not empty. */
( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); ( void ) uxListRemove( &( pxTimer->xTimerListItem ) );
traceTIMER_EXPIRED( pxTimer );
/* If the timer is an auto-reload timer then calculate the next /* If the timer is an auto-reload timer then calculate the next
* expiry time and re-insert the timer in the list of active timers. */ * expiry time and re-insert the timer in the list of active timers. */
if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 )
{ {
/* The timer is inserted into a list using a time relative to anything prvReloadTimer( pxTimer, xNextExpireTime, xTimeNow );
* other than the current time. It will therefore be inserted into the
* correct list relative to the time this task thinks it is now. */
if( prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xNextExpireTime ) != pdFALSE )
{
/* The timer expired before it was added to the active timer
* list. Reload it now. */
xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY );
configASSERT( xResult );
( void ) xResult;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
} }
else else
{ {
pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE;
mtCOVERAGE_TEST_MARKER();
} }
/* Call the timer callback. */ /* Call the timer callback. */
traceTIMER_EXPIRED( pxTimer );
pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );
} }
/*-----------------------------------------------------------*/ /*-----------------------------------------------------------*/
@ -741,7 +753,7 @@
{ {
DaemonTaskMessage_t xMessage; DaemonTaskMessage_t xMessage;
Timer_t * pxTimer; Timer_t * pxTimer;
BaseType_t xTimerListsWereSwitched, xResult; BaseType_t xTimerListsWereSwitched;
TickType_t xTimeNow; TickType_t xTimeNow;
while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL ) /*lint !e603 xMessage does not have to be initialised as it is passed out, not in, and it is not used unless xQueueReceive() returns pdTRUE. */ while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL ) /*lint !e603 xMessage does not have to be initialised as it is passed out, not in, and it is not used unless xQueueReceive() returns pdTRUE. */
@ -802,7 +814,6 @@
case tmrCOMMAND_START_FROM_ISR: case tmrCOMMAND_START_FROM_ISR:
case tmrCOMMAND_RESET: case tmrCOMMAND_RESET:
case tmrCOMMAND_RESET_FROM_ISR: case tmrCOMMAND_RESET_FROM_ISR:
case tmrCOMMAND_START_DONT_TRACE:
/* Start or restart a timer. */ /* Start or restart a timer. */
pxTimer->ucStatus |= tmrSTATUS_IS_ACTIVE; pxTimer->ucStatus |= tmrSTATUS_IS_ACTIVE;
@ -810,19 +821,18 @@
{ {
/* The timer expired before it was added to the active /* The timer expired before it was added to the active
* timer list. Process it now. */ * timer list. Process it now. */
pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );
traceTIMER_EXPIRED( pxTimer );
if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 )
{ {
xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, NULL, tmrNO_DELAY ); prvReloadTimer( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow );
configASSERT( xResult );
( void ) xResult;
} }
else else
{ {
mtCOVERAGE_TEST_MARKER(); pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE;
} }
/* Call the timer callback. */
traceTIMER_EXPIRED( pxTimer );
pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );
} }
else else
{ {
@ -889,10 +899,8 @@
static void prvSwitchTimerLists( void ) static void prvSwitchTimerLists( void )
{ {
TickType_t xNextExpireTime, xReloadTime; TickType_t xNextExpireTime;
List_t * pxTemp; List_t * pxTemp;
Timer_t * pxTimer;
BaseType_t xResult;
/* The tick count has overflowed. The timer lists must be switched. /* The tick count has overflowed. The timer lists must be switched.
* If there are any timers still referenced from the current timer list * If there are any timers still referenced from the current timer list
@ -902,43 +910,10 @@
{ {
xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );
/* Remove the timer from the list. */ /* Process the expired timer. For auto-reload timers, be careful to
pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ * process only expirations that occur on the current list. Further
( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); * expirations must wait until after the lists are switched. */
traceTIMER_EXPIRED( pxTimer ); prvProcessExpiredTimer( xNextExpireTime, tmrMAX_TIME_BEFORE_OVERFLOW );
/* Execute its callback, then send a command to restart the timer if
* it is an auto-reload timer. It cannot be restarted here as the lists
* have not yet been switched. */
pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer );
if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 )
{
/* Calculate the reload value, and if the reload value results in
* the timer going into the same timer list then it has already expired
* and the timer should be re-inserted into the current list so it is
* processed again within this loop. Otherwise a command should be sent
* to restart the timer to ensure it is only inserted into a list after
* the lists have been swapped. */
xReloadTime = ( xNextExpireTime + pxTimer->xTimerPeriodInTicks );
if( xReloadTime > xNextExpireTime )
{
listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xReloadTime );
listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer );
vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) );
}
else
{
xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY );
configASSERT( xResult );
( void ) xResult;
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
} }
pxTemp = pxCurrentTimerList; pxTemp = pxCurrentTimerList;