]> git.sur5r.net Git - freertos/blob - Demo/Common/Minimal/GenQTest.c
275b814d3348b35f3dcc4de1e92b0b131194ac2f
[freertos] / Demo / Common / Minimal / GenQTest.c
1 /*\r
2         FreeRTOS.org V4.4.0 - Copyright (C) 2003-2007 Richard Barry.\r
3 \r
4         This file is part of the FreeRTOS.org distribution.\r
5 \r
6         FreeRTOS.org is free software; you can redistribute it and/or modify\r
7         it under the terms of the GNU General Public License as published by\r
8         the Free Software Foundation; either version 2 of the License, or\r
9         (at your option) any later version.\r
10 \r
11         FreeRTOS.org is distributed in the hope that it will be useful,\r
12         but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14         GNU General Public License for more details.\r
15 \r
16         You should have received a copy of the GNU General Public License\r
17         along with FreeRTOS.org; if not, write to the Free Software\r
18         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19 \r
20         A special exception to the GPL can be applied should you wish to distribute\r
21         a combined work that includes FreeRTOS.org, without being obliged to provide\r
22         the source code for any proprietary components.  See the licensing section\r
23         of http://www.FreeRTOS.org for full details of how and when the exception\r
24         can be applied.\r
25 \r
26         ***************************************************************************\r
27         See http://www.FreeRTOS.org for documentation, latest information, license\r
28         and contact details.  Please ensure to read the configuration and relevant\r
29         port sections of the online documentation.\r
30 \r
31         Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along\r
32         with commercial development and support options.\r
33         ***************************************************************************\r
34 */\r
35 \r
36 \r
37 /* \r
38  * Tests the extra queue functionality introduced in FreeRTOS.org V4.5.0 - \r
39  * including xQueueSendToFront(), xQueueSendToBack(), xQueuePeek() and \r
40  * mutex behaviour. \r
41  *\r
42  * See the comments above the prvSendFrontAndBackTest() and \r
43  * prvLowPriorityMutexTask() prototypes below for more information.\r
44  */\r
45 \r
46 \r
47 #include <stdlib.h>\r
48 \r
49 /* Scheduler include files. */\r
50 #include "FreeRTOS.h"\r
51 #include "task.h"\r
52 #include "queue.h"\r
53 #include "semphr.h"\r
54 \r
55 /* Demo program include files. */\r
56 #include "GenQTest.h"\r
57 \r
58 #define genqQUEUE_LENGTH                ( 5 )\r
59 #define genqNO_BLOCK                    ( 0 )\r
60 \r
61 #define genqMUTEX_LOW_PRIORITY          ( tskIDLE_PRIORITY )\r
62 #define genqMUTEX_TEST_PRIORITY         ( tskIDLE_PRIORITY + 1 )\r
63 #define genqMUTEX_MEDIUM_PRIORITY       ( tskIDLE_PRIORITY + 2 )\r
64 #define genqMUTEX_HIGH_PRIORITY         ( tskIDLE_PRIORITY + 3 )\r
65 \r
66 /*-----------------------------------------------------------*/\r
67 \r
68 /*\r
69  * Tests the behaviour of the xQueueSendToFront() and xQueueSendToBack()\r
70  * macros by using both to fill a queue, then reading from the queue to\r
71  * check the resultant queue order is as expected.  Queue data is also\r
72  * peeked.\r
73  */\r
74 static void prvSendFrontAndBackTest( void *pvParameters );\r
75 \r
76 /*\r
77  * The following three tasks are used to demonstrate the mutex behaviour.\r
78  * Each task is given a different priority to demonstrate the priority\r
79  * inheritance mechanism.\r
80  *\r
81  * The low priority task obtains a mutex.  After this a high priority task\r
82  * attempts to obtain the same mutex, causing its priority to be inherited\r
83  * by the low priority task.  The task with the inherited high priority then\r
84  * resumes a medium priority task to ensure it is not blocked by the medium\r
85  * priority task while it holds the inherited high priority.  Once the mutex\r
86  * is returned the task with the inherited priority returns to its original\r
87  * low priority, and is therefore immediately preempted by first the high\r
88  * priority task and then the medium prioroity task before it can continue.\r
89  */\r
90 static void prvLowPriorityMutexTask( void *pvParameters );\r
91 static void prvMediumPriorityMutexTask( void *pvParameters );\r
92 static void prvHighPriorityMutexTask( void *pvParameters );\r
93 \r
94 /*-----------------------------------------------------------*/\r
95 \r
96 /* Flag that will be latched to pdTRUE should any unexpected behaviour be\r
97 detected in any of the tasks. */\r
98 static portBASE_TYPE xErrorDetected = pdFALSE;\r
99 \r
100 /* Counters that are incremented on each cycle of a test.  This is used to\r
101 detect a stalled task - a test that is no longer running. */\r
102 static volatile unsigned portLONG ulLoopCounter = 0;\r
103 static volatile unsigned portLONG ulLoopCounter2 = 0;\r
104 \r
105 /* The variable that is guarded by the mutex in the mutex demo tasks. */\r
106 static volatile unsigned portLONG ulGuardedVariable = 0;\r
107 \r
108 /* Handles used in the mutext test to suspend and resume the high and medium\r
109 priority mutex test tasks. */\r
110 static xTaskHandle xHighPriorityMutexTask, xMediumPriorityMutexTask;\r
111 \r
112 /*-----------------------------------------------------------*/\r
113 \r
114 void vStartGenericQueueTasks( unsigned portBASE_TYPE uxPriority )\r
115 {\r
116 xQueueHandle xQueue;\r
117 xSemaphoreHandle xMutex;\r
118 \r
119         /* Create the queue that we are going to use for the\r
120         prvSendFrontAndBackTest demo. */\r
121         xQueue = xQueueCreate( genqQUEUE_LENGTH, sizeof( unsigned portLONG ) );\r
122 \r
123         /* Create the demo task and pass it the queue just created.  We are\r
124         passing the queue handle by value so it does not matter that it is\r
125         declared on the stack here. */\r
126         xTaskCreate( prvSendFrontAndBackTest, "GenQ", configMINIMAL_STACK_SIZE, ( void * ) xQueue, uxPriority, NULL );\r
127 \r
128         /* Create the mutex used by the prvMutexTest task. */\r
129         xMutex = xSemaphoreCreateMutex();\r
130 \r
131         /* Create the mutex demo tasks and pass it the mutex just created.  We are\r
132         passing the mutex handle by value so it does not matter that it is declared\r
133         on the stack here. */\r
134         xTaskCreate( prvLowPriorityMutexTask, "MuLow", configMINIMAL_STACK_SIZE, ( void * ) xMutex, genqMUTEX_LOW_PRIORITY, NULL );\r
135         xTaskCreate( prvMediumPriorityMutexTask, "MuMed", configMINIMAL_STACK_SIZE, NULL, genqMUTEX_MEDIUM_PRIORITY, &xMediumPriorityMutexTask );\r
136         xTaskCreate( prvHighPriorityMutexTask, "MuHigh", configMINIMAL_STACK_SIZE, ( void * ) xMutex, genqMUTEX_HIGH_PRIORITY, &xHighPriorityMutexTask );\r
137 }\r
138 /*-----------------------------------------------------------*/\r
139 \r
140 static void prvSendFrontAndBackTest( void *pvParameters )\r
141 {\r
142 unsigned portLONG ulData, ulData2;\r
143 xQueueHandle xQueue;\r
144 \r
145         #ifdef USE_STDIO\r
146         void vPrintDisplayMessage( const portCHAR * const * ppcMessageToSend );\r
147         \r
148                 const portCHAR * const pcTaskStartMsg = "Queue SendToFront/SendToBack/Peek test started.\r\n";\r
149 \r
150                 /* Queue a message for printing to say the task has started. */\r
151                 vPrintDisplayMessage( &pcTaskStartMsg );\r
152         #endif\r
153 \r
154         xQueue = ( xQueueHandle ) pvParameters;\r
155 \r
156         for( ;; )\r
157         {\r
158                 /* The queue is empty, so sending an item to the back of the queue\r
159                 should have the same efect as sending it to the front of the queue.\r
160 \r
161                 First send to the front and check everything is as expected. */\r
162                 xQueueSendToFront( xQueue, ( void * ) &ulLoopCounter, genqNO_BLOCK );\r
163 \r
164                 if( uxQueueMessagesWaiting( xQueue ) != 1 )\r
165                 {\r
166                         xErrorDetected = pdTRUE;\r
167                 }\r
168 \r
169                 if( xQueueReceive( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != pdPASS )\r
170                 {\r
171                         xErrorDetected = pdTRUE;\r
172                 }\r
173 \r
174                 /* The data we sent to the queue should equal the data we just received\r
175                 from the queue. */\r
176                 if( ulLoopCounter != ulData )\r
177                 {\r
178                         xErrorDetected = pdTRUE;\r
179                 }\r
180 \r
181                 /* Then do the same, sending the data to the back, checking everything\r
182                 is as expected. */\r
183                 if( uxQueueMessagesWaiting( xQueue ) != 0 )\r
184                 {\r
185                         xErrorDetected = pdTRUE;\r
186                 }\r
187 \r
188                 xQueueSendToBack( xQueue, ( void * ) &ulLoopCounter, genqNO_BLOCK );\r
189 \r
190                 if( uxQueueMessagesWaiting( xQueue ) != 1 )\r
191                 {\r
192                         xErrorDetected = pdTRUE;\r
193                 }\r
194 \r
195                 if( xQueueReceive( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != pdPASS )\r
196                 {\r
197                         xErrorDetected = pdTRUE;\r
198                 }\r
199 \r
200                 if( uxQueueMessagesWaiting( xQueue ) != 0 )\r
201                 {\r
202                         xErrorDetected = pdTRUE;\r
203                 }\r
204 \r
205                 /* The data we sent to the queue should equal the data we just received\r
206                 from the queue. */\r
207                 if( ulLoopCounter != ulData )\r
208                 {\r
209                         xErrorDetected = pdTRUE;\r
210                 }\r
211 \r
212                 #if configUSE_PREEMPTION == 0\r
213                         taskYIELD();\r
214                 #endif\r
215 \r
216 \r
217 \r
218                 /* Place 2, 3, 4 into the queue, adding items to the back of the queue. */\r
219                 for( ulData = 2; ulData < 5; ulData++ )\r
220                 {\r
221                         xQueueSendToBack( xQueue, ( void * ) &ulData, genqNO_BLOCK );\r
222                 }\r
223 \r
224                 /* Now the order in the queue should be 2, 3, 4, with 2 being the first\r
225                 thing to be read out.  Now add 1 then 0 to the front of the queue. */\r
226                 if( uxQueueMessagesWaiting( xQueue ) != 3 )\r
227                 {\r
228                         xErrorDetected = pdTRUE;\r
229                 }\r
230                 ulData = 1;\r
231                 xQueueSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK );\r
232                 ulData = 0;\r
233                 xQueueSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK );\r
234 \r
235                 /* Now the queue should be full, and when we read the data out we\r
236                 should receive 0, 1, 2, 3, 4. */\r
237                 if( uxQueueMessagesWaiting( xQueue ) != 5 )\r
238                 {\r
239                         xErrorDetected = pdTRUE;\r
240                 }\r
241 \r
242                 if( xQueueSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != errQUEUE_FULL )\r
243                 {\r
244                         xErrorDetected = pdTRUE;\r
245                 }\r
246 \r
247                 if( xQueueSendToBack( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != errQUEUE_FULL )\r
248                 {\r
249                         xErrorDetected = pdTRUE;\r
250                 }\r
251 \r
252                 #if configUSE_PREEMPTION == 0\r
253                         taskYIELD();\r
254                 #endif\r
255 \r
256                 /* Check the data we read out is in the expected order. */\r
257                 for( ulData = 0; ulData < genqQUEUE_LENGTH; ulData++ )\r
258                 {\r
259                         /* Try peeking the data first. */\r
260                         if( xQueuePeek( xQueue, &ulData2, genqNO_BLOCK ) != pdPASS )\r
261                         {\r
262                                 xErrorDetected = pdTRUE;\r
263                         }\r
264 \r
265                         if( ulData != ulData2 )\r
266                         {\r
267                                 xErrorDetected = pdTRUE;\r
268                         }\r
269                         \r
270 \r
271                         /* Now try receiving the data for real.  The value should be the\r
272                         same.  Clobber the value first so we know we really received it. */\r
273                         ulData2 = ~ulData2;\r
274                         if( xQueueReceive( xQueue, &ulData2, genqNO_BLOCK ) != pdPASS )\r
275                         {\r
276                                 xErrorDetected = pdTRUE;\r
277                         }\r
278 \r
279                         if( ulData != ulData2 )\r
280                         {\r
281                                 xErrorDetected = pdTRUE;\r
282                         }\r
283                 }\r
284 \r
285                 /* The queue should now be empty again. */\r
286                 if( uxQueueMessagesWaiting( xQueue ) != 0 )\r
287                 {\r
288                         xErrorDetected = pdTRUE;\r
289                 }\r
290 \r
291                 #if configUSE_PREEMPTION == 0\r
292                         taskYIELD();\r
293                 #endif\r
294 \r
295 \r
296                 /* Our queue is empty once more, add 10, 11 to the back. */\r
297                 ulData = 10;\r
298                 if( xQueueSend( xQueue, &ulData, genqNO_BLOCK ) != pdPASS )\r
299                 {\r
300                         xErrorDetected = pdTRUE;\r
301                 }\r
302                 ulData = 11;\r
303                 if( xQueueSend( xQueue, &ulData, genqNO_BLOCK ) != pdPASS )\r
304                 {\r
305                         xErrorDetected = pdTRUE;\r
306                 }\r
307 \r
308                 if( uxQueueMessagesWaiting( xQueue ) != 2 )\r
309                 {\r
310                         xErrorDetected = pdTRUE;\r
311                 }\r
312 \r
313                 /* Now we should have 10, 11 in the queue.  Add 7, 8, 9 to the\r
314                 front. */\r
315                 for( ulData = 9; ulData >= 7; ulData-- )\r
316                 {\r
317                         if( xQueueSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != pdPASS )\r
318                         {\r
319                                 xErrorDetected = pdTRUE;\r
320                         }\r
321                 }\r
322 \r
323                 /* Now check that the queue is full, and that receiving data provides\r
324                 the expected sequence of 7, 8, 9, 10, 11. */\r
325                 if( uxQueueMessagesWaiting( xQueue ) != 5 )\r
326                 {\r
327                         xErrorDetected = pdTRUE;\r
328                 }\r
329 \r
330                 if( xQueueSendToFront( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != errQUEUE_FULL )\r
331                 {\r
332                         xErrorDetected = pdTRUE;\r
333                 }\r
334 \r
335                 if( xQueueSendToBack( xQueue, ( void * ) &ulData, genqNO_BLOCK ) != errQUEUE_FULL )\r
336                 {\r
337                         xErrorDetected = pdTRUE;\r
338                 }\r
339 \r
340                 #if configUSE_PREEMPTION == 0\r
341                         taskYIELD();\r
342                 #endif\r
343 \r
344                 /* Check the data we read out is in the expected order. */\r
345                 for( ulData = 7; ulData < ( 7 + genqQUEUE_LENGTH ); ulData++ )\r
346                 {\r
347                         if( xQueueReceive( xQueue, &ulData2, genqNO_BLOCK ) != pdPASS )\r
348                         {\r
349                                 xErrorDetected = pdTRUE;\r
350                         }\r
351 \r
352                         if( ulData != ulData2 )\r
353                         {\r
354                                 xErrorDetected = pdTRUE;\r
355                         }\r
356                 }\r
357 \r
358                 if( uxQueueMessagesWaiting( xQueue ) != 0 )\r
359                 {\r
360                         xErrorDetected = pdTRUE;\r
361                 }\r
362 \r
363                 ulLoopCounter++;\r
364         }\r
365 }\r
366 /*-----------------------------------------------------------*/\r
367 \r
368 static void prvLowPriorityMutexTask( void *pvParameters )\r
369 {\r
370 xSemaphoreHandle xMutex = ( xSemaphoreHandle ) pvParameters;\r
371 \r
372         #ifdef USE_STDIO\r
373         void vPrintDisplayMessage( const portCHAR * const * ppcMessageToSend );\r
374         \r
375                 const portCHAR * const pcTaskStartMsg = "Mutex with priority inheritance test started.\r\n";\r
376 \r
377                 /* Queue a message for printing to say the task has started. */\r
378                 vPrintDisplayMessage( &pcTaskStartMsg );\r
379         #endif\r
380 \r
381         for( ;; )\r
382         {\r
383                 /* Take the mutex.  It should be available now. */\r
384                 if( xSemaphoreTake( xMutex, genqNO_BLOCK ) != pdPASS )\r
385                 {\r
386                         xErrorDetected = pdTRUE;\r
387                 }\r
388 \r
389                 /* Set our guarded variable to a known start value. */\r
390                 ulGuardedVariable = 0;\r
391 \r
392                 /* Our priority should be as per that assigned when the task was\r
393                 created. */\r
394                 if( uxTaskPriorityGet( NULL ) != genqMUTEX_LOW_PRIORITY )\r
395                 {\r
396                         xErrorDetected = pdTRUE;\r
397                 }\r
398 \r
399                 /* Now unsuspend the high priority task.  This will attempt to take the\r
400                 mutex, and block when it finds it cannot obtain it. */\r
401                 vTaskResume( xHighPriorityMutexTask );\r
402 \r
403                 /* We should now have inherited the prioritoy of the high priority task,\r
404                 as by now it will have attempted to get the mutex. */\r
405                 if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
406                 {\r
407                         xErrorDetected = pdTRUE;\r
408                 }\r
409 \r
410                 /* We can attempt to set our priority to the test priority - between the\r
411                 idle priority and the medium/high test priorities, but our actual\r
412                 prioroity should remain at the high priority. */\r
413                 vTaskPrioritySet( NULL, genqMUTEX_TEST_PRIORITY );\r
414                 if( uxTaskPriorityGet( NULL ) != genqMUTEX_HIGH_PRIORITY )\r
415                 {\r
416                         xErrorDetected = pdTRUE;\r
417                 }\r
418 \r
419                 /* Now unsuspend the medium priority task.  This should not run as our\r
420                 inherited priority is above that of the medium priority task. */\r
421                 vTaskResume( xMediumPriorityMutexTask );\r
422 \r
423                 /* If the did run then it will have incremented our guarded variable. */\r
424                 if( ulGuardedVariable != 0 )\r
425                 {\r
426                         xErrorDetected = pdTRUE;\r
427                 }\r
428 \r
429                 /* When we give back the semaphore our priority should be disinherited\r
430                 back to the priority to which we attempted to set ourselves.  This means\r
431                 that when the high priority task next blocks, the medium priority task\r
432                 should execute and increment the guarded variable.   When we next run\r
433                 both the high and medium priority tasks will have been suspended again. */\r
434                 if( xSemaphoreGive( xMutex ) != pdPASS )\r
435                 {\r
436                         xErrorDetected = pdTRUE;\r
437                 }\r
438 \r
439                 /* Check that the guarded variable did indeed increment... */\r
440                 if( ulGuardedVariable != 1 )\r
441                 {\r
442                         xErrorDetected = pdTRUE;\r
443                 }\r
444 \r
445                 /* ... and that our priority has been disinherited to\r
446                 genqMUTEX_TEST_PRIORITY. */\r
447                 if( uxTaskPriorityGet( NULL ) != genqMUTEX_TEST_PRIORITY )\r
448                 {\r
449                         xErrorDetected = pdTRUE;\r
450                 }\r
451 \r
452                 /* Set our priority back to our original priority ready for the next\r
453                 loop around this test. */\r
454                 vTaskPrioritySet( NULL, genqMUTEX_LOW_PRIORITY );\r
455 \r
456                 /* Just to show we are still running. */\r
457                 ulLoopCounter2++;\r
458 \r
459                 #if configUSE_PREEMPTION == 0\r
460                         taskYIELD();\r
461                 #endif          \r
462         }\r
463 }\r
464 /*-----------------------------------------------------------*/\r
465 \r
466 static void prvMediumPriorityMutexTask( void *pvParameters )\r
467 {\r
468         for( ;; )\r
469         {\r
470                 /* The medium priority task starts by suspending itself.  The low\r
471                 priority task will unsuspend this task when required. */\r
472                 vTaskSuspend( NULL );\r
473 \r
474                 /* When this task unsuspends all it does is increment the guarded\r
475                 variable, this is so the low priority task knows that it has\r
476                 executed. */\r
477                 ulGuardedVariable++;\r
478         }\r
479 }\r
480 /*-----------------------------------------------------------*/\r
481 \r
482 static void prvHighPriorityMutexTask( void *pvParameters )\r
483 {\r
484 xSemaphoreHandle xMutex = ( xSemaphoreHandle ) pvParameters;\r
485 \r
486         for( ;; )\r
487         {\r
488                 /* The high priority task starts by suspending itself.  The low\r
489                 priority task will unsuspend this task when required. */\r
490                 vTaskSuspend( NULL );\r
491 \r
492                 /* When this task unsuspends all it does is attempt to obtain\r
493                 the mutex.  It should find the mutex is not available so a\r
494                 block time is specified. */\r
495                 if( xSemaphoreTake( xMutex, portMAX_DELAY ) != pdPASS )\r
496                 {\r
497                         xErrorDetected = pdTRUE;\r
498                 }\r
499 \r
500                 /* When we eventually obtain the mutex we just give it back then\r
501                 return to suspend ready for the next test. */\r
502                 if( xSemaphoreGive( xMutex ) != pdPASS )\r
503                 {\r
504                         xErrorDetected = pdTRUE;\r
505                 }               \r
506         }\r
507 }\r
508 /*-----------------------------------------------------------*/\r
509 \r
510 /* This is called to check that all the created tasks are still running. */\r
511 portBASE_TYPE xAreGenericQueueTasksStillRunning( void )\r
512 {\r
513 static unsigned portLONG ulLastLoopCounter = 0, ulLastLoopCounter2 = 0;\r
514 \r
515         /* If the demo task is still running then we expect the loopcounters to\r
516         have incremented since this function was last called. */\r
517         if( ulLastLoopCounter == ulLoopCounter )\r
518         {\r
519                 xErrorDetected = pdTRUE;\r
520         }\r
521 \r
522         if( ulLastLoopCounter2 == ulLoopCounter2 )\r
523         {\r
524                 xErrorDetected = pdTRUE;\r
525         }\r
526 \r
527         ulLastLoopCounter = ulLoopCounter;\r
528         ulLastLoopCounter2 = ulLoopCounter2;    \r
529 \r
530         /* Errors detected in the task itself will have latched xErrorDetected\r
531         to true. */\r
532 \r
533         return !xErrorDetected;\r
534 }\r
535 \r
536 \r