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