Continue work on the new timer implementation. Nearly complete.
diff --git a/Source/include/FreeRTOS.h b/Source/include/FreeRTOS.h
index a6e1035..b19fbae 100644
--- a/Source/include/FreeRTOS.h
+++ b/Source/include/FreeRTOS.h
@@ -181,6 +181,10 @@
#define INCLUDE_xTaskResumeFromISR 1
#endif
+#ifndef configASSERT
+ #define configASSERT( x )
+#endif
+
/* The timers module relies on xTaskGetSchedulerState(). */
#if configUSE_TIMERS == 1
diff --git a/Source/include/timers.h b/Source/include/timers.h
index 95431e4..7e30f1c 100644
--- a/Source/include/timers.h
+++ b/Source/include/timers.h
@@ -67,11 +67,10 @@
#endif
/* IDs for commands that can be sent/received on the timer queue. */
-#define trmCOMMAND_PROCESS_TIMER_OVERFLOW 0 /* For use by the kernel only! */
-#define tmrCOMMAND_START 1
-#define tmrCOMMAND_STOP 2
-#define tmrCOMMAND_CHANGE_PERIOD 3
-#define tmrCOMMAND_DELETE 4
+#define tmrCOMMAND_START 0
+#define tmrCOMMAND_STOP 1
+#define tmrCOMMAND_CHANGE_PERIOD 2
+#define tmrCOMMAND_DELETE 3
/*-----------------------------------------------------------
* MACROS AND DEFINITIONS
diff --git a/Source/queue.c b/Source/queue.c
index 8064130..c08d4c1 100644
--- a/Source/queue.c
+++ b/Source/queue.c
@@ -1469,20 +1469,27 @@
void vQueueWaitForMessageRestricted( xQueueHandle pxQueue, portTickType xTicksToWait )
{
- /* This function should not be called by application code hence the
- 'Restricted' in its name. It is not part of the public API. It is designed
- for use by kernel code, and has special calling requirements - it should be
- called from a critical section, and then a yield performed after it is
- called. Also, the call tree makes use of vListInsert() which should normally
- not be called from a critical section - so an assumption is made that the list
- being inserted into is empty and therefore the insertion will be fast. */
+ /* This function should not be called by application code hence the
+ 'Restricted' in its name. It is not part of the public API. It is
+ designed for use by kernel code, and has special calling requirements.
+ It can result in vListInsert() being called on a list that can only
+ possibly ever have one item in it, so the list will be fast, but even
+ so it should be called with the scheduler locked and not from a critical
+ section. */
- /* Only do anything if there are no message in the queue. */
+ /* Only do anything if there are no messages in the queue. This function
+ will not actually cause the task to block, just place it on a blocked
+ list. It will not block until the scheduler is unlocked - at which
+ time a yield will be performed. If an item is added to the queue while
+ the queue is locked, and the calling task blocks on the queue, then the
+ calling task will be immediately unblocked when the queue is unlocked. */
+ prvLockQueue( pxQueue );
if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0U )
{
/* There is nothing in the queue, block for the specified period. */
vTaskPlaceOnEventListRestricted( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait );
}
+ prvUnlockQueue( pxQueue );
}
#endif
diff --git a/Source/tasks.c b/Source/tasks.c
index 3e4fb52..70b629d 100644
--- a/Source/tasks.c
+++ b/Source/tasks.c
@@ -754,7 +754,7 @@
/* This function is not intended to be a public API function and definitely
is not for generic use as it assumes pxTask is not the running task and not
- suspended, does not remove the task from any event lists it might be
+ suspended, does not remove the task from any event lists it might be
blocked on, and does not take care of mutual exclusion. */
vListRemove( &( pxTCB->xGenericListItem ) );
prvAddTaskToReadyQueue( pxTCB );
@@ -1444,19 +1444,13 @@
/* Tick count has overflowed so we need to swap the delay lists.
If there are any items in pxDelayedTaskList here then there is
an error! */
+ configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) );
+
pxTemp = pxDelayedTaskList;
pxDelayedTaskList = pxOverflowDelayedTaskList;
pxOverflowDelayedTaskList = pxTemp;
xNumOfOverflows++;
-
- #if configUSE_TIMERS == 1
- {
- /* The timer service task needs to know to switch its lists
- too. */
- xTimerGenericCommand( NULL, trmCOMMAND_PROCESS_TIMER_OVERFLOW, 0, 0 );
- }
- #endif
-
+
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
{
/* The delayed list is empty. Set xNextTaskUnblockTime to the
@@ -1756,9 +1750,9 @@
{
portTickType xTimeToWake;
- /* This function should not be called by application code hence the
- 'Restricted' in its name. It is not part of the public API. It is
- designed for use by kernel code, and has special calling requirements -
+ /* This function should not be called by application code hence the
+ 'Restricted' in its name. It is not part of the public API. It is
+ designed for use by kernel code, and has special calling requirements -
it should be called from a critical section. */
@@ -1769,7 +1763,7 @@
vListInsertEnd( ( xList * ) pxEventList, ( xListItem * ) &( pxCurrentTCB->xEventListItem ) );
/* We must remove this task from the ready list before adding it to the
- blocked list as the same list item is used for both lists. This
+ blocked list as the same list item is used for both lists. This
function is called form a critical section. */
vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) );
diff --git a/Source/timers.c b/Source/timers.c
index 2a6b8fe..c9d592c 100644
--- a/Source/timers.c
+++ b/Source/timers.c
@@ -117,25 +117,45 @@
* Called by the timer service task to interpret and process a command it
* received on the timer queue.
*/
-static void prvProcessReceivedCommands( portTickType xAssumedTimeNow ) PRIVILEGED_FUNCTION;
+static void prvProcessReceivedCommands( void ) PRIVILEGED_FUNCTION;
/*
- * Insert the timer into either xActiveTimerList1, or xActiveTimerList2,
- * depending on if the expire time causes a timer counter overflow.
+ * Insert the timer into either xActiveTimerList1, or xActiveTimerList2,
+ * depending on if the expire time causes a timer counter overflow.
*/
-static void prvInsertTimerInActiveList( xTIMER *pxTimer, portTickType xNextExpiryTime, portTickType xAssumedTimeNow ) PRIVILEGED_FUNCTION;
+static void prvInsertTimerInActiveList( xTIMER *pxTimer, portTickType xNextExpiryTime, portTickType xTimeNow ) PRIVILEGED_FUNCTION;
/*
* An active timer has reached its expire time. Reload the timer if it is an
* auto reload timer, then call its callback.
*/
-static void prvProcessExpiredTimer( portTickType xNextExpireTime, portTickType xAssumedTimeNow ) PRIVILEGED_FUNCTION;
+static void prvProcessExpiredTimer( portTickType xNextExpireTime, portTickType xTimeNow ) PRIVILEGED_FUNCTION;
/*
* The tick count has overflowed. Switch the timer lists after ensuring the
* current timer list does not still reference some timers.
*/
-static void prvSwitchTimerLists( portTickType xAssumedTimeNow ) PRIVILEGED_FUNCTION;
+static void prvSwitchTimerLists( portTickType xTimeNow, portTickType xLastTime ) PRIVILEGED_FUNCTION;
+
+/*
+ * Obtain the current tick count, setting *pxTimerListsWereSwitched to pdTRUE
+ * if a tick count overflow occurred since prvSampleTimeNow() was last called.
+ */
+static portTickType prvSampleTimeNow( portBASE_TYPE *pxTimerListsWereSwitched ) PRIVILEGED_FUNCTION;
+
+/*
+ * If the timer list contains any active timers then return the expire time of
+ * the timer that will expire first and set *pxListWasEmpty to false. If the
+ * timer list does not contain any timers then return 0 and set *pxListWasEmpty
+ * to pdTRUE.
+ */
+static portTickType prvLookForExpiredTimer( portBASE_TYPE *pxListWasEmpty ) PRIVILEGED_FUNCTION;
+
+/*
+ * If a timer has expired, process it. Otherwise, block the timer service task
+ * until either a timer does expire or a command is received.
+ */
+static void prvProcessTimerOrBlockTask( portTickType xNextExpireTime, portBASE_TYPE xListWasEmpty ) PRIVILEGED_FUNCTION;
/*-----------------------------------------------------------*/
@@ -151,7 +171,7 @@
if( xTimerQueue != NULL )
{
- xReturn = xTaskCreate( prvTimerTask, ( const signed char * ) "Timer Service", configTIMER_TASK_STACK_DEPTH, NULL, configTIMER_TASK_PRIORITY, NULL );
+ xReturn = xTaskCreate( prvTimerTask, ( const signed char * ) "Tmr Svc", configTIMER_TASK_STACK_DEPTH, NULL, configTIMER_TASK_PRIORITY, NULL);
}
return xReturn;
@@ -183,7 +203,7 @@
}
/*-----------------------------------------------------------*/
-portBASE_TYPE xTimerGenericCommand( xTimerHandle xTimer, portBASE_TYPE xCommandID, portTickType xOptionalValue, portTickType xBlockTime )
+portBASE_TYPE xTimerGenericCommand( xTimerHandle xTimer, portBASE_TYPE xCommandID, portTickType xOptionalValue, portBASE_TYPE *pxHigherPriorityTaskWoken, portTickType xBlockTime )
{
portBASE_TYPE xReturn = pdFAIL;
xTIMER_MESSAGE xMessage;
@@ -197,13 +217,20 @@
xMessage.xMessageValue = xOptionalValue;
xMessage.pxTimer = ( xTIMER * ) xTimer;
- if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )
+ if( pxHigherPriorityTaskWoken == NULL )
{
- xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xBlockTime );
+ if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING )
+ {
+ xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xBlockTime );
+ }
+ else
+ {
+ xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY );
+ }
}
else
{
- xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY );
+ xReturn = xQueueSendToBackFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken );
}
}
@@ -211,128 +238,155 @@
}
/*-----------------------------------------------------------*/
-static void prvProcessExpiredTimer( portTickType xNextExpireTime, portTickType xAssumedTimeNow )
+static void prvProcessExpiredTimer( portTickType xNextExpireTime, portTickType xTimeNow )
{
xTIMER *pxTimer;
- if( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE )
+ /* Remove the timer from the list of active timers. A check has already
+ been performed to ensure the list is not empty. */
+ pxTimer = ( xTIMER * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList );
+ vListRemove( &( pxTimer->xTimerListItem ) );
+
+ /* 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. */
+ if( pxTimer->uxAutoReload == pdTRUE )
{
- /* Remove the timer from the list of active timers. */
- pxTimer = ( xTIMER * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList );
- vListRemove( &( pxTimer->xTimerListItem ) );
-
- /* 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. */
- if( pxTimer->uxAutoReload == pdTRUE )
- {
- /* This is the only time a timer is inserted into a list using
- a time relative to anything other than the current time. It
- will therefore be inserted into the correct list relative to
- the time this task thinks it is now, even if a command to
- switch lists due to a tick count overflow is already waiting in
- the timer queue. */
- prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xAssumedTimeNow );
- }
-
- /* Call the timer callback. */
- pxTimer->pxCallbackFunction( ( xTimerHandle ) pxTimer );
+ /* This is the only time a timer is inserted into a list using
+ a time relative to anything other than the current time. It
+ will therefore be inserted into the correct list relative to
+ the time this task thinks it is now, even if a command to
+ switch lists due to a tick count overflow is already waiting in
+ the timer queue. */
+ prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow );
}
+
+ /* Call the timer callback. */
+ pxTimer->pxCallbackFunction( ( xTimerHandle ) pxTimer );
}
/*-----------------------------------------------------------*/
static void prvTimerTask( void *pvParameters )
{
-portTickType xNextExpireTime, xTimeNow, xFrozenTimeNow;
+portTickType xNextExpireTime;
+portBASE_TYPE xListWasEmpty;
/* Just to avoid compiler warnings. */
( void ) pvParameters;
for( ;; )
{
- /* Take a snapshot of the time to use while assessing expiry and auto
- reload times. */
- xFrozenTimeNow = xTaskGetTickCount();
+ /* Query the timers list to see if it contains any timers, and if so,
+ obtain the time at which the next timer will expire. */
+ xNextExpireTime = prvLookForExpiredTimer( &xListWasEmpty );
- /* Timers are listed in expiry time order, with the head of the list
- referencing the task that will expire first. Obtain the time at which
- the timer with the nearest expiry time will expire. If there are no
- active timers then just set the next expire time to the maximum possible
- time to ensure this task does not run unnecessarily. */
- if( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE )
- {
- xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );
- }
- else
- {
- xNextExpireTime = portMAX_DELAY;
- }
-
- /* Has the timer expired? This expiry time is relative to the snapshot
- of the time taken to be used in this loop iteration - so it doesn't
- matter at this point if a tick count overflows here. */
- if( xNextExpireTime <= xFrozenTimeNow )
- {
- prvProcessExpiredTimer( xNextExpireTime, xFrozenTimeNow );
- }
- else
- {
- /* Block this task until the next timer expires, or a command is
- received. */
- vTaskSuspendAll();
- {
- /* Has the tick overflowed since a time snapshot was taken? */
- xTimeNow = xTaskGetTickCount();
- if( xTimeNow >= xFrozenTimeNow )
- {
- /* Has the expire not still not been met? The tick count
- may be greater now than when the time snapshot was taken. */
- if( xNextExpireTime <= xTimeNow )
- {
- prvProcessExpiredTimer( xNextExpireTime, xFrozenTimeNow );
- }
- else
- {
- /* The tick count has not overflowed since the time
- snapshot, and the next expire time has not been reached
- since the last snapshot was taken. This task should
- therefore block to wait for the next expire time. */
- vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ) );
- }
- }
- else
- {
- /* The tick count has overflowed since the time snapshot
- was taken, therefore, the task should not block but continue
- with another loop. The command queue should contain a
- command to switch lists. */
- }
- }
- if( xTaskResumeAll() == pdFALSE )
- {
- /* Yield to wait for either a command to arrive, or the block time
- to expire. If a command arrived between the critical section being
- exited and this yield then the yield will just return to the same
- task. */
- portYIELD_WITHIN_API();
- }
-
- /* Take a snapshot of the time now for use in this iteration of the
- task loop. */
- xFrozenTimeNow = xTaskGetTickCount();
-
- /* Empty the command queue, if it contains any commands. */
- prvProcessReceivedCommands( xFrozenTimeNow );
- }
+ /* If a timer has expired, process it. Otherwise, block this task
+ until either a timer does expire, or a command is received. */
+ prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty );
+
+ /* Empty the command queue. */
+ prvProcessReceivedCommands();
}
}
/*-----------------------------------------------------------*/
-static void prvInsertTimerInActiveList( xTIMER *pxTimer, portTickType xNextExpiryTime, portTickType xAssumedTimeNow )
+static void prvProcessTimerOrBlockTask( portTickType xNextExpireTime, portBASE_TYPE xListWasEmpty )
+{
+portTickType xTimeNow;
+portBASE_TYPE xTimerListsWereSwitched;
+
+ vTaskSuspendAll();
+ {
+ /* Obtain the time now to make an assessment as to whether the timer
+ has expired or not. If obtaining the time causes the lists to switch
+ then don't process this timer as any timers that remained in the list
+ when the lists were switched will have been processed within the
+ prvSampelTimeNow() function. */
+ xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );
+ if( xTimerListsWereSwitched == pdFALSE )
+ {
+ /* The tick count has not overflowed, has the timer expired? */
+ if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) )
+ {
+ prvProcessExpiredTimer( xNextExpireTime, xTimeNow );
+ }
+ else
+ {
+ /* The tick count has not overflowed, and the next expire
+ time has not been reached yet. This task should therefore
+ block to wait for the next expire time or a command to be
+ received - whichever comes first. The following line cannot
+ be reached unless xNextExpireTime > xTimeNow, except in the
+ case when the current timer list is empty. */
+ vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ) );
+ }
+ }
+ }
+ if( xTaskResumeAll() == pdFALSE )
+ {
+ /* Yield to wait for either a command to arrive, or the block time
+ to expire. If a command arrived between the critical section being
+ exited and this yield then the yield will not cause the task
+ to block. */
+ portYIELD_WITHIN_API();
+ }
+}
+/*-----------------------------------------------------------*/
+
+static portTickType prvLookForExpiredTimer( portBASE_TYPE *pxListWasEmpty )
+{
+portTickType xNextExpireTime;
+
+ /* Timers are listed in expiry time order, with the head of the list
+ referencing the task that will expire first. Obtain the time at which
+ the timer with the nearest expiry time will expire. If there are no
+ active timers then just set the next expire time to 0. That will cause
+ this task to unblock when the tick count overflows, at which point the
+ timer lists will be switched and the next expiry time can be
+ re-assessed. */
+ *pxListWasEmpty = listLIST_IS_EMPTY( pxCurrentTimerList );
+ if( *pxListWasEmpty == pdFALSE )
+ {
+ xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );
+ }
+ else
+ {
+ /* Ensure the task unblocks when the tick count rolls over. */
+ xNextExpireTime = ( portTickType ) 0U;
+ }
+
+ return xNextExpireTime;
+}
+/*-----------------------------------------------------------*/
+
+static portTickType prvSampleTimeNow( portBASE_TYPE *pxTimerListsWereSwitched )
+{
+portTickType xTimeNow;
+static portTickType xLastTime = ( portTickType ) 0U;
+
+ xTimeNow = xTaskGetTickCount();
+
+ if( xTimeNow < xLastTime )
+ {
+ prvSwitchTimerLists( xTimeNow, xLastTime );
+ *pxTimerListsWereSwitched = pdTRUE;
+ }
+ else
+ {
+ *pxTimerListsWereSwitched = pdFALSE;
+ }
+
+ xLastTime = xTimeNow;
+
+ return xTimeNow;
+}
+/*-----------------------------------------------------------*/
+
+static void prvInsertTimerInActiveList( xTIMER *pxTimer, portTickType xNextExpiryTime, portTickType xTimeNow )
{
listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime );
listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer );
- if( xNextExpiryTime < xAssumedTimeNow )
+ if( xNextExpiryTime < xTimeNow )
{
vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) );
}
@@ -343,11 +397,16 @@
}
/*-----------------------------------------------------------*/
-static void prvProcessReceivedCommands( portTickType xAssumedTimeNow )
+static void prvProcessReceivedCommands( void )
{
xTIMER_MESSAGE xMessage;
xTIMER *pxTimer;
-portBASE_TYPE xSwitchListsOnExit = pdFALSE;
+portBASE_TYPE xTimerListsWereSwitched;
+portTickType xTimeNow;
+
+ /* In this case the xTimerListsWereSwitched parameter is not used, but it
+ must be present in the function call. */
+ xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched );
while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL )
{
@@ -369,7 +428,7 @@
{
case tmrCOMMAND_START :
/* Start or restart a timer. */
- prvInsertTimerInActiveList( pxTimer, xAssumedTimeNow + pxTimer->xTimerPeriodInTicks, xAssumedTimeNow );
+ prvInsertTimerInActiveList( pxTimer, xTimeNow + pxTimer->xTimerPeriodInTicks, xTimeNow );
break;
case tmrCOMMAND_STOP :
@@ -379,7 +438,7 @@
case tmrCOMMAND_CHANGE_PERIOD :
pxTimer->xTimerPeriodInTicks = xMessage.xMessageValue;
- prvInsertTimerInActiveList( pxTimer, ( xAssumedTimeNow + pxTimer->xTimerPeriodInTicks ), xAssumedTimeNow );
+ prvInsertTimerInActiveList( pxTimer, ( xTimeNow + pxTimer->xTimerPeriodInTicks ), xTimeNow );
break;
case tmrCOMMAND_DELETE :
@@ -387,39 +446,32 @@
just free up the memory. */
vPortFree( pxTimer );
break;
-
- case trmCOMMAND_PROCESS_TIMER_OVERFLOW :
- /* Hold this pending until all the other messages have been
- processed. */
- xSwitchListsOnExit = pdTRUE;
- break;
default :
/* Don't expect to get here. */
break;
}
}
-
- if( xSwitchListsOnExit == pdTRUE )
- {
- prvSwitchTimerLists( xAssumedTimeNow );
- }
}
/*-----------------------------------------------------------*/
-static void prvSwitchTimerLists( portTickType xAssumedTimeNow )
+static void prvSwitchTimerLists( portTickType xTimeNow, portTickType xLastTime )
{
portTickType xNextExpireTime;
xList *pxTemp;
- /* The tick count has overflowed. The timer lists must be switched.
- If there are any timers still referenced from the current timer list
- then they must have expired and should be processed before the lists
+ /* Remove compiler warnings if configASSERT() is not defined. */
+ ( void ) xLastTime;
+
+ /* The tick count has overflowed. The timer lists must be switched.
+ If there are any timers still referenced from the current timer list
+ then they must have expired and should be processed before the lists
are switched. */
while( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE )
{
xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList );
- prvProcessExpiredTimer( xNextExpireTime, xAssumedTimeNow );
+ configASSERT( ( xNextExpireTime >= xLastTime ) );
+ prvProcessExpiredTimer( xNextExpireTime, xTimeNow );
}
pxTemp = pxCurrentTimerList;