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