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