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