#define genqMUTEX_MEDIUM_PRIORITY ( tskIDLE_PRIORITY + 2 )\r
#define genqMUTEX_HIGH_PRIORITY ( tskIDLE_PRIORITY + 3 )\r
\r
+#define genqINTERRUPT_MUTEX_GIVE_PERIOD_MS ( 100 )\r
/*-----------------------------------------------------------*/\r
\r
/*\r
static void prvMediumPriorityMutexTask( void *pvParameters );\r
static void prvHighPriorityMutexTask( void *pvParameters );\r
\r
+/*\r
+ * Exercises the priority inheritance when a task takes two mutexes, returning\r
+ * them in a different order to which they were taken.\r
+ */\r
+static void prvTakeTwoMutexesReturnInDifferentOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex );\r
+\r
+/*\r
+ * Exercises the priority inheritance when a task takes two mutexes, returning\r
+ * them in the same order in which they were taken.\r
+ */\r
+static void prvTakeTwoMutexesReturnInSameOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex );\r
+\r
+/*\r
+ * Task that receives an a mutex that is given from an interrupt - although\r
+ * generally mutexes should not be used given in interrupts (and definitely\r
+ * never taken in an interrupt) there are some circumstances when it may be\r
+ * desirable. NOTE: This function is not declared static to prevent compiler\r
+ * warnings being generated in demos where the function is declared but not\r
+ * used.\r
+ */\r
+void vInterruptMutexTask( void *pvParameters );\r
+\r
/*-----------------------------------------------------------*/\r
\r
/* Flag that will be latched to pdTRUE should any unexpected behaviour be\r
priority mutex test tasks. */\r
static TaskHandle_t xHighPriorityMutexTask, xMediumPriorityMutexTask;\r
\r
+/* A mutex which is given from an interrupt - although generally mutexes should\r
+not be used given in interrupts (and definitely never taken in an interrupt)\r
+there are some circumstances when it may be desirable. */\r
+static SemaphoreHandle_t xISRMutex = NULL;\r
+\r
/*-----------------------------------------------------------*/\r
\r
void vStartGenericQueueTasks( UBaseType_t uxPriority )\r
QueueHandle_t xQueue;\r
SemaphoreHandle_t xMutex;\r
\r
+ xISRMutex = xSemaphoreCreateMutex();\r
+ configASSERT( xISRMutex );\r
+\r
/* Create the queue that we are going to use for the\r
prvSendFrontAndBackTest demo. */\r
xQueue = xQueueCreate( genqQUEUE_LENGTH, sizeof( uint32_t ) );\r
xTaskCreate( prvLowPriorityMutexTask, "MuLow", configMINIMAL_STACK_SIZE, ( void * ) xMutex, genqMUTEX_LOW_PRIORITY, NULL );\r
xTaskCreate( prvMediumPriorityMutexTask, "MuMed", configMINIMAL_STACK_SIZE, NULL, genqMUTEX_MEDIUM_PRIORITY, &xMediumPriorityMutexTask );\r
xTaskCreate( prvHighPriorityMutexTask, "MuHigh", configMINIMAL_STACK_SIZE, ( void * ) xMutex, genqMUTEX_HIGH_PRIORITY, &xHighPriorityMutexTask );\r
+\r
+ /* Only when the windows simulator is being used - create the task that\r
+ receives a mutex from an interrupt. */\r
+ #ifdef _WINDOWS_\r
+ {\r
+ xTaskCreate( vInterruptMutexTask, "IntMu", configMINIMAL_STACK_SIZE, NULL, genqMUTEX_MEDIUM_PRIORITY, NULL );\r
+ }\r
+ #endif /* __WINDOWS__ */\r
}\r
/*-----------------------------------------------------------*/\r
\r
}\r
/*-----------------------------------------------------------*/\r
\r
-static void prvLowPriorityMutexTask( void *pvParameters )\r
+static void prvTakeTwoMutexesReturnInDifferentOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex )\r
{\r
-SemaphoreHandle_t xMutex = ( SemaphoreHandle_t ) pvParameters, xLocalMutex;\r
+ /* Take the mutex. It should be available now. */\r
+ if( xSemaphoreTake( xMutex, genqNO_BLOCK ) != pdPASS )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
\r
- #ifdef USE_STDIO\r
- void vPrintDisplayMessage( const char * const * ppcMessageToSend );\r
+ /* Set the guarded variable to a known start value. */\r
+ ulGuardedVariable = 0;\r
\r
- const char * const pcTaskStartMsg = "Mutex with priority inheritance test started.\r\n";\r
+ /* This task's priority should be as per that assigned when the task was\r
+ created. */\r
+ if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
\r
- /* Queue a message for printing to say the task has started. */\r
- vPrintDisplayMessage( &pcTaskStartMsg );\r
+ /* Now unsuspend the high priority task. This will attempt to take the\r
+ mutex, and block when it finds it cannot obtain it. */\r
+ vTaskResume( xHighPriorityMutexTask );\r
+\r
+ #if configUSE_PREEMPTION == 0\r
+ taskYIELD();\r
#endif\r
\r
- /* The local mutex is used to check the 'mutexs held' count. */\r
- xLocalMutex = xSemaphoreCreateMutex();\r
- configASSERT( xLocalMutex );\r
+ /* Ensure the task is reporting its priority as blocked and not\r
+ suspended (as it would have done in versions up to V7.5.3). */\r
+ #if( INCLUDE_eTaskGetState == 1 )\r
+ {\r
+ configASSERT( eTaskGetState( xHighPriorityMutexTask ) == eBlocked );\r
+ }\r
+ #endif /* INCLUDE_eTaskGetState */\r
\r
- for( ;; )\r
+ /* The priority of the high priority task should now have been inherited\r
+ as by now it will have attempted to get the mutex. */\r
+ if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
{\r
- /* Take the mutex. It should be available now. */\r
- if( xSemaphoreTake( xMutex, genqNO_BLOCK ) != pdPASS )\r
- {\r
- xErrorDetected = pdTRUE;\r
- }\r
+ xErrorDetected = pdTRUE;\r
+ }\r
\r
- /* Set the guarded variable to a known start value. */\r
- ulGuardedVariable = 0;\r
+ /* Attempt to set the priority of this task to the test priority -\r
+ between the idle priority and the medium/high test priorities, but the\r
+ actual priority should remain at the high priority. */\r
+ vTaskPrioritySet( NULL, genqMUTEX_TEST_PRIORITY );\r
+ if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
\r
- /* This task's priority should be as per that assigned when the task was\r
- created. */\r
- if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )\r
- {\r
- xErrorDetected = pdTRUE;\r
- }\r
+ /* Now unsuspend the medium priority task. This should not run as the\r
+ inherited priority of this task is above that of the medium priority\r
+ task. */\r
+ vTaskResume( xMediumPriorityMutexTask );\r
\r
- /* Now unsuspend the high priority task. This will attempt to take the\r
- mutex, and block when it finds it cannot obtain it. */\r
- vTaskResume( xHighPriorityMutexTask );\r
+ /* If the medium priority task did run then it will have incremented the\r
+ guarded variable. */\r
+ if( ulGuardedVariable != 0 )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
\r
- #if configUSE_PREEMPTION == 0\r
- taskYIELD();\r
- #endif\r
+ /* Take the local mutex too, so two mutexes are now held. */\r
+ if( xSemaphoreTake( xLocalMutex, genqNO_BLOCK ) != pdPASS )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
\r
- /* Ensure the task is reporting its priority as blocked and not\r
- suspended (as it would have done in versions up to V7.5.3). */\r
- #if( INCLUDE_eTaskGetState == 1 )\r
- {\r
- configASSERT( eTaskGetState( xHighPriorityMutexTask ) == eBlocked );\r
- }\r
- #endif /* INCLUDE_eTaskGetState */\r
+ /* When the semaphore is given back the priority of this task should not\r
+ yet be disinherited because the local mutex is still held. This is a\r
+ simplification to allow FreeRTOS to be integrated with middleware that\r
+ attempts to hold multiple mutexes without bloating the code with complex\r
+ algorithms. It is possible that the high priority mutex task will\r
+ execute as it shares a priority with this task. */\r
+ if( xSemaphoreGive( xMutex ) != pdPASS )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
\r
- /* The priority of the high priority task should now have been inherited\r
- as by now it will have attempted to get the mutex. */\r
- if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
- {\r
- xErrorDetected = pdTRUE;\r
- }\r
+ #if configUSE_PREEMPTION == 0\r
+ taskYIELD();\r
+ #endif\r
\r
- /* Attempt to set the priority of this task to the test priority -\r
- between the idle priority and the medium/high test priorities, but the\r
- actual priority should remain at the high priority. */\r
- vTaskPrioritySet( NULL, genqMUTEX_TEST_PRIORITY );\r
- if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
- {\r
- xErrorDetected = pdTRUE;\r
- }\r
+ /* The guarded variable is only incremented by the medium priority task,\r
+ which still should not have executed as this task should remain at the\r
+ higher priority, ensure this is the case. */\r
+ if( ulGuardedVariable != 0 )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
\r
- /* Now unsuspend the medium priority task. This should not run as the\r
- inherited priority of this task is above that of the medium priority\r
- task. */\r
- vTaskResume( xMediumPriorityMutexTask );\r
+ if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
\r
- /* If the medium priority task did run then it will have incremented the \r
- guarded variable. */\r
- if( ulGuardedVariable != 0 )\r
- {\r
- xErrorDetected = pdTRUE;\r
- }\r
+ /* Now also give back the local mutex, taking the held count back to 0.\r
+ This time the priority of this task should be disinherited back to the\r
+ priority to which it was set while the mutex was held. This means\r
+ the medium priority task should execute and increment the guarded\r
+ variable. When this task next runs both the high and medium priority\r
+ tasks will have been suspended again. */\r
+ if( xSemaphoreGive( xLocalMutex ) != pdPASS )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
\r
- /* Take the local mutex too, so two mutexes are now held. */\r
- if( xSemaphoreTake( xLocalMutex, genqNO_BLOCK ) != pdPASS )\r
- {\r
- xErrorDetected = pdTRUE;\r
- }\r
+ #if configUSE_PREEMPTION == 0\r
+ taskYIELD();\r
+ #endif\r
\r
- /* When the semaphore is given back the priority of this task should not\r
- yet be disinherited because the local mutex is still held. This is a\r
- simplification to allow FreeRTOS to be integrated with middleware that\r
- attempts to hold multiple mutexes without bloating the code with complex\r
- algorithms. It is possible that the high priority mutex task will\r
- execute as it shares a priority with this task. */\r
- if( xSemaphoreGive( xMutex ) != pdPASS )\r
- {\r
- xErrorDetected = pdTRUE;\r
- }\r
+ /* Check the guarded variable did indeed increment... */\r
+ if( ulGuardedVariable != 1 )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
\r
- #if configUSE_PREEMPTION == 0\r
- taskYIELD();\r
- #endif\r
+ /* ... and that the priority of this task has been disinherited to\r
+ genqMUTEX_TEST_PRIORITY. */\r
+ if( uxTaskPriorityGet( NULL ) != genqMUTEX_TEST_PRIORITY )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
\r
- /* The guarded variable is only incremented by the medium priority task,\r
- which still should not have executed as this task should remain at the\r
- higher priority, ensure this is the case. */\r
- if( ulGuardedVariable != 0 )\r
- {\r
- xErrorDetected = pdTRUE;\r
- }\r
+ /* Set the priority of this task back to its original value, ready for\r
+ the next loop around this test. */\r
+ vTaskPrioritySet( NULL, genqMUTEX_LOW_PRIORITY );\r
+}\r
+/*-----------------------------------------------------------*/\r
\r
- if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
- {\r
- xErrorDetected = pdTRUE;\r
- }\r
+static void prvTakeTwoMutexesReturnInSameOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex )\r
+{\r
+ /* Take the mutex. It should be available now. */\r
+ if( xSemaphoreTake( xMutex, genqNO_BLOCK ) != pdPASS )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
\r
- /* Now also give back the local mutex, taking the held count back to 0.\r
- This time the priority of this task should be disinherited back to the\r
- priority to which it was set while the mutex was held. This means\r
- the medium priority task should execute and increment the guarded \r
- variable. When this task next runs both the high and medium priority \r
- tasks will have been suspended again. */\r
- if( xSemaphoreGive( xLocalMutex ) != pdPASS )\r
- {\r
- xErrorDetected = pdTRUE;\r
- }\r
+ /* Set the guarded variable to a known start value. */\r
+ ulGuardedVariable = 0;\r
+\r
+ /* This task's priority should be as per that assigned when the task was\r
+ created. */\r
+ if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
+\r
+ /* Now unsuspend the high priority task. This will attempt to take the\r
+ mutex, and block when it finds it cannot obtain it. */\r
+ vTaskResume( xHighPriorityMutexTask );\r
+\r
+ #if configUSE_PREEMPTION == 0\r
+ taskYIELD();\r
+ #endif\r
+\r
+ /* Ensure the task is reporting its priority as blocked and not\r
+ suspended (as it would have done in versions up to V7.5.3). */\r
+ #if( INCLUDE_eTaskGetState == 1 )\r
+ {\r
+ configASSERT( eTaskGetState( xHighPriorityMutexTask ) == eBlocked );\r
+ }\r
+ #endif /* INCLUDE_eTaskGetState */\r
+\r
+ /* The priority of the high priority task should now have been inherited\r
+ as by now it will have attempted to get the mutex. */\r
+ if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
+\r
+ /* Now unsuspend the medium priority task. This should not run as the\r
+ inherited priority of this task is above that of the medium priority\r
+ task. */\r
+ vTaskResume( xMediumPriorityMutexTask );\r
+\r
+ /* If the medium priority task did run then it will have incremented the\r
+ guarded variable. */\r
+ if( ulGuardedVariable != 0 )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
+\r
+ /* Take the local mutex too, so two mutexes are now held. */\r
+ if( xSemaphoreTake( xLocalMutex, genqNO_BLOCK ) != pdPASS )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
+\r
+ /* When the local semaphore is given back the priority of this task should\r
+ not yet be disinherited because the shared mutex is still held. This is a\r
+ simplification to allow FreeRTOS to be integrated with middleware that\r
+ attempts to hold multiple mutexes without bloating the code with complex\r
+ algorithms. It is possible that the high priority mutex task will\r
+ execute as it shares a priority with this task. */\r
+ if( xSemaphoreGive( xLocalMutex ) != pdPASS )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
+\r
+ #if configUSE_PREEMPTION == 0\r
+ taskYIELD();\r
+ #endif\r
+\r
+ /* The guarded variable is only incremented by the medium priority task,\r
+ which still should not have executed as this task should remain at the\r
+ higher priority, ensure this is the case. */\r
+ if( ulGuardedVariable != 0 )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
+\r
+ if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
+\r
+ /* Now also give back the shared mutex, taking the held count back to 0.\r
+ This time the priority of this task should be disinherited back to the\r
+ priority at which it was created. This means the medium priority task\r
+ should execute and increment the guarded variable. When this task next runs\r
+ both the high and medium priority tasks will have been suspended again. */\r
+ if( xSemaphoreGive( xMutex ) != pdPASS )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
+\r
+ #if configUSE_PREEMPTION == 0\r
+ taskYIELD();\r
+ #endif\r
+\r
+ /* Check the guarded variable did indeed increment... */\r
+ if( ulGuardedVariable != 1 )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
+\r
+ /* ... and that the priority of this task has been disinherited to\r
+ genqMUTEX_LOW_PRIORITY. */\r
+ if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+static void prvLowPriorityMutexTask( void *pvParameters )\r
+{\r
+SemaphoreHandle_t xMutex = ( SemaphoreHandle_t ) pvParameters, xLocalMutex;\r
+\r
+ #ifdef USE_STDIO\r
+ void vPrintDisplayMessage( const char * const * ppcMessageToSend );\r
+\r
+ const char * const pcTaskStartMsg = "Mutex with priority inheritance test started.\r\n";\r
+\r
+ /* Queue a message for printing to say the task has started. */\r
+ vPrintDisplayMessage( &pcTaskStartMsg );\r
+ #endif\r
+\r
+ /* The local mutex is used to check the 'mutexs held' count. */\r
+ xLocalMutex = xSemaphoreCreateMutex();\r
+ configASSERT( xLocalMutex );\r
+\r
+ for( ;; )\r
+ {\r
+ /* The first tests exercise the priority inheritance when two mutexes\r
+ are taken then returned in a different order to which they were\r
+ taken. */\r
+ prvTakeTwoMutexesReturnInDifferentOrder( xMutex, xLocalMutex );\r
+\r
+ /* Just to show this task is still running. */\r
+ ulLoopCounter2++;\r
\r
#if configUSE_PREEMPTION == 0\r
taskYIELD();\r
#endif\r
\r
- /* Check the guarded variable did indeed increment... */\r
- if( ulGuardedVariable != 1 )\r
- {\r
- xErrorDetected = pdTRUE;\r
- }\r
-\r
- /* ... and that the priority of this task has been disinherited to\r
- genqMUTEX_TEST_PRIORITY. */\r
- if( uxTaskPriorityGet( NULL ) != genqMUTEX_TEST_PRIORITY )\r
- {\r
- xErrorDetected = pdTRUE;\r
- }\r
-\r
- /* Set the priority of this task back to its original value, ready for\r
- the next loop around this test. */\r
- vTaskPrioritySet( NULL, genqMUTEX_LOW_PRIORITY );\r
+ /* The second tests exercise the priority inheritance when two mutexes\r
+ are taken then returned in the same order in which they were taken. */\r
+ prvTakeTwoMutexesReturnInSameOrder( xMutex, xLocalMutex );\r
\r
/* Just to show this task is still running. */\r
ulLoopCounter2++;\r
}\r
/*-----------------------------------------------------------*/\r
\r
+/* NOTE: This function is not declared static to prevent compiler warnings in\r
+demos where the function is declared but not used. */\r
+void vInterruptMutexTask( void *pvParameters )\r
+{\r
+const TickType_t xInterruptGivePeriod = pdMS_TO_TICKS( genqINTERRUPT_MUTEX_GIVE_PERIOD_MS );\r
+volatile uint32_t ulLoops = 0;\r
+\r
+ /* Just to avoid compiler warnings. */\r
+ ( void ) pvParameters;\r
+\r
+ for( ;; )\r
+ {\r
+ /* Has to wait longer than the time between gives to make sure it\r
+ should definitely have received the mutex. */\r
+ if( xSemaphoreTake( xISRMutex, ( xInterruptGivePeriod * 2 ) ) != pdPASS )\r
+ {\r
+ xErrorDetected = pdTRUE;\r
+ }\r
+ else\r
+ {\r
+ ulLoops++;\r
+ }\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
+void vMutexISRInteractionTest( void )\r
+{\r
+static TickType_t xLastGiveTime = 0;\r
+TickType_t xTimeNow;\r
+\r
+ xTimeNow = xTaskGetTickCountFromISR();\r
+ if( ( xTimeNow - xLastGiveTime ) >= pdMS_TO_TICKS( genqINTERRUPT_MUTEX_GIVE_PERIOD_MS ) )\r
+ {\r
+ configASSERT( xISRMutex );\r
+ xSemaphoreGiveFromISR( xISRMutex, NULL );\r
+ xLastGiveTime = xTimeNow;\r
+ }\r
+}\r
+/*-----------------------------------------------------------*/\r
+\r
/* This is called to check that all the created tasks are still running. */\r
BaseType_t xAreGenericQueueTasksStillRunning( void )\r
{\r
static uint32_t ulLastLoopCounter = 0, ulLastLoopCounter2 = 0;\r
\r
- /* If the demo task is still running then we expect the loopcounters to\r
+ /* If the demo task is still running then we expect the loop counters to\r
have incremented since this function was last called. */\r
if( ulLastLoopCounter == ulLoopCounter )\r
{\r