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