]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Common/Minimal/GenQTest.c
b9022333ffd04f08332b06c3b80735af165c477d
[freertos] / FreeRTOS / Demo / Common / Minimal / GenQTest.c
1 /*\r
2  * FreeRTOS Kernel V10.2.1\r
3  * Copyright (C) 2019 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  *\r
22  * http://www.FreeRTOS.org\r
23  * http://aws.amazon.com/freertos\r
24  *\r
25  * 1 tab == 4 spaces!\r
26  */\r
27 \r
28 \r
29 /*\r
30  * Tests the extra queue functionality introduced in FreeRTOS.org V4.5.0 -\r
31  * including xQueueSendToFront(), xQueueSendToBack(), xQueuePeek() and\r
32  * mutex behaviour.\r
33  *\r
34  * See the comments above the prvSendFrontAndBackTest() and\r
35  * prvLowPriorityMutexTask() prototypes below for more information.\r
36  */\r
37 \r
38 /* Standard includes. */\r
39 #include <stdlib.h>\r
40 \r
41 /* Scheduler include files. */\r
42 #include "FreeRTOS.h"\r
43 #include "task.h"\r
44 #include "queue.h"\r
45 #include "semphr.h"\r
46 \r
47 /* Demo program include files. */\r
48 #include "GenQTest.h"\r
49 \r
50 #define genqQUEUE_LENGTH                ( 5 )\r
51 #define intsemNO_BLOCK                  ( 0 )\r
52 #define genqSHORT_BLOCK                 ( pdMS_TO_TICKS( 2 ) )\r
53 \r
54 #define genqMUTEX_LOW_PRIORITY          ( tskIDLE_PRIORITY )\r
55 #define genqMUTEX_TEST_PRIORITY         ( tskIDLE_PRIORITY + 1 )\r
56 #define genqMUTEX_MEDIUM_PRIORITY       ( tskIDLE_PRIORITY + 2 )\r
57 #define genqMUTEX_HIGH_PRIORITY         ( tskIDLE_PRIORITY + 3 )\r
58 \r
59 #ifndef genqMUTEX_TEST_TASK_STACK_SIZE\r
60         #define genqMUTEX_TEST_TASK_STACK_SIZE configMINIMAL_STACK_SIZE\r
61 #endif\r
62 \r
63 #ifndef genqGENERIC_QUEUE_TEST_TASK_STACK_SIZE\r
64         #define genqGENERIC_QUEUE_TEST_TASK_STACK_SIZE configMINIMAL_STACK_SIZE\r
65 #endif\r
66 /*-----------------------------------------------------------*/\r
67 \r
68 /*\r
69  * Tests the behaviour of the xQueueSendToFront() and xQueueSendToBack()\r
70  * macros by using both to fill a queue, then reading from the queue to\r
71  * check the resultant queue order is as expected.  Queue data is also\r
72  * peeked.\r
73  */\r
74 static void prvSendFrontAndBackTest( void *pvParameters );\r
75 \r
76 /*\r
77  * The following three tasks are used to demonstrate the mutex behaviour.\r
78  * Each task is given a different priority to demonstrate the priority\r
79  * inheritance mechanism.\r
80  *\r
81  * The low priority task obtains a mutex.  After this a high priority task\r
82  * attempts to obtain the same mutex, causing its priority to be inherited\r
83  * by the low priority task.  The task with the inherited high priority then\r
84  * resumes a medium priority task to ensure it is not blocked by the medium\r
85  * priority task while it holds the inherited high priority.  Once the mutex\r
86  * is returned the task with the inherited priority returns to its original\r
87  * low priority, and is therefore immediately preempted by first the high\r
88  * priority task and then the medium priority task before it can continue.\r
89  */\r
90 static void prvLowPriorityMutexTask( void *pvParameters );\r
91 static void prvMediumPriorityMutexTask( void *pvParameters );\r
92 static void prvHighPriorityMutexTask( void *pvParameters );\r
93 \r
94 /*\r
95  * Tests the behaviour when a low priority task inherits the priority of a\r
96  * higher priority task when taking two mutexes, and returns the mutexes in\r
97  * first the same order as the two mutexes were obtained, and second the\r
98  * opposite order as the two mutexes were obtained.\r
99  */\r
100 static void prvTakeTwoMutexesReturnInSameOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex );\r
101 static void prvTakeTwoMutexesReturnInDifferentOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex );\r
102 \r
103 #if( INCLUDE_xTaskAbortDelay == 1 )\r
104 \r
105         #if( configUSE_PREEMPTION == 0 )\r
106                 #error The additional tests included when INCLUDE_xTaskAbortDelay is 1 expect preemption to be used.\r
107         #endif\r
108 \r
109         /* Tests the behaviour when a low priority task inherits the priority of a\r
110         high priority task only for the high priority task to timeout before\r
111         obtaining the mutex. */\r
112         static void prvHighPriorityTimeout( SemaphoreHandle_t xMutex );\r
113 #endif\r
114 \r
115 /*-----------------------------------------------------------*/\r
116 \r
117 /* Flag that will be latched to pdTRUE should any unexpected behaviour be\r
118 detected in any of the tasks. */\r
119 static volatile BaseType_t xErrorDetected = pdFALSE;\r
120 \r
121 /* Counters that are incremented on each cycle of a test.  This is used to\r
122 detect a stalled task - a test that is no longer running. */\r
123 static volatile uint32_t ulLoopCounter = 0;\r
124 static volatile uint32_t ulLoopCounter2 = 0;\r
125 \r
126 /* The variable that is guarded by the mutex in the mutex demo tasks. */\r
127 static volatile uint32_t ulGuardedVariable = 0;\r
128 \r
129 /* Handles used in the mutex test to suspend and resume the high and medium\r
130 priority mutex test tasks. */\r
131 static TaskHandle_t xHighPriorityMutexTask, xMediumPriorityMutexTask;\r
132 \r
133 /* If INCLUDE_xTaskAbortDelay is 1 additional tests are performed, requiring an\r
134 additional task. */\r
135 #if( INCLUDE_xTaskAbortDelay == 1 )\r
136         static TaskHandle_t xSecondMediumPriorityMutexTask;\r
137 #endif\r
138 \r
139 /* Lets the high priority semaphore task know that its wait for the semaphore\r
140 was aborted, in which case not being able to obtain the semaphore is not to be\r
141 considered an error. */\r
142 static volatile BaseType_t xBlockWasAborted = pdFALSE;\r
143 \r
144 /*-----------------------------------------------------------*/\r
145 \r
146 void vStartGenericQueueTasks( UBaseType_t uxPriority )\r
147 {\r
148 QueueHandle_t xQueue;\r
149 SemaphoreHandle_t xMutex;\r
150 \r
151         /* Create the queue that we are going to use for the\r
152         prvSendFrontAndBackTest demo. */\r
153         xQueue = xQueueCreate( genqQUEUE_LENGTH, sizeof( uint32_t ) );\r
154 \r
155         if( xQueue != NULL )\r
156         {\r
157                 /* vQueueAddToRegistry() adds the queue to the queue registry, if one\r
158                 is in use.  The queue registry is provided as a means for kernel aware\r
159                 debuggers to locate queues and has no purpose if a kernel aware debugger\r
160                 is not being used.  The call to vQueueAddToRegistry() will be removed\r
161                 by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is\r
162                 defined to be less than 1. */\r
163                 vQueueAddToRegistry( xQueue, "Gen_Queue_Test" );\r
164 \r
165                 /* Create the demo task and pass it the queue just created.  We are\r
166                 passing the queue handle by value so it does not matter that it is\r
167                 declared on the stack here. */\r
168                 xTaskCreate( prvSendFrontAndBackTest, "GenQ", genqGENERIC_QUEUE_TEST_TASK_STACK_SIZE, ( void * ) xQueue, uxPriority, NULL );\r
169         }\r
170 \r
171         /* Create the mutex used by the prvMutexTest task. */\r
172         xMutex = xSemaphoreCreateMutex();\r
173 \r
174         if( xMutex != NULL )\r
175         {\r
176                 /* vQueueAddToRegistry() adds the mutex to the registry, if one is\r
177                 in use.  The registry is provided as a means for kernel aware\r
178                 debuggers to locate mutexes and has no purpose if a kernel aware\r
179                 debugger is not being used.  The call to vQueueAddToRegistry() will be\r
180                 removed by the pre-processor if configQUEUE_REGISTRY_SIZE is not\r
181                 defined or is defined to be less than 1. */\r
182                 vQueueAddToRegistry( ( QueueHandle_t ) xMutex, "Gen_Queue_Mutex" );\r
183 \r
184                 /* Create the mutex demo tasks and pass it the mutex just created.  We\r
185                 are passing the mutex handle by value so it does not matter that it is\r
186                 declared on the stack here. */\r
187                 xTaskCreate( prvLowPriorityMutexTask, "MuLow", genqMUTEX_TEST_TASK_STACK_SIZE, ( void * ) xMutex, genqMUTEX_LOW_PRIORITY, NULL );\r
188                 xTaskCreate( prvMediumPriorityMutexTask, "MuMed", configMINIMAL_STACK_SIZE, NULL, genqMUTEX_MEDIUM_PRIORITY, &xMediumPriorityMutexTask );\r
189                 xTaskCreate( prvHighPriorityMutexTask, "MuHigh", genqMUTEX_TEST_TASK_STACK_SIZE, ( void * ) xMutex, genqMUTEX_HIGH_PRIORITY, &xHighPriorityMutexTask );\r
190 \r
191                 /* If INCLUDE_xTaskAbortDelay is set then additional tests are performed,\r
192                 requiring two instances of prvHighPriorityMutexTask(). */\r
193                 #if( INCLUDE_xTaskAbortDelay == 1 )\r
194                 {\r
195                         xTaskCreate( prvHighPriorityMutexTask, "MuHigh2", configMINIMAL_STACK_SIZE, ( void * ) xMutex, genqMUTEX_MEDIUM_PRIORITY, &xSecondMediumPriorityMutexTask );\r
196                 }\r
197                 #endif /* INCLUDE_xTaskAbortDelay */\r
198         }\r
199 }\r
200 /*-----------------------------------------------------------*/\r
201 \r
202 static void prvSendFrontAndBackTest( void *pvParameters )\r
203 {\r
204 uint32_t ulData, ulData2, ulLoopCounterSnapshot;\r
205 QueueHandle_t xQueue;\r
206 \r
207         #ifdef USE_STDIO\r
208         void vPrintDisplayMessage( const char * const * ppcMessageToSend );\r
209 \r
210                 const char * const pcTaskStartMsg = "Queue SendToFront/SendToBack/Peek test started.\r\n";\r
211 \r
212                 /* Queue a message for printing to say the task has started. */\r
213                 vPrintDisplayMessage( &pcTaskStartMsg );\r
214         #endif\r
215 \r
216         xQueue = ( QueueHandle_t ) pvParameters;\r
217 \r
218         for( ;; )\r
219         {\r
220                 /* The queue is empty, so sending an item to the back of the queue\r
221                 should have the same effect as sending it to the front of the queue.\r
222 \r
223                 First send to the front and check everything is as expected. */\r
224                 ulLoopCounterSnapshot = ulLoopCounter;\r
225                 xQueueSendToFront( xQueue, ( void * ) &ulLoopCounterSnapshot, intsemNO_BLOCK );\r
226 \r
227                 if( uxQueueMessagesWaiting( xQueue ) != 1 )\r
228                 {\r
229                         xErrorDetected = pdTRUE;\r
230                 }\r
231 \r
232                 if( xQueueReceive( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != pdPASS )\r
233                 {\r
234                         xErrorDetected = pdTRUE;\r
235                 }\r
236 \r
237                 /* The data we sent to the queue should equal the data we just received\r
238                 from the queue. */\r
239                 if( ulLoopCounter != ulData )\r
240                 {\r
241                         xErrorDetected = pdTRUE;\r
242                 }\r
243 \r
244                 /* Then do the same, sending the data to the back, checking everything\r
245                 is as expected. */\r
246                 if( uxQueueMessagesWaiting( xQueue ) != 0 )\r
247                 {\r
248                         xErrorDetected = pdTRUE;\r
249                 }\r
250 \r
251                 ulLoopCounterSnapshot = ulLoopCounter;\r
252                 xQueueSendToBack( xQueue, ( void * ) &ulLoopCounterSnapshot, intsemNO_BLOCK );\r
253 \r
254                 if( uxQueueMessagesWaiting( xQueue ) != 1 )\r
255                 {\r
256                         xErrorDetected = pdTRUE;\r
257                 }\r
258 \r
259                 if( xQueueReceive( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != pdPASS )\r
260                 {\r
261                         xErrorDetected = pdTRUE;\r
262                 }\r
263 \r
264                 if( uxQueueMessagesWaiting( xQueue ) != 0 )\r
265                 {\r
266                         xErrorDetected = pdTRUE;\r
267                 }\r
268 \r
269                 /* The data sent to the queue should equal the data just received from\r
270                 the queue. */\r
271                 if( ulLoopCounter != ulData )\r
272                 {\r
273                         xErrorDetected = pdTRUE;\r
274                 }\r
275 \r
276                 #if configUSE_PREEMPTION == 0\r
277                         taskYIELD();\r
278                 #endif\r
279 \r
280 \r
281 \r
282                 /* Place 2, 3, 4 into the queue, adding items to the back of the queue. */\r
283                 for( ulData = 2; ulData < 5; ulData++ )\r
284                 {\r
285                         xQueueSendToBack( xQueue, ( void * ) &ulData, intsemNO_BLOCK );\r
286                 }\r
287 \r
288                 /* Now the order in the queue should be 2, 3, 4, with 2 being the first\r
289                 thing to be read out.  Now add 1 then 0 to the front of the queue. */\r
290                 if( uxQueueMessagesWaiting( xQueue ) != 3 )\r
291                 {\r
292                         xErrorDetected = pdTRUE;\r
293                 }\r
294                 ulData = 1;\r
295                 xQueueSendToFront( xQueue, ( void * ) &ulData, intsemNO_BLOCK );\r
296                 ulData = 0;\r
297                 xQueueSendToFront( xQueue, ( void * ) &ulData, intsemNO_BLOCK );\r
298 \r
299                 /* Now the queue should be full, and when we read the data out we\r
300                 should receive 0, 1, 2, 3, 4. */\r
301                 if( uxQueueMessagesWaiting( xQueue ) != 5 )\r
302                 {\r
303                         xErrorDetected = pdTRUE;\r
304                 }\r
305 \r
306                 if( xQueueSendToFront( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != errQUEUE_FULL )\r
307                 {\r
308                         xErrorDetected = pdTRUE;\r
309                 }\r
310 \r
311                 if( xQueueSendToBack( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != errQUEUE_FULL )\r
312                 {\r
313                         xErrorDetected = pdTRUE;\r
314                 }\r
315 \r
316                 #if configUSE_PREEMPTION == 0\r
317                         taskYIELD();\r
318                 #endif\r
319 \r
320                 /* Check the data we read out is in the expected order. */\r
321                 for( ulData = 0; ulData < genqQUEUE_LENGTH; ulData++ )\r
322                 {\r
323                         /* Try peeking the data first. */\r
324                         if( xQueuePeek( xQueue, &ulData2, intsemNO_BLOCK ) != pdPASS )\r
325                         {\r
326                                 xErrorDetected = pdTRUE;\r
327                         }\r
328 \r
329                         if( ulData != ulData2 )\r
330                         {\r
331                                 xErrorDetected = pdTRUE;\r
332                         }\r
333 \r
334 \r
335                         /* Now try receiving the data for real.  The value should be the\r
336                         same.  Clobber the value first so we know we really received it. */\r
337                         ulData2 = ~ulData2;\r
338                         if( xQueueReceive( xQueue, &ulData2, intsemNO_BLOCK ) != pdPASS )\r
339                         {\r
340                                 xErrorDetected = pdTRUE;\r
341                         }\r
342 \r
343                         if( ulData != ulData2 )\r
344                         {\r
345                                 xErrorDetected = pdTRUE;\r
346                         }\r
347                 }\r
348 \r
349                 /* The queue should now be empty again. */\r
350                 if( uxQueueMessagesWaiting( xQueue ) != 0 )\r
351                 {\r
352                         xErrorDetected = pdTRUE;\r
353                 }\r
354 \r
355                 #if configUSE_PREEMPTION == 0\r
356                         taskYIELD();\r
357                 #endif\r
358 \r
359 \r
360                 /* Our queue is empty once more, add 10, 11 to the back. */\r
361                 ulData = 10;\r
362                 if( xQueueSend( xQueue, &ulData, intsemNO_BLOCK ) != pdPASS )\r
363                 {\r
364                         xErrorDetected = pdTRUE;\r
365                 }\r
366                 ulData = 11;\r
367                 if( xQueueSend( xQueue, &ulData, intsemNO_BLOCK ) != pdPASS )\r
368                 {\r
369                         xErrorDetected = pdTRUE;\r
370                 }\r
371 \r
372                 if( uxQueueMessagesWaiting( xQueue ) != 2 )\r
373                 {\r
374                         xErrorDetected = pdTRUE;\r
375                 }\r
376 \r
377                 /* Now we should have 10, 11 in the queue.  Add 7, 8, 9 to the\r
378                 front. */\r
379                 for( ulData = 9; ulData >= 7; ulData-- )\r
380                 {\r
381                         if( xQueueSendToFront( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != pdPASS )\r
382                         {\r
383                                 xErrorDetected = pdTRUE;\r
384                         }\r
385                 }\r
386 \r
387                 /* Now check that the queue is full, and that receiving data provides\r
388                 the expected sequence of 7, 8, 9, 10, 11. */\r
389                 if( uxQueueMessagesWaiting( xQueue ) != 5 )\r
390                 {\r
391                         xErrorDetected = pdTRUE;\r
392                 }\r
393 \r
394                 if( xQueueSendToFront( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != errQUEUE_FULL )\r
395                 {\r
396                         xErrorDetected = pdTRUE;\r
397                 }\r
398 \r
399                 if( xQueueSendToBack( xQueue, ( void * ) &ulData, intsemNO_BLOCK ) != errQUEUE_FULL )\r
400                 {\r
401                         xErrorDetected = pdTRUE;\r
402                 }\r
403 \r
404                 #if configUSE_PREEMPTION == 0\r
405                         taskYIELD();\r
406                 #endif\r
407 \r
408                 /* Check the data we read out is in the expected order. */\r
409                 for( ulData = 7; ulData < ( 7 + genqQUEUE_LENGTH ); ulData++ )\r
410                 {\r
411                         if( xQueueReceive( xQueue, &ulData2, intsemNO_BLOCK ) != pdPASS )\r
412                         {\r
413                                 xErrorDetected = pdTRUE;\r
414                         }\r
415 \r
416                         if( ulData != ulData2 )\r
417                         {\r
418                                 xErrorDetected = pdTRUE;\r
419                         }\r
420                 }\r
421 \r
422                 if( uxQueueMessagesWaiting( xQueue ) != 0 )\r
423                 {\r
424                         xErrorDetected = pdTRUE;\r
425                 }\r
426 \r
427                 /* Increment the loop counter to indicate these tasks are still\r
428                 executing. */\r
429                 ulLoopCounter++;\r
430         }\r
431 }\r
432 /*-----------------------------------------------------------*/\r
433 \r
434 #if( INCLUDE_xTaskAbortDelay == 1 )\r
435 \r
436         static void prvHighPriorityTimeout( SemaphoreHandle_t xMutex )\r
437         {\r
438         static UBaseType_t uxLoopCount = 0;\r
439 \r
440                 /* The tests in this function are very similar, the slight variations\r
441                 are for code coverage purposes. */\r
442 \r
443                 /* Take the mutex.  It should be available now.  Check before and after\r
444                 taking that the holder is reported correctly. */\r
445                 if( xSemaphoreGetMutexHolder( xMutex ) != NULL )\r
446                 {\r
447                         xErrorDetected = pdTRUE;\r
448                 }\r
449                 if( xSemaphoreTake( xMutex, intsemNO_BLOCK ) != pdPASS )\r
450                 {\r
451                         xErrorDetected = pdTRUE;\r
452                 }\r
453                 if( xSemaphoreGetMutexHolder( xMutex ) != xTaskGetCurrentTaskHandle() )\r
454                 {\r
455                         xErrorDetected = pdTRUE;\r
456                 }\r
457 \r
458                 /* This task's priority should be as per that assigned when the task was\r
459                 created. */\r
460                 if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )\r
461                 {\r
462                         xErrorDetected = pdTRUE;\r
463                 }\r
464 \r
465                 /* Now unsuspend the high priority task.  This will attempt to take the\r
466                 mutex, and block when it finds it cannot obtain it. */\r
467                 vTaskResume( xHighPriorityMutexTask );\r
468 \r
469                 /* This task should now have inherited the priority of the high priority\r
470                 task as by now the high priority task will have attempted to obtain the\r
471                 mutex. */\r
472                 if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
473                 {\r
474                         xErrorDetected = pdTRUE;\r
475                 }\r
476 \r
477                 /* Unblock a second medium priority task.  It too will attempt to take\r
478                 the mutex and enter the Blocked state - it won't run yet though as this\r
479                 task has inherited a priority above it. */\r
480                 vTaskResume( xSecondMediumPriorityMutexTask );\r
481 \r
482                 /* This task should still have the priority of the high priority task as\r
483                 that had already been inherited as is the highest priority of the three\r
484                 tasks using the mutex. */\r
485                 if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
486                 {\r
487                         xErrorDetected = pdTRUE;\r
488                 }\r
489 \r
490                 /* On some loops, block for a short while to provide additional\r
491                 code coverage.  Blocking here will allow the medium priority task to\r
492                 execute and so also block on the mutex so when the high priority task\r
493                 causes this task to disinherit the high priority it is inherited down to\r
494                 the priority of the medium priority task.  When there is no delay the\r
495                 medium priority task will not run until after the disinheritance, so\r
496                 this task will disinherit back to its base priority, then only up to the\r
497                 medium priority after the medium priority has executed. */\r
498                 vTaskDelay( uxLoopCount & ( UBaseType_t ) 0x07 );\r
499 \r
500                 /* Now force the high priority task to unblock.  It will fail to obtain\r
501                 the mutex and go back to the suspended state - allowing this task to\r
502                 execute again.  xBlockWasAborted is set to pdTRUE so the higher priority\r
503                 task knows that its failure to obtain the semaphore is not an error. */\r
504                 xBlockWasAborted = pdTRUE;\r
505                 if( xTaskAbortDelay( xHighPriorityMutexTask ) != pdPASS )\r
506                 {\r
507                         xErrorDetected = pdTRUE;\r
508                 }\r
509 \r
510                 /* This task has inherited the priority of xHighPriorityMutexTask so\r
511                 could still be running even though xHighPriorityMutexTask is no longer\r
512                 blocked.  Delay for a short while to ensure xHighPriorityMutexTask gets\r
513                 a chance to run - indicated by this task changing priority.  It should\r
514                 disinherit the high priority task, but then inherit the priority of the\r
515                 medium priority task that is waiting for the same mutex. */\r
516                 while( uxTaskPriorityGet( NULL ) != genqMUTEX_MEDIUM_PRIORITY )\r
517                 {\r
518                         /* If this task gets stuck here then the check variables will stop\r
519                         incrementing and the check task will detect the error. */\r
520                         vTaskDelay( genqSHORT_BLOCK );\r
521                 }\r
522 \r
523                 /* Now force the medium priority task to unblock.  xBlockWasAborted is\r
524                 set to pdTRUE so the medium priority task knows that its failure to\r
525                 obtain the semaphore is not an error. */\r
526                 xBlockWasAborted = pdTRUE;\r
527                 if( xTaskAbortDelay( xSecondMediumPriorityMutexTask ) != pdPASS )\r
528                 {\r
529                         xErrorDetected = pdTRUE;\r
530                 }\r
531 \r
532                 /* This time no other tasks are waiting for the mutex, so this task\r
533                 should return to its base priority.  This might not happen straight\r
534                 away as it is running at the same priority as the task it just\r
535                 unblocked. */\r
536                 while( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )\r
537                 {\r
538                         /* If this task gets stuck here then the check variables will stop\r
539                         incrementing and the check task will detect the error. */\r
540                         vTaskDelay( genqSHORT_BLOCK );\r
541                 }\r
542 \r
543                 /* Give the semaphore back ready for the next test.  Check the mutex\r
544                 holder before and after using the "FromISR" version for code coverage. */\r
545                 if( xSemaphoreGetMutexHolderFromISR( xMutex ) != xTaskGetCurrentTaskHandle() )\r
546                 {\r
547                         xErrorDetected = pdTRUE;\r
548                 }\r
549                 xSemaphoreGive( xMutex );\r
550                 if( xSemaphoreGetMutexHolderFromISR( xMutex ) != NULL )\r
551                 {\r
552                         xErrorDetected = pdTRUE;\r
553                 }\r
554 \r
555                 configASSERT( xErrorDetected == pdFALSE );\r
556 \r
557 \r
558 \r
559                 /* Now do the same again, but this time unsuspend the tasks in the\r
560                 opposite order.  This takes a different path though the code because\r
561                 when the high priority task has its block aborted there is already\r
562                 another task in the list of tasks waiting for the mutex, and the\r
563                 low priority task drops down to that priority, rather than dropping\r
564                 down to its base priority before inheriting the priority of the medium\r
565                 priority task. */\r
566                 if( xSemaphoreTake( xMutex, intsemNO_BLOCK ) != pdPASS )\r
567                 {\r
568                         xErrorDetected = pdTRUE;\r
569                 }\r
570 \r
571                 if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )\r
572                 {\r
573                         xErrorDetected = pdTRUE;\r
574                 }\r
575 \r
576                 /* This time unsuspend the medium priority task first.  This will\r
577                 attempt to take the mutex, and block when it finds it cannot obtain it. */\r
578                 vTaskResume( xSecondMediumPriorityMutexTask );\r
579 \r
580                 /* This time this task should now have inherited the priority of the\r
581                 medium task. */\r
582                 if( uxTaskPriorityGet( NULL ) != genqMUTEX_MEDIUM_PRIORITY )\r
583                 {\r
584                         xErrorDetected = pdTRUE;\r
585                 }\r
586 \r
587                 /* This time the high priority task in unsuspended second. */\r
588                 vTaskResume( xHighPriorityMutexTask );\r
589 \r
590                 /* The high priority task should already have run, causing this task to\r
591                 inherit a priority for the second time. */\r
592                 if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
593                 {\r
594                         xErrorDetected = pdTRUE;\r
595                 }\r
596 \r
597                 /* This time, when the high priority task has its delay aborted and it\r
598                 fails to obtain the mutex this task will immediately have its priority\r
599                 lowered down to that of the highest priority task waiting on the mutex,\r
600                 which is the medium priority task. */\r
601                 xBlockWasAborted = pdTRUE;\r
602                 if( xTaskAbortDelay( xHighPriorityMutexTask ) != pdPASS )\r
603                 {\r
604                         xErrorDetected = pdTRUE;\r
605                 }\r
606 \r
607                 while( uxTaskPriorityGet( NULL ) != genqMUTEX_MEDIUM_PRIORITY )\r
608                 {\r
609                         /* If this task gets stuck here then the check variables will stop\r
610                         incrementing and the check task will detect the error. */\r
611                         vTaskDelay( genqSHORT_BLOCK );\r
612                 }\r
613 \r
614                 /* And finally, when the medium priority task also have its delay\r
615                 aborted there are no other tasks waiting for the mutex so this task\r
616                 returns to its base priority. */\r
617                 xBlockWasAborted = pdTRUE;\r
618                 if( xTaskAbortDelay( xSecondMediumPriorityMutexTask ) != pdPASS )\r
619                 {\r
620                         xErrorDetected = pdTRUE;\r
621                 }\r
622 \r
623                 while( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )\r
624                 {\r
625                         /* If this task gets stuck here then the check variables will stop\r
626                         incrementing and the check task will detect the error. */\r
627                         vTaskDelay( genqSHORT_BLOCK );\r
628                 }\r
629 \r
630                 /* Give the semaphore back ready for the next test. */\r
631                 xSemaphoreGive( xMutex );\r
632 \r
633                 configASSERT( xErrorDetected == pdFALSE );\r
634 \r
635                 /* uxLoopCount is used to add a variable delay, and in-so-doing provide\r
636                 additional code coverage. */\r
637                 uxLoopCount++;\r
638         }\r
639 \r
640 #endif /* INCLUDE_xTaskAbortDelay == 1 */\r
641 /*-----------------------------------------------------------*/\r
642 \r
643 static void prvTakeTwoMutexesReturnInDifferentOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex )\r
644 {\r
645         /* Take the mutex.  It should be available now. */\r
646         if( xSemaphoreTake( xMutex, intsemNO_BLOCK ) != pdPASS )\r
647         {\r
648                 xErrorDetected = pdTRUE;\r
649         }\r
650 \r
651         /* Set the guarded variable to a known start value. */\r
652         ulGuardedVariable = 0;\r
653 \r
654         /* This task's priority should be as per that assigned when the task was\r
655         created. */\r
656         if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )\r
657         {\r
658                 xErrorDetected = pdTRUE;\r
659         }\r
660 \r
661         /* Now unsuspend the high priority task.  This will attempt to take the\r
662         mutex, and block when it finds it cannot obtain it. */\r
663         vTaskResume( xHighPriorityMutexTask );\r
664 \r
665         #if configUSE_PREEMPTION == 0\r
666                 taskYIELD();\r
667         #endif\r
668 \r
669         /* Ensure the task is reporting its priority as blocked and not\r
670         suspended (as it would have done in versions up to V7.5.3). */\r
671         #if( INCLUDE_eTaskGetState == 1 )\r
672         {\r
673                 configASSERT( eTaskGetState( xHighPriorityMutexTask ) == eBlocked );\r
674         }\r
675         #endif /* INCLUDE_eTaskGetState */\r
676 \r
677         /* This task should now have inherited the priority of the high priority\r
678         task as by now the high priority task will have attempted to obtain the\r
679         mutex. */\r
680         if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
681         {\r
682                 xErrorDetected = pdTRUE;\r
683         }\r
684 \r
685         /* Attempt to set the priority of this task to the test priority -\r
686         between the idle priority and the medium/high test priorities, but the\r
687         actual priority should remain at the high priority. */\r
688         vTaskPrioritySet( NULL, genqMUTEX_TEST_PRIORITY );\r
689         if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
690         {\r
691                 xErrorDetected = pdTRUE;\r
692         }\r
693 \r
694         /* Now unsuspend the medium priority task.  This should not run as the\r
695         inherited priority of this task is above that of the medium priority\r
696         task. */\r
697         vTaskResume( xMediumPriorityMutexTask );\r
698 \r
699         /* If the medium priority task did run then it will have incremented the\r
700         guarded variable. */\r
701         if( ulGuardedVariable != 0 )\r
702         {\r
703                 xErrorDetected = pdTRUE;\r
704         }\r
705 \r
706         /* Take the local mutex too, so two mutexes are now held. */\r
707         if( xSemaphoreTake( xLocalMutex, intsemNO_BLOCK ) != pdPASS )\r
708         {\r
709                 xErrorDetected = pdTRUE;\r
710         }\r
711 \r
712         /* When the semaphore is given back the priority of this task should not\r
713         yet be disinherited because the local mutex is still held.  This is a\r
714         simplification to allow FreeRTOS to be integrated with middleware that\r
715         attempts to hold multiple mutexes without bloating the code with complex\r
716         algorithms.  It is possible that the high priority mutex task will\r
717         execute as it shares a priority with this task. */\r
718         if( xSemaphoreGive( xMutex ) != pdPASS )\r
719         {\r
720                 xErrorDetected = pdTRUE;\r
721         }\r
722 \r
723         #if configUSE_PREEMPTION == 0\r
724                 taskYIELD();\r
725         #endif\r
726 \r
727         /* The guarded variable is only incremented by the medium priority task,\r
728         which still should not have executed as this task should remain at the\r
729         higher priority, ensure this is the case. */\r
730         if( ulGuardedVariable != 0 )\r
731         {\r
732                 xErrorDetected = pdTRUE;\r
733         }\r
734 \r
735         if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
736         {\r
737                 xErrorDetected = pdTRUE;\r
738         }\r
739 \r
740         /* Now also give back the local mutex, taking the held count back to 0.\r
741         This time the priority of this task should be disinherited back to the\r
742         priority to which it was set while the mutex was held.  This means\r
743         the medium priority task should execute and increment the guarded\r
744         variable.   When this task next runs both the high and medium priority\r
745         tasks will have been suspended again. */\r
746         if( xSemaphoreGive( xLocalMutex ) != pdPASS )\r
747         {\r
748                 xErrorDetected = pdTRUE;\r
749         }\r
750 \r
751         #if configUSE_PREEMPTION == 0\r
752                 taskYIELD();\r
753         #endif\r
754 \r
755         /* Check the guarded variable did indeed increment... */\r
756         if( ulGuardedVariable != 1 )\r
757         {\r
758                 xErrorDetected = pdTRUE;\r
759         }\r
760 \r
761         /* ... and that the priority of this task has been disinherited to\r
762         genqMUTEX_TEST_PRIORITY. */\r
763         if( uxTaskPriorityGet( NULL ) != genqMUTEX_TEST_PRIORITY )\r
764         {\r
765                 xErrorDetected = pdTRUE;\r
766         }\r
767 \r
768         /* Set the priority of this task back to its original value, ready for\r
769         the next loop around this test. */\r
770         vTaskPrioritySet( NULL, genqMUTEX_LOW_PRIORITY );\r
771 }\r
772 /*-----------------------------------------------------------*/\r
773 \r
774 static void prvTakeTwoMutexesReturnInSameOrder( SemaphoreHandle_t xMutex, SemaphoreHandle_t xLocalMutex )\r
775 {\r
776         /* Take the mutex.  It should be available now. */\r
777         if( xSemaphoreTake( xMutex, intsemNO_BLOCK ) != pdPASS )\r
778         {\r
779                 xErrorDetected = pdTRUE;\r
780         }\r
781 \r
782         /* Set the guarded variable to a known start value. */\r
783         ulGuardedVariable = 0;\r
784 \r
785         /* This task's priority should be as per that assigned when the task was\r
786         created. */\r
787         if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )\r
788         {\r
789                 xErrorDetected = pdTRUE;\r
790         }\r
791 \r
792         /* Now unsuspend the high priority task.  This will attempt to take the\r
793         mutex, and block when it finds it cannot obtain it. */\r
794         vTaskResume( xHighPriorityMutexTask );\r
795 \r
796         #if configUSE_PREEMPTION == 0\r
797                 taskYIELD();\r
798         #endif\r
799 \r
800         /* Ensure the task is reporting its priority as blocked and not\r
801         suspended (as it would have done in versions up to V7.5.3). */\r
802         #if( INCLUDE_eTaskGetState == 1 )\r
803         {\r
804                 configASSERT( eTaskGetState( xHighPriorityMutexTask ) == eBlocked );\r
805         }\r
806         #endif /* INCLUDE_eTaskGetState */\r
807 \r
808         /* This task should now have inherited the priority of the high priority\r
809         task as by now the high priority task will have attempted to obtain the\r
810         mutex. */\r
811         if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
812         {\r
813                 xErrorDetected = pdTRUE;\r
814         }\r
815 \r
816         /* Now unsuspend the medium priority task.  This should not run as the\r
817         inherited priority of this task is above that of the medium priority\r
818         task. */\r
819         vTaskResume( xMediumPriorityMutexTask );\r
820 \r
821         /* If the medium priority task did run then it will have incremented the\r
822         guarded variable. */\r
823         if( ulGuardedVariable != 0 )\r
824         {\r
825                 xErrorDetected = pdTRUE;\r
826         }\r
827 \r
828         /* Take the local mutex too, so two mutexes are now held. */\r
829         if( xSemaphoreTake( xLocalMutex, intsemNO_BLOCK ) != pdPASS )\r
830         {\r
831                 xErrorDetected = pdTRUE;\r
832         }\r
833 \r
834         /* When the local semaphore is given back the priority of this task should\r
835         not yet be disinherited because the shared mutex is still held.  This is a\r
836         simplification to allow FreeRTOS to be integrated with middleware that\r
837         attempts to hold multiple mutexes without bloating the code with complex\r
838         algorithms.  It is possible that the high priority mutex task will\r
839         execute as it shares a priority with this task. */\r
840         if( xSemaphoreGive( xLocalMutex ) != pdPASS )\r
841         {\r
842                 xErrorDetected = pdTRUE;\r
843         }\r
844 \r
845         #if configUSE_PREEMPTION == 0\r
846                 taskYIELD();\r
847         #endif\r
848 \r
849         /* The guarded variable is only incremented by the medium priority task,\r
850         which still should not have executed as this task should remain at the\r
851         higher priority, ensure this is the case. */\r
852         if( ulGuardedVariable != 0 )\r
853         {\r
854                 xErrorDetected = pdTRUE;\r
855         }\r
856 \r
857         if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
858         {\r
859                 xErrorDetected = pdTRUE;\r
860         }\r
861 \r
862         /* Now also give back the shared mutex, taking the held count back to 0.\r
863         This time the priority of this task should be disinherited back to the\r
864         priority at which it was created.  This means the medium priority task\r
865         should execute and increment the guarded variable.  When this task next runs\r
866         both the high and medium priority tasks will have been suspended again. */\r
867         if( xSemaphoreGive( xMutex ) != pdPASS )\r
868         {\r
869                 xErrorDetected = pdTRUE;\r
870         }\r
871 \r
872         #if configUSE_PREEMPTION == 0\r
873                 taskYIELD();\r
874         #endif\r
875 \r
876         /* Check the guarded variable did indeed increment... */\r
877         if( ulGuardedVariable != 1 )\r
878         {\r
879                 xErrorDetected = pdTRUE;\r
880         }\r
881 \r
882         /* ... and that the priority of this task has been disinherited to\r
883         genqMUTEX_LOW_PRIORITY. */\r
884         if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )\r
885         {\r
886                 xErrorDetected = pdTRUE;\r
887         }\r
888 }\r
889 /*-----------------------------------------------------------*/\r
890 \r
891 static void prvLowPriorityMutexTask( void *pvParameters )\r
892 {\r
893 SemaphoreHandle_t xMutex = ( SemaphoreHandle_t ) pvParameters, xLocalMutex;\r
894 \r
895         #ifdef USE_STDIO\r
896         void vPrintDisplayMessage( const char * const * ppcMessageToSend );\r
897 \r
898                 const char * const pcTaskStartMsg = "Mutex with priority inheritance test started.\r\n";\r
899 \r
900                 /* Queue a message for printing to say the task has started. */\r
901                 vPrintDisplayMessage( &pcTaskStartMsg );\r
902         #endif\r
903 \r
904         /* The local mutex is used to check the 'mutexs held' count. */\r
905         xLocalMutex = xSemaphoreCreateMutex();\r
906         configASSERT( xLocalMutex );\r
907 \r
908         for( ;; )\r
909         {\r
910                 /* The first tests exercise the priority inheritance when two mutexes\r
911                 are taken then returned in a different order to which they were\r
912                 taken. */\r
913                 prvTakeTwoMutexesReturnInDifferentOrder( xMutex, xLocalMutex );\r
914 \r
915                 /* Just to show this task is still running. */\r
916                 ulLoopCounter2++;\r
917 \r
918                 #if configUSE_PREEMPTION == 0\r
919                         taskYIELD();\r
920                 #endif\r
921 \r
922                 /* The second tests exercise the priority inheritance when two mutexes\r
923                 are taken then returned in the same order in which they were taken. */\r
924                 prvTakeTwoMutexesReturnInSameOrder( xMutex, xLocalMutex );\r
925 \r
926                 /* Just to show this task is still running. */\r
927                 ulLoopCounter2++;\r
928 \r
929                 #if configUSE_PREEMPTION == 0\r
930                         taskYIELD();\r
931                 #endif\r
932 \r
933                 #if( INCLUDE_xTaskAbortDelay == 1 )\r
934                 {\r
935                         /* Tests the behaviour when a low priority task inherits the\r
936                         priority of a high priority task only for the high priority task to\r
937                         timeout before obtaining the mutex. */\r
938                         prvHighPriorityTimeout( xMutex );\r
939                 }\r
940                 #endif\r
941         }\r
942 }\r
943 /*-----------------------------------------------------------*/\r
944 \r
945 static void prvMediumPriorityMutexTask( void *pvParameters )\r
946 {\r
947         ( void ) pvParameters;\r
948 \r
949         for( ;; )\r
950         {\r
951                 /* The medium priority task starts by suspending itself.  The low\r
952                 priority task will unsuspend this task when required. */\r
953                 vTaskSuspend( NULL );\r
954 \r
955                 /* When this task unsuspends all it does is increment the guarded\r
956                 variable, this is so the low priority task knows that it has\r
957                 executed. */\r
958                 ulGuardedVariable++;\r
959         }\r
960 }\r
961 /*-----------------------------------------------------------*/\r
962 \r
963 static void prvHighPriorityMutexTask( void *pvParameters )\r
964 {\r
965 SemaphoreHandle_t xMutex = ( SemaphoreHandle_t ) pvParameters;\r
966 \r
967         for( ;; )\r
968         {\r
969                 /* The high priority task starts by suspending itself.  The low\r
970                 priority task will unsuspend this task when required. */\r
971                 vTaskSuspend( NULL );\r
972 \r
973                 /* When this task unsuspends all it does is attempt to obtain the\r
974                 mutex.  It should find the mutex is not available so a block time is\r
975                 specified. */\r
976                 if( xSemaphoreTake( xMutex, portMAX_DELAY ) != pdPASS )\r
977                 {\r
978                         /* This task would expect to obtain the mutex unless its wait for\r
979                         the mutex was aborted. */\r
980                         if( xBlockWasAborted == pdFALSE )\r
981                         {\r
982                                 xErrorDetected = pdTRUE;\r
983                         }\r
984                         else\r
985                         {\r
986                                 xBlockWasAborted = pdFALSE;\r
987                         }\r
988                 }\r
989                 else\r
990                 {\r
991                         /* When the mutex is eventually obtained it is just given back before\r
992                         returning to suspend ready for the next cycle. */\r
993                         if( xSemaphoreGive( xMutex ) != pdPASS )\r
994                         {\r
995                                 xErrorDetected = pdTRUE;\r
996                         }\r
997                 }\r
998         }\r
999 }\r
1000 /*-----------------------------------------------------------*/\r
1001 \r
1002 \r
1003 /* This is called to check that all the created tasks are still running. */\r
1004 BaseType_t xAreGenericQueueTasksStillRunning( void )\r
1005 {\r
1006 static uint32_t ulLastLoopCounter = 0, ulLastLoopCounter2 = 0;\r
1007 \r
1008         /* If the demo task is still running then we expect the loop counters to\r
1009         have incremented since this function was last called. */\r
1010         if( ulLastLoopCounter == ulLoopCounter )\r
1011         {\r
1012                 xErrorDetected = pdTRUE;\r
1013         }\r
1014 \r
1015         if( ulLastLoopCounter2 == ulLoopCounter2 )\r
1016         {\r
1017                 xErrorDetected = pdTRUE;\r
1018         }\r
1019 \r
1020         ulLastLoopCounter = ulLoopCounter;\r
1021         ulLastLoopCounter2 = ulLoopCounter2;\r
1022 \r
1023         /* Errors detected in the task itself will have latched xErrorDetected\r
1024         to true. */\r
1025 \r
1026         return ( BaseType_t ) !xErrorDetected;\r
1027 }\r
1028 \r
1029 \r