]> git.sur5r.net Git - freertos/blob - Demo/Common/Minimal/blocktim.c
0ced88592c06535590a60750bbcaefb4dd008bfe
[freertos] / Demo / Common / Minimal / blocktim.c
1 /*\r
2     FreeRTOS V7.0.0 - Copyright (C) 2011 Real Time Engineers Ltd.\r
3         \r
4 \r
5     ***************************************************************************\r
6      *                                                                       *\r
7      *    FreeRTOS tutorial books are available in pdf and paperback.        *\r
8      *    Complete, revised, and edited pdf reference manuals are also       *\r
9      *    available.                                                         *\r
10      *                                                                       *\r
11      *    Purchasing FreeRTOS documentation will not only help you, by       *\r
12      *    ensuring you get running as quickly as possible and with an        *\r
13      *    in-depth knowledge of how to use FreeRTOS, it will also help       *\r
14      *    the FreeRTOS project to continue with its mission of providing     *\r
15      *    professional grade, cross platform, de facto standard solutions    *\r
16      *    for microcontrollers - completely free of charge!                  *\r
17      *                                                                       *\r
18      *    >>> See http://www.FreeRTOS.org/Documentation for details. <<<     *\r
19      *                                                                       *\r
20      *    Thank you for using FreeRTOS, and thank you for your support!      *\r
21      *                                                                       *\r
22     ***************************************************************************\r
23 \r
24 \r
25     This file is part of the FreeRTOS distribution.\r
26 \r
27     FreeRTOS is free software; you can redistribute it and/or modify it under\r
28     the terms of the GNU General Public License (version 2) as published by the\r
29     Free Software Foundation AND MODIFIED BY the FreeRTOS exception.\r
30     >>>NOTE<<< The modification to the GPL is included to allow you to\r
31     distribute a combined work that includes FreeRTOS without being obliged to\r
32     provide the source code for proprietary components outside of the FreeRTOS\r
33     kernel.  FreeRTOS is distributed in the hope that it will be useful, but\r
34     WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY\r
35     or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for\r
36     more details. You should have received a copy of the GNU General Public\r
37     License and the FreeRTOS license exception along with FreeRTOS; if not it\r
38     can be viewed here: http://www.freertos.org/a00114.html and also obtained\r
39     by writing to Richard Barry, contact details for whom are available on the\r
40     FreeRTOS WEB site.\r
41 \r
42     1 tab == 4 spaces!\r
43 \r
44     http://www.FreeRTOS.org - Documentation, latest information, license and\r
45     contact details.\r
46 \r
47     http://www.SafeRTOS.com - A version that is certified for use in safety\r
48     critical systems.\r
49 \r
50     http://www.OpenRTOS.com - Commercial support, development, porting,\r
51     licensing and training services.\r
52 */\r
53 \r
54 /*\r
55  * This file contains some test scenarios that ensure tasks do not exit queue\r
56  * send or receive functions prematurely.  A description of the tests is\r
57  * included within the code.\r
58  */\r
59 \r
60 /* Kernel includes. */\r
61 #include "FreeRTOS.h"\r
62 #include "task.h"\r
63 #include "queue.h"\r
64 \r
65 /* Demo includes. */\r
66 #include "blocktim.h"\r
67 \r
68 /* Task priorities.  Allow these to be overridden. */\r
69 #ifndef bktPRIMARY_PRIORITY\r
70         #define bktPRIMARY_PRIORITY             ( configMAX_PRIORITIES - 3 )\r
71 #endif\r
72 \r
73 #ifndef bktSECONDARY_PRIORITY\r
74         #define bktSECONDARY_PRIORITY   ( configMAX_PRIORITIES - 4 )\r
75 #endif\r
76 \r
77 /* Task behaviour. */\r
78 #define bktQUEUE_LENGTH                         ( 5 )\r
79 #define bktSHORT_WAIT                           ( ( ( portTickType ) 20 ) / portTICK_RATE_MS )\r
80 #define bktPRIMARY_BLOCK_TIME           ( 10 )\r
81 #define bktALLOWABLE_MARGIN                     ( 15 )\r
82 #define bktTIME_TO_BLOCK                        ( 175 )\r
83 #define bktDONT_BLOCK                           ( ( portTickType ) 0 )\r
84 #define bktRUN_INDICATOR                        ( ( unsigned portBASE_TYPE ) 0x55 )\r
85 \r
86 /* The queue on which the tasks block. */\r
87 static xQueueHandle xTestQueue;\r
88 \r
89 /* Handle to the secondary task is required by the primary task for calls\r
90 to vTaskSuspend/Resume(). */\r
91 static xTaskHandle xSecondary;\r
92 \r
93 /* Used to ensure that tasks are still executing without error. */\r
94 static volatile portBASE_TYPE xPrimaryCycles = 0, xSecondaryCycles = 0;\r
95 static volatile portBASE_TYPE xErrorOccurred = pdFALSE;\r
96 \r
97 /* Provides a simple mechanism for the primary task to know when the\r
98 secondary task has executed. */\r
99 static volatile unsigned portBASE_TYPE xRunIndicator;\r
100 \r
101 /* The two test tasks.  Their behaviour is commented within the files. */\r
102 static void vPrimaryBlockTimeTestTask( void *pvParameters );\r
103 static void vSecondaryBlockTimeTestTask( void *pvParameters );\r
104 \r
105 /*-----------------------------------------------------------*/\r
106 \r
107 void vCreateBlockTimeTasks( void )\r
108 {\r
109         /* Create the queue on which the two tasks block. */\r
110     xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( portBASE_TYPE ) );\r
111 \r
112         /* vQueueAddToRegistry() adds the queue to the queue registry, if one is\r
113         in use.  The queue registry is provided as a means for kernel aware\r
114         debuggers to locate queues and has no purpose if a kernel aware debugger\r
115         is not being used.  The call to vQueueAddToRegistry() will be removed\r
116         by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is\r
117         defined to be less than 1. */\r
118         vQueueAddToRegistry( xTestQueue, ( signed char * ) "Block_Time_Queue" );\r
119 \r
120         /* Create the two test tasks. */\r
121         xTaskCreate( vPrimaryBlockTimeTestTask, ( signed char * )"BTest1", configMINIMAL_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL );\r
122         xTaskCreate( vSecondaryBlockTimeTestTask, ( signed char * )"BTest2", configMINIMAL_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary );\r
123 }\r
124 /*-----------------------------------------------------------*/\r
125 \r
126 static void vPrimaryBlockTimeTestTask( void *pvParameters )\r
127 {\r
128 portBASE_TYPE xItem, xData;\r
129 portTickType xTimeWhenBlocking;\r
130 portTickType xTimeToBlock, xBlockedTime;\r
131 \r
132         ( void ) pvParameters;\r
133 \r
134         for( ;; )\r
135         {\r
136                 /*********************************************************************\r
137         Test 1\r
138 \r
139         Simple block time wakeup test on queue receives. */\r
140                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
141                 {\r
142                         /* The queue is empty. Attempt to read from the queue using a block\r
143                         time.  When we wake, ensure the delta in time is as expected. */\r
144                         xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;\r
145 \r
146                         xTimeWhenBlocking = xTaskGetTickCount();\r
147 \r
148                         /* We should unblock after xTimeToBlock having not received\r
149                         anything on the queue. */\r
150                         if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )\r
151                         {\r
152                                 xErrorOccurred = pdTRUE;\r
153                         }\r
154 \r
155                         /* How long were we blocked for? */\r
156                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
157 \r
158                         if( xBlockedTime < xTimeToBlock )\r
159                         {\r
160                                 /* Should not have blocked for less than we requested. */\r
161                                 xErrorOccurred = pdTRUE;\r
162                         }\r
163 \r
164                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
165                         {\r
166                                 /* Should not have blocked for longer than we requested,\r
167                                 although we would not necessarily run as soon as we were\r
168                                 unblocked so a margin is allowed. */\r
169                                 xErrorOccurred = pdTRUE;\r
170                         }\r
171                 }\r
172 \r
173                 /*********************************************************************\r
174         Test 2\r
175 \r
176         Simple block time wakeup test on queue sends.\r
177 \r
178                 First fill the queue.  It should be empty so all sends should pass. */\r
179                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
180                 {\r
181                         if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
182                         {\r
183                                 xErrorOccurred = pdTRUE;\r
184                         }\r
185 \r
186                         #if configUSE_PREEMPTION == 0\r
187                                 taskYIELD();\r
188                         #endif\r
189                 }\r
190 \r
191                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
192                 {\r
193                         /* The queue is full. Attempt to write to the queue using a block\r
194                         time.  When we wake, ensure the delta in time is as expected. */\r
195                         xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;\r
196 \r
197                         xTimeWhenBlocking = xTaskGetTickCount();\r
198 \r
199                         /* We should unblock after xTimeToBlock having not received\r
200                         anything on the queue. */\r
201                         if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )\r
202                         {\r
203                                 xErrorOccurred = pdTRUE;\r
204                         }\r
205 \r
206                         /* How long were we blocked for? */\r
207                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
208 \r
209                         if( xBlockedTime < xTimeToBlock )\r
210                         {\r
211                                 /* Should not have blocked for less than we requested. */\r
212                                 xErrorOccurred = pdTRUE;\r
213                         }\r
214 \r
215                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
216                         {\r
217                                 /* Should not have blocked for longer than we requested,\r
218                                 although we would not necessarily run as soon as we were\r
219                                 unblocked so a margin is allowed. */\r
220                                 xErrorOccurred = pdTRUE;\r
221                         }\r
222                 }\r
223 \r
224                 /*********************************************************************\r
225         Test 3\r
226 \r
227                 Wake the other task, it will block attempting to post to the queue.\r
228                 When we read from the queue the other task will wake, but before it\r
229                 can run we will post to the queue again.  When the other task runs it\r
230                 will find the queue still full, even though it was woken.  It should\r
231                 recognise that its block time has not expired and return to block for\r
232                 the remains of its block time.\r
233 \r
234                 Wake the other task so it blocks attempting to post to the already\r
235                 full queue. */\r
236                 xRunIndicator = 0;\r
237                 vTaskResume( xSecondary );\r
238 \r
239                 /* We need to wait a little to ensure the other task executes. */\r
240                 while( xRunIndicator != bktRUN_INDICATOR )\r
241                 {\r
242                         /* The other task has not yet executed. */\r
243                         vTaskDelay( bktSHORT_WAIT );\r
244                 }\r
245                 /* Make sure the other task is blocked on the queue. */\r
246                 vTaskDelay( bktSHORT_WAIT );\r
247                 xRunIndicator = 0;\r
248 \r
249                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
250                 {\r
251                         /* Now when we make space on the queue the other task should wake\r
252                         but not execute as this task has higher priority. */\r
253                         if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
254                         {\r
255                                 xErrorOccurred = pdTRUE;\r
256                         }\r
257 \r
258                         /* Now fill the queue again before the other task gets a chance to\r
259                         execute.  If the other task had executed we would find the queue\r
260                         full ourselves, and the other task have set xRunIndicator. */\r
261                         if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
262                         {\r
263                                 xErrorOccurred = pdTRUE;\r
264                         }\r
265 \r
266                         if( xRunIndicator == bktRUN_INDICATOR )\r
267                         {\r
268                                 /* The other task should not have executed. */\r
269                                 xErrorOccurred = pdTRUE;\r
270                         }\r
271 \r
272                         /* Raise the priority of the other task so it executes and blocks\r
273                         on the queue again. */\r
274                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
275 \r
276                         /* The other task should now have re-blocked without exiting the\r
277                         queue function. */\r
278                         if( xRunIndicator == bktRUN_INDICATOR )\r
279                         {\r
280                                 /* The other task should not have executed outside of the\r
281                                 queue function. */\r
282                                 xErrorOccurred = pdTRUE;\r
283                         }\r
284 \r
285                         /* Set the priority back down. */\r
286                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );\r
287                 }\r
288 \r
289                 /* Let the other task timeout.  When it unblockes it will check that it\r
290                 unblocked at the correct time, then suspend itself. */\r
291                 while( xRunIndicator != bktRUN_INDICATOR )\r
292                 {\r
293                         vTaskDelay( bktSHORT_WAIT );\r
294                 }\r
295                 vTaskDelay( bktSHORT_WAIT );\r
296                 xRunIndicator = 0;\r
297 \r
298 \r
299                 /*********************************************************************\r
300         Test 4\r
301 \r
302                 As per test 3 - but with the send and receive the other way around.\r
303                 The other task blocks attempting to read from the queue.\r
304 \r
305                 Empty the queue.  We should find that it is full. */\r
306                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
307                 {\r
308                         if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
309                         {\r
310                                 xErrorOccurred = pdTRUE;\r
311                         }\r
312                 }\r
313 \r
314                 /* Wake the other task so it blocks attempting to read from  the\r
315                 already empty queue. */\r
316                 vTaskResume( xSecondary );\r
317 \r
318                 /* We need to wait a little to ensure the other task executes. */\r
319                 while( xRunIndicator != bktRUN_INDICATOR )\r
320                 {\r
321                         vTaskDelay( bktSHORT_WAIT );\r
322                 }\r
323                 vTaskDelay( bktSHORT_WAIT );\r
324                 xRunIndicator = 0;\r
325 \r
326                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
327                 {\r
328                         /* Now when we place an item on the queue the other task should\r
329                         wake but not execute as this task has higher priority. */\r
330                         if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
331                         {\r
332                                 xErrorOccurred = pdTRUE;\r
333                         }\r
334 \r
335                         /* Now empty the queue again before the other task gets a chance to\r
336                         execute.  If the other task had executed we would find the queue\r
337                         empty ourselves, and the other task would be suspended. */\r
338                         if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
339                         {\r
340                                 xErrorOccurred = pdTRUE;\r
341                         }\r
342 \r
343                         if( xRunIndicator == bktRUN_INDICATOR )\r
344                         {\r
345                                 /* The other task should not have executed. */\r
346                                 xErrorOccurred = pdTRUE;\r
347                         }\r
348 \r
349                         /* Raise the priority of the other task so it executes and blocks\r
350                         on the queue again. */\r
351                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
352 \r
353                         /* The other task should now have re-blocked without exiting the\r
354                         queue function. */\r
355                         if( xRunIndicator == bktRUN_INDICATOR )\r
356                         {\r
357                                 /* The other task should not have executed outside of the\r
358                                 queue function. */\r
359                                 xErrorOccurred = pdTRUE;\r
360                         }\r
361                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );\r
362                 }\r
363 \r
364                 /* Let the other task timeout.  When it unblockes it will check that it\r
365                 unblocked at the correct time, then suspend itself. */\r
366                 while( xRunIndicator != bktRUN_INDICATOR )\r
367                 {\r
368                         vTaskDelay( bktSHORT_WAIT );\r
369                 }\r
370                 vTaskDelay( bktSHORT_WAIT );\r
371 \r
372                 xPrimaryCycles++;\r
373         }\r
374 }\r
375 /*-----------------------------------------------------------*/\r
376 \r
377 static void vSecondaryBlockTimeTestTask( void *pvParameters )\r
378 {\r
379 portTickType xTimeWhenBlocking, xBlockedTime;\r
380 portBASE_TYPE xData;\r
381 \r
382         ( void ) pvParameters;\r
383 \r
384         for( ;; )\r
385         {\r
386                 /*********************************************************************\r
387         Test 1 and 2\r
388 \r
389                 This task does does not participate in these tests. */\r
390                 vTaskSuspend( NULL );\r
391 \r
392                 /*********************************************************************\r
393         Test 3\r
394 \r
395                 The first thing we do is attempt to read from the queue.  It should be\r
396                 full so we block.  Note the time before we block so we can check the\r
397                 wake time is as per that expected. */\r
398                 xTimeWhenBlocking = xTaskGetTickCount();\r
399 \r
400                 /* We should unblock after bktTIME_TO_BLOCK having not sent\r
401                 anything to the queue. */\r
402                 xData = 0;\r
403                 xRunIndicator = bktRUN_INDICATOR;\r
404                 if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )\r
405                 {\r
406                         xErrorOccurred = pdTRUE;\r
407                 }\r
408 \r
409                 /* How long were we inside the send function? */\r
410                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
411 \r
412                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
413                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
414                 {\r
415                         xErrorOccurred = pdTRUE;\r
416                 }\r
417 \r
418                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN\r
419                 either.  A margin is permitted as we would not necessarily run as\r
420                 soon as we unblocked. */\r
421                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
422                 {\r
423                         xErrorOccurred = pdTRUE;\r
424                 }\r
425 \r
426                 /* Suspend ready for test 3. */\r
427                 xRunIndicator = bktRUN_INDICATOR;\r
428                 vTaskSuspend( NULL );\r
429 \r
430                 /*********************************************************************\r
431         Test 4\r
432 \r
433                 As per test three, but with the send and receive reversed. */\r
434                 xTimeWhenBlocking = xTaskGetTickCount();\r
435 \r
436                 /* We should unblock after bktTIME_TO_BLOCK having not received\r
437                 anything on the queue. */\r
438                 xRunIndicator = bktRUN_INDICATOR;\r
439                 if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )\r
440                 {\r
441                         xErrorOccurred = pdTRUE;\r
442                 }\r
443 \r
444                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
445 \r
446                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
447                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
448                 {\r
449                         xErrorOccurred = pdTRUE;\r
450                 }\r
451 \r
452                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN\r
453                 either.  A margin is permitted as we would not necessarily run as soon\r
454                 as we unblocked. */\r
455                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
456                 {\r
457                         xErrorOccurred = pdTRUE;\r
458                 }\r
459 \r
460                 xRunIndicator = bktRUN_INDICATOR;\r
461 \r
462                 xSecondaryCycles++;\r
463         }\r
464 }\r
465 /*-----------------------------------------------------------*/\r
466 \r
467 portBASE_TYPE xAreBlockTimeTestTasksStillRunning( void )\r
468 {\r
469 static portBASE_TYPE xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;\r
470 portBASE_TYPE xReturn = pdPASS;\r
471 \r
472         /* Have both tasks performed at least one cycle since this function was\r
473         last called? */\r
474         if( xPrimaryCycles == xLastPrimaryCycleCount )\r
475         {\r
476                 xReturn = pdFAIL;\r
477         }\r
478 \r
479         if( xSecondaryCycles == xLastSecondaryCycleCount )\r
480         {\r
481                 xReturn = pdFAIL;\r
482         }\r
483 \r
484         if( xErrorOccurred == pdTRUE )\r
485         {\r
486                 xReturn = pdFAIL;\r
487         }\r
488 \r
489         xLastSecondaryCycleCount = xSecondaryCycles;\r
490         xLastPrimaryCycleCount = xPrimaryCycles;\r
491 \r
492         return xReturn;\r
493 }\r