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