]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Common/Minimal/AltBlock.c
2f1ce52c23ef4071976ec163c1dfa8e15fd3246b
[freertos] / FreeRTOS / Demo / Common / Minimal / AltBlock.c
1 /*\r
2     FreeRTOS V8.0.0:rc1 - Copyright (C) 2014 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     ***************************************************************************\r
8      *                                                                       *\r
9      *    FreeRTOS provides completely free yet professionally developed,    *\r
10      *    robust, strictly quality controlled, supported, and cross          *\r
11      *    platform software that has become a de facto standard.             *\r
12      *                                                                       *\r
13      *    Help yourself get started quickly and support the FreeRTOS         *\r
14      *    project by purchasing a FreeRTOS tutorial book, reference          *\r
15      *    manual, or both from: http://www.FreeRTOS.org/Documentation        *\r
16      *                                                                       *\r
17      *    Thank you!                                                         *\r
18      *                                                                       *\r
19     ***************************************************************************\r
20 \r
21     This file is part of the FreeRTOS distribution.\r
22 \r
23     FreeRTOS is free software; you can redistribute it and/or modify it under\r
24     the terms of the GNU General Public License (version 2) as published by the\r
25     Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.\r
26 \r
27     >>! NOTE: The modification to the GPL is included to allow you to distribute\r
28     >>! a combined work that includes FreeRTOS without being obliged to provide\r
29     >>! the source code for proprietary components outside of the FreeRTOS\r
30     >>! kernel.\r
31 \r
32     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY\r
33     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\r
34     FOR A PARTICULAR PURPOSE.  Full license text is available from the following\r
35     link: http://www.freertos.org/a00114.html\r
36 \r
37     1 tab == 4 spaces!\r
38 \r
39     ***************************************************************************\r
40      *                                                                       *\r
41      *    Having a problem?  Start by reading the FAQ "My application does   *\r
42      *    not run, what could be wrong?"                                     *\r
43      *                                                                       *\r
44      *    http://www.FreeRTOS.org/FAQHelp.html                               *\r
45      *                                                                       *\r
46     ***************************************************************************\r
47 \r
48     http://www.FreeRTOS.org - Documentation, books, training, latest versions,\r
49     license and Real Time Engineers Ltd. contact details.\r
50 \r
51     http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,\r
52     including FreeRTOS+Trace - an indispensable productivity tool, a DOS\r
53     compatible FAT file system, and our tiny thread aware UDP/IP stack.\r
54 \r
55     http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High\r
56     Integrity Systems to sell under the OpenRTOS brand.  Low cost OpenRTOS\r
57     licenses offer ticketed support, indemnification and middleware.\r
58 \r
59     http://www.SafeRTOS.com - High Integrity Systems also provide a safety\r
60     engineered and independently SIL3 certified version for use in safety and\r
61     mission critical applications that require provable dependability.\r
62 \r
63     1 tab == 4 spaces!\r
64 */\r
65 \r
66 /*\r
67  * This is a version of BlockTim.c that uses the light weight API.\r
68  *\r
69  * This file contains some test scenarios that ensure tasks do not exit queue\r
70  * send or receive functions prematurely.  A description of the tests is\r
71  * included within the code.\r
72  */\r
73 \r
74 /* Kernel includes. */\r
75 #include "FreeRTOS.h"\r
76 #include "task.h"\r
77 #include "queue.h"\r
78 \r
79 /* Demo includes. */\r
80 #include "AltBlock.h"\r
81 \r
82 /* Task priorities. */\r
83 #define bktPRIMARY_PRIORITY                     ( 3 )\r
84 #define bktSECONDARY_PRIORITY           ( 2 )\r
85 \r
86 /* Task behaviour. */\r
87 #define bktQUEUE_LENGTH                         ( 5 )\r
88 #define bktSHORT_WAIT                           ( ( ( TickType_t ) 20 ) / portTICK_PERIOD_MS )\r
89 #define bktPRIMARY_BLOCK_TIME           ( 10 )\r
90 #define bktALLOWABLE_MARGIN                     ( 12 )\r
91 #define bktTIME_TO_BLOCK                        ( 175 )\r
92 #define bktDONT_BLOCK                           ( ( TickType_t ) 0 )\r
93 #define bktRUN_INDICATOR                        ( ( unsigned portBASE_TYPE ) 0x55 )\r
94 \r
95 /* The queue on which the tasks block. */\r
96 static QueueHandle_t xTestQueue;\r
97 \r
98 /* Handle to the secondary task is required by the primary task for calls\r
99 to vTaskSuspend/Resume(). */\r
100 static TaskHandle_t xSecondary;\r
101 \r
102 /* Used to ensure that tasks are still executing without error. */\r
103 static portBASE_TYPE xPrimaryCycles = 0, xSecondaryCycles = 0;\r
104 static portBASE_TYPE xErrorOccurred = pdFALSE;\r
105 \r
106 /* Provides a simple mechanism for the primary task to know when the\r
107 secondary task has executed. */\r
108 static volatile unsigned portBASE_TYPE xRunIndicator;\r
109 \r
110 /* The two test tasks.  Their behaviour is commented within the files. */\r
111 static void vPrimaryBlockTimeTestTask( void *pvParameters );\r
112 static void vSecondaryBlockTimeTestTask( void *pvParameters );\r
113 \r
114 /*-----------------------------------------------------------*/\r
115 \r
116 void vCreateAltBlockTimeTasks( void )\r
117 {\r
118         /* Create the queue on which the two tasks block. */\r
119     xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( portBASE_TYPE ) );\r
120 \r
121         /* vQueueAddToRegistry() adds the queue to the queue registry, if one is\r
122         in use.  The queue registry is provided as a means for kernel aware \r
123         debuggers to locate queues and has no purpose if a kernel aware debugger\r
124         is not being used.  The call to vQueueAddToRegistry() will be removed\r
125         by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is \r
126         defined to be less than 1. */\r
127         vQueueAddToRegistry( xTestQueue, "AltBlockQueue" );\r
128 \r
129 \r
130         /* Create the two test tasks. */\r
131         xTaskCreate( vPrimaryBlockTimeTestTask, "FBTest1", configMINIMAL_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL );\r
132         xTaskCreate( vSecondaryBlockTimeTestTask, "FBTest2", configMINIMAL_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary );\r
133 }\r
134 /*-----------------------------------------------------------*/\r
135 \r
136 static void vPrimaryBlockTimeTestTask( void *pvParameters )\r
137 {\r
138 portBASE_TYPE xItem, xData;\r
139 TickType_t xTimeWhenBlocking;\r
140 TickType_t xTimeToBlock, xBlockedTime;\r
141 \r
142         #ifdef USE_STDIO\r
143         void vPrintDisplayMessage( const char * const * ppcMessageToSend );\r
144         \r
145                 const char * const pcTaskStartMsg = "Alt primary block time test started.\r\n";\r
146 \r
147                 /* Queue a message for printing to say the task has started. */\r
148                 vPrintDisplayMessage( &pcTaskStartMsg );\r
149         #endif\r
150 \r
151         ( void ) pvParameters;\r
152 \r
153         for( ;; )\r
154         {\r
155                 /*********************************************************************\r
156         Test 1\r
157 \r
158         Simple block time wakeup test on queue receives. */\r
159                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
160                 {\r
161                         /* The queue is empty. Attempt to read from the queue using a block\r
162                         time.  When we wake, ensure the delta in time is as expected. */\r
163                         xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;\r
164 \r
165                         /* A critical section is used to minimise the jitter in the time\r
166                         measurements. */\r
167                         portENTER_CRITICAL();\r
168                         {\r
169                                 xTimeWhenBlocking = xTaskGetTickCount();\r
170                                 \r
171                                 /* We should unblock after xTimeToBlock having not received\r
172                                 anything on the queue. */\r
173                                 if( xQueueAltReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )\r
174                                 {\r
175                                         xErrorOccurred = pdTRUE;\r
176                                 }\r
177 \r
178                                 /* How long were we blocked for? */\r
179                                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
180                         }\r
181                         portEXIT_CRITICAL();\r
182 \r
183                         if( xBlockedTime < xTimeToBlock )\r
184                         {\r
185                                 /* Should not have blocked for less than we requested. */\r
186                                 xErrorOccurred = pdTRUE;\r
187                         }\r
188 \r
189                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
190                         {\r
191                                 /* Should not have blocked for longer than we requested,\r
192                                 although we would not necessarily run as soon as we were\r
193                                 unblocked so a margin is allowed. */\r
194                                 xErrorOccurred = pdTRUE;\r
195                         }\r
196                 }\r
197 \r
198 \r
199                 #if configUSE_PREEMPTION == 0\r
200                         taskYIELD();\r
201                 #endif\r
202 \r
203 \r
204                 /*********************************************************************\r
205         Test 2\r
206 \r
207         Simple block time wakeup test on queue sends.\r
208 \r
209                 First fill the queue.  It should be empty so all sends should pass. */\r
210                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
211                 {\r
212                         if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
213                         {\r
214                                 xErrorOccurred = pdTRUE;\r
215                         }\r
216                 }\r
217 \r
218                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
219                 {\r
220                         /* The queue is full. Attempt to write to the queue using a block\r
221                         time.  When we wake, ensure the delta in time is as expected. */\r
222                         xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;\r
223 \r
224                         portENTER_CRITICAL();\r
225                         {\r
226                                 xTimeWhenBlocking = xTaskGetTickCount();\r
227                                 \r
228                                 /* We should unblock after xTimeToBlock having not received\r
229                                 anything on the queue. */\r
230                                 if( xQueueAltSendToBack( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )\r
231                                 {\r
232                                         xErrorOccurred = pdTRUE;\r
233                                 }\r
234 \r
235                                 /* How long were we blocked for? */\r
236                                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
237                         }\r
238                         portEXIT_CRITICAL();\r
239 \r
240                         if( xBlockedTime < xTimeToBlock )\r
241                         {\r
242                                 /* Should not have blocked for less than we requested. */\r
243                                 xErrorOccurred = pdTRUE;\r
244                         }\r
245 \r
246                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
247                         {\r
248                                 /* Should not have blocked for longer than we requested,\r
249                                 although we would not necessarily run as soon as we were\r
250                                 unblocked so a margin is allowed. */\r
251                                 xErrorOccurred = pdTRUE;\r
252                         }\r
253                 }\r
254 \r
255                 #if configUSE_PREEMPTION == 0\r
256                         taskYIELD();\r
257                 #endif\r
258 \r
259                 \r
260                 /*********************************************************************\r
261         Test 3\r
262 \r
263                 Wake the other task, it will block attempting to post to the queue.\r
264                 When we read from the queue the other task will wake, but before it\r
265                 can run we will post to the queue again.  When the other task runs it\r
266                 will find the queue still full, even though it was woken.  It should\r
267                 recognise that its block time has not expired and return to block for\r
268                 the remains of its block time.\r
269 \r
270                 Wake the other task so it blocks attempting to post to the already\r
271                 full queue. */\r
272                 xRunIndicator = 0;\r
273                 vTaskResume( xSecondary );\r
274 \r
275                 /* We need to wait a little to ensure the other task executes. */\r
276                 while( xRunIndicator != bktRUN_INDICATOR )\r
277                 {\r
278                         /* The other task has not yet executed. */\r
279                         vTaskDelay( bktSHORT_WAIT );\r
280                 }\r
281                 /* Make sure the other task is blocked on the queue. */\r
282                 vTaskDelay( bktSHORT_WAIT );\r
283                 xRunIndicator = 0;\r
284 \r
285                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
286                 {\r
287                         /* Now when we make space on the queue the other task should wake\r
288                         but not execute as this task has higher priority. */                            \r
289                         if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
290                         {\r
291                                 xErrorOccurred = pdTRUE;\r
292                         }\r
293 \r
294                         /* Now fill the queue again before the other task gets a chance to\r
295                         execute.  If the other task had executed we would find the queue\r
296                         full ourselves, and the other task have set xRunIndicator. */\r
297                         if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
298                         {\r
299                                 xErrorOccurred = pdTRUE;\r
300                         }\r
301 \r
302                         if( xRunIndicator == bktRUN_INDICATOR )\r
303                         {\r
304                                 /* The other task should not have executed. */\r
305                                 xErrorOccurred = pdTRUE;\r
306                         }\r
307 \r
308                         /* Raise the priority of the other task so it executes and blocks\r
309                         on the queue again. */\r
310                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
311 \r
312                         /* The other task should now have re-blocked without exiting the\r
313                         queue function. */\r
314                         if( xRunIndicator == bktRUN_INDICATOR )\r
315                         {\r
316                                 /* The other task should not have executed outside of the\r
317                                 queue function. */\r
318                                 xErrorOccurred = pdTRUE;\r
319                         }\r
320 \r
321                         /* Set the priority back down. */\r
322                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );                  \r
323                 }\r
324 \r
325                 /* Let the other task timeout.  When it unblockes it will check that it\r
326                 unblocked at the correct time, then suspend itself. */\r
327                 while( xRunIndicator != bktRUN_INDICATOR )\r
328                 {\r
329                         vTaskDelay( bktSHORT_WAIT );\r
330                 }\r
331                 vTaskDelay( bktSHORT_WAIT );\r
332                 xRunIndicator = 0;\r
333 \r
334                 #if configUSE_PREEMPTION == 0\r
335                         taskYIELD();\r
336                 #endif\r
337 \r
338                 /*********************************************************************\r
339         Test 4\r
340 \r
341                 As per test 3 - but with the send and receive the other way around.\r
342                 The other task blocks attempting to read from the queue.\r
343 \r
344                 Empty the queue.  We should find that it is full. */\r
345                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
346                 {\r
347                         if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
348                         {\r
349                                 xErrorOccurred = pdTRUE;\r
350                         }\r
351                 }\r
352                 \r
353                 /* Wake the other task so it blocks attempting to read from  the\r
354                 already empty queue. */\r
355                 vTaskResume( xSecondary );\r
356 \r
357                 /* We need to wait a little to ensure the other task executes. */\r
358                 while( xRunIndicator != bktRUN_INDICATOR )\r
359                 {\r
360                         vTaskDelay( bktSHORT_WAIT );\r
361                 }\r
362                 vTaskDelay( bktSHORT_WAIT );\r
363                 xRunIndicator = 0;\r
364 \r
365                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
366                 {\r
367                         /* Now when we place an item on the queue the other task should\r
368                         wake but not execute as this task has higher priority. */                               \r
369                         if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
370                         {\r
371                                 xErrorOccurred = pdTRUE;\r
372                         }\r
373 \r
374                         /* Now empty the queue again before the other task gets a chance to\r
375                         execute.  If the other task had executed we would find the queue\r
376                         empty ourselves, and the other task would be suspended. */\r
377                         if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
378                         {\r
379                                 xErrorOccurred = pdTRUE;\r
380                         }\r
381 \r
382                         if( xRunIndicator == bktRUN_INDICATOR )\r
383                         {\r
384                                 /* The other task should not have executed. */\r
385                                 xErrorOccurred = pdTRUE;\r
386                         }\r
387 \r
388                         /* Raise the priority of the other task so it executes and blocks\r
389                         on the queue again. */\r
390                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
391 \r
392                         /* The other task should now have re-blocked without exiting the\r
393                         queue function. */\r
394                         if( xRunIndicator == bktRUN_INDICATOR )\r
395                         {\r
396                                 /* The other task should not have executed outside of the\r
397                                 queue function. */\r
398                                 xErrorOccurred = pdTRUE;\r
399                         }\r
400                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );                  \r
401                 }\r
402 \r
403                 /* Let the other task timeout.  When it unblockes it will check that it\r
404                 unblocked at the correct time, then suspend itself. */\r
405                 while( xRunIndicator != bktRUN_INDICATOR )\r
406                 {\r
407                         vTaskDelay( bktSHORT_WAIT );\r
408                 }\r
409                 vTaskDelay( bktSHORT_WAIT );\r
410 \r
411                 xPrimaryCycles++;\r
412         }\r
413 }\r
414 /*-----------------------------------------------------------*/\r
415 \r
416 static void vSecondaryBlockTimeTestTask( void *pvParameters )\r
417 {\r
418 TickType_t xTimeWhenBlocking, xBlockedTime;\r
419 portBASE_TYPE xData;\r
420 \r
421         #ifdef USE_STDIO\r
422         void vPrintDisplayMessage( const char * const * ppcMessageToSend );\r
423         \r
424                 const char * const pcTaskStartMsg = "Alt secondary block time test started.\r\n";\r
425 \r
426                 /* Queue a message for printing to say the task has started. */\r
427                 vPrintDisplayMessage( &pcTaskStartMsg );\r
428         #endif\r
429 \r
430         ( void ) pvParameters;\r
431 \r
432         for( ;; )\r
433         {\r
434                 /*********************************************************************\r
435         Test 1 and 2\r
436 \r
437                 This task does does not participate in these tests. */\r
438                 vTaskSuspend( NULL );\r
439 \r
440                 /*********************************************************************\r
441         Test 3\r
442 \r
443                 The first thing we do is attempt to read from the queue.  It should be\r
444                 full so we block.  Note the time before we block so we can check the\r
445                 wake time is as per that expected. */\r
446                 portENTER_CRITICAL();\r
447                 {\r
448                         xTimeWhenBlocking = xTaskGetTickCount();\r
449                         \r
450                         /* We should unblock after bktTIME_TO_BLOCK having not received\r
451                         anything on the queue. */\r
452                         xData = 0;\r
453                         xRunIndicator = bktRUN_INDICATOR;\r
454                         if( xQueueAltSendToBack( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )\r
455                         {\r
456                                 xErrorOccurred = pdTRUE;\r
457                         }\r
458 \r
459                         /* How long were we inside the send function? */\r
460                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
461                 }\r
462                 portEXIT_CRITICAL();\r
463 \r
464                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
465                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
466                 {\r
467                         xErrorOccurred = pdTRUE;\r
468                 }\r
469 \r
470                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN\r
471                 either.  A margin is permitted as we would not necessarily run as\r
472                 soon as we unblocked. */\r
473                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
474                 {\r
475                         xErrorOccurred = pdTRUE;\r
476                 }\r
477 \r
478                 /* Suspend ready for test 3. */\r
479                 xRunIndicator = bktRUN_INDICATOR;\r
480                 vTaskSuspend( NULL );\r
481 \r
482                 /*********************************************************************\r
483         Test 4\r
484 \r
485                 As per test three, but with the send and receive reversed. */\r
486                 portENTER_CRITICAL();\r
487                 {\r
488                         xTimeWhenBlocking = xTaskGetTickCount();\r
489                         \r
490                         /* We should unblock after bktTIME_TO_BLOCK having not received\r
491                         anything on the queue. */\r
492                         xRunIndicator = bktRUN_INDICATOR;\r
493                         if( xQueueAltReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )\r
494                         {\r
495                                 xErrorOccurred = pdTRUE;\r
496                         }\r
497 \r
498                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
499                 }\r
500                 portEXIT_CRITICAL();\r
501 \r
502                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
503                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
504                 {\r
505                         xErrorOccurred = pdTRUE;\r
506                 }\r
507 \r
508                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN\r
509                 either.  A margin is permitted as we would not necessarily run as soon\r
510                 as we unblocked. */\r
511                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
512                 {\r
513                         xErrorOccurred = pdTRUE;\r
514                 }\r
515 \r
516                 xRunIndicator = bktRUN_INDICATOR;\r
517 \r
518                 xSecondaryCycles++;\r
519         }\r
520 }\r
521 /*-----------------------------------------------------------*/\r
522 \r
523 portBASE_TYPE xAreAltBlockTimeTestTasksStillRunning( void )\r
524 {\r
525 static portBASE_TYPE xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;\r
526 portBASE_TYPE xReturn = pdPASS;\r
527 \r
528         /* Have both tasks performed at least one cycle since this function was\r
529         last called? */\r
530         if( xPrimaryCycles == xLastPrimaryCycleCount )\r
531         {\r
532                 xReturn = pdFAIL;\r
533         }\r
534 \r
535         if( xSecondaryCycles == xLastSecondaryCycleCount )\r
536         {\r
537                 xReturn = pdFAIL;\r
538         }\r
539 \r
540         if( xErrorOccurred == pdTRUE )\r
541         {\r
542                 xReturn = pdFAIL;\r
543         }\r
544 \r
545         xLastSecondaryCycleCount = xSecondaryCycles;\r
546         xLastPrimaryCycleCount = xPrimaryCycles;\r
547 \r
548         return xReturn;\r
549 }\r