]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Common/Minimal/AltBlock.c
Add additional critical section to the default tickless implementations.
[freertos] / FreeRTOS / Demo / Common / Minimal / AltBlock.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 is a version of BlockTim.c that uses the light weight API.\r
67  *\r
68  * This file contains some test scenarios that ensure tasks do not exit queue\r
69  * send or receive functions prematurely.  A description of the tests is\r
70  * included within the code.\r
71  */\r
72 \r
73 /* Kernel includes. */\r
74 #include "FreeRTOS.h"\r
75 #include "task.h"\r
76 #include "queue.h"\r
77 \r
78 /* Demo includes. */\r
79 #include "AltBlock.h"\r
80 \r
81 /* Task priorities. */\r
82 #define bktPRIMARY_PRIORITY                     ( 3 )\r
83 #define bktSECONDARY_PRIORITY           ( 2 )\r
84 \r
85 /* Task behaviour. */\r
86 #define bktQUEUE_LENGTH                         ( 5 )\r
87 #define bktSHORT_WAIT                           ( ( ( portTickType ) 20 ) / portTICK_RATE_MS )\r
88 #define bktPRIMARY_BLOCK_TIME           ( 10 )\r
89 #define bktALLOWABLE_MARGIN                     ( 12 )\r
90 #define bktTIME_TO_BLOCK                        ( 175 )\r
91 #define bktDONT_BLOCK                           ( ( portTickType ) 0 )\r
92 #define bktRUN_INDICATOR                        ( ( unsigned portBASE_TYPE ) 0x55 )\r
93 \r
94 /* The queue on which the tasks block. */\r
95 static xQueueHandle xTestQueue;\r
96 \r
97 /* Handle to the secondary task is required by the primary task for calls\r
98 to vTaskSuspend/Resume(). */\r
99 static xTaskHandle xSecondary;\r
100 \r
101 /* Used to ensure that tasks are still executing without error. */\r
102 static portBASE_TYPE xPrimaryCycles = 0, xSecondaryCycles = 0;\r
103 static portBASE_TYPE xErrorOccurred = pdFALSE;\r
104 \r
105 /* Provides a simple mechanism for the primary task to know when the\r
106 secondary task has executed. */\r
107 static volatile unsigned portBASE_TYPE xRunIndicator;\r
108 \r
109 /* The two test tasks.  Their behaviour is commented within the files. */\r
110 static void vPrimaryBlockTimeTestTask( void *pvParameters );\r
111 static void vSecondaryBlockTimeTestTask( void *pvParameters );\r
112 \r
113 /*-----------------------------------------------------------*/\r
114 \r
115 void vCreateAltBlockTimeTasks( void )\r
116 {\r
117         /* Create the queue on which the two tasks block. */\r
118     xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( portBASE_TYPE ) );\r
119 \r
120         /* vQueueAddToRegistry() adds the queue to the queue registry, if one is\r
121         in use.  The queue registry is provided as a means for kernel aware \r
122         debuggers to locate queues and has no purpose if a kernel aware debugger\r
123         is not being used.  The call to vQueueAddToRegistry() will be removed\r
124         by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is \r
125         defined to be less than 1. */\r
126         vQueueAddToRegistry( xTestQueue, ( signed portCHAR * ) "AltBlockQueue" );\r
127 \r
128 \r
129         /* Create the two test tasks. */\r
130         xTaskCreate( vPrimaryBlockTimeTestTask, ( signed portCHAR * )"FBTest1", configMINIMAL_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL );\r
131         xTaskCreate( vSecondaryBlockTimeTestTask, ( signed portCHAR * )"FBTest2", configMINIMAL_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary );\r
132 }\r
133 /*-----------------------------------------------------------*/\r
134 \r
135 static void vPrimaryBlockTimeTestTask( void *pvParameters )\r
136 {\r
137 portBASE_TYPE xItem, xData;\r
138 portTickType xTimeWhenBlocking;\r
139 portTickType xTimeToBlock, xBlockedTime;\r
140 \r
141         #ifdef USE_STDIO\r
142         void vPrintDisplayMessage( const portCHAR * const * ppcMessageToSend );\r
143         \r
144                 const portCHAR * const pcTaskStartMsg = "Alt primary block time test started.\r\n";\r
145 \r
146                 /* Queue a message for printing to say the task has started. */\r
147                 vPrintDisplayMessage( &pcTaskStartMsg );\r
148         #endif\r
149 \r
150         ( void ) pvParameters;\r
151 \r
152         for( ;; )\r
153         {\r
154                 /*********************************************************************\r
155         Test 1\r
156 \r
157         Simple block time wakeup test on queue receives. */\r
158                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
159                 {\r
160                         /* The queue is empty. Attempt to read from the queue using a block\r
161                         time.  When we wake, ensure the delta in time is as expected. */\r
162                         xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;\r
163 \r
164                         /* A critical section is used to minimise the jitter in the time\r
165                         measurements. */\r
166                         portENTER_CRITICAL();\r
167                         {\r
168                                 xTimeWhenBlocking = xTaskGetTickCount();\r
169                                 \r
170                                 /* We should unblock after xTimeToBlock having not received\r
171                                 anything on the queue. */\r
172                                 if( xQueueAltReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )\r
173                                 {\r
174                                         xErrorOccurred = pdTRUE;\r
175                                 }\r
176 \r
177                                 /* How long were we blocked for? */\r
178                                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
179                         }\r
180                         portEXIT_CRITICAL();\r
181 \r
182                         if( xBlockedTime < xTimeToBlock )\r
183                         {\r
184                                 /* Should not have blocked for less than we requested. */\r
185                                 xErrorOccurred = pdTRUE;\r
186                         }\r
187 \r
188                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
189                         {\r
190                                 /* Should not have blocked for longer than we requested,\r
191                                 although we would not necessarily run as soon as we were\r
192                                 unblocked so a margin is allowed. */\r
193                                 xErrorOccurred = pdTRUE;\r
194                         }\r
195                 }\r
196 \r
197 \r
198                 #if configUSE_PREEMPTION == 0\r
199                         taskYIELD();\r
200                 #endif\r
201 \r
202 \r
203                 /*********************************************************************\r
204         Test 2\r
205 \r
206         Simple block time wakeup test on queue sends.\r
207 \r
208                 First fill the queue.  It should be empty so all sends should pass. */\r
209                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
210                 {\r
211                         if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
212                         {\r
213                                 xErrorOccurred = pdTRUE;\r
214                         }\r
215                 }\r
216 \r
217                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
218                 {\r
219                         /* The queue is full. Attempt to write to the queue using a block\r
220                         time.  When we wake, ensure the delta in time is as expected. */\r
221                         xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;\r
222 \r
223                         portENTER_CRITICAL();\r
224                         {\r
225                                 xTimeWhenBlocking = xTaskGetTickCount();\r
226                                 \r
227                                 /* We should unblock after xTimeToBlock having not received\r
228                                 anything on the queue. */\r
229                                 if( xQueueAltSendToBack( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )\r
230                                 {\r
231                                         xErrorOccurred = pdTRUE;\r
232                                 }\r
233 \r
234                                 /* How long were we blocked for? */\r
235                                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
236                         }\r
237                         portEXIT_CRITICAL();\r
238 \r
239                         if( xBlockedTime < xTimeToBlock )\r
240                         {\r
241                                 /* Should not have blocked for less than we requested. */\r
242                                 xErrorOccurred = pdTRUE;\r
243                         }\r
244 \r
245                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
246                         {\r
247                                 /* Should not have blocked for longer than we requested,\r
248                                 although we would not necessarily run as soon as we were\r
249                                 unblocked so a margin is allowed. */\r
250                                 xErrorOccurred = pdTRUE;\r
251                         }\r
252                 }\r
253 \r
254                 #if configUSE_PREEMPTION == 0\r
255                         taskYIELD();\r
256                 #endif\r
257 \r
258                 \r
259                 /*********************************************************************\r
260         Test 3\r
261 \r
262                 Wake the other task, it will block attempting to post to the queue.\r
263                 When we read from the queue the other task will wake, but before it\r
264                 can run we will post to the queue again.  When the other task runs it\r
265                 will find the queue still full, even though it was woken.  It should\r
266                 recognise that its block time has not expired and return to block for\r
267                 the remains of its block time.\r
268 \r
269                 Wake the other task so it blocks attempting to post to the already\r
270                 full queue. */\r
271                 xRunIndicator = 0;\r
272                 vTaskResume( xSecondary );\r
273 \r
274                 /* We need to wait a little to ensure the other task executes. */\r
275                 while( xRunIndicator != bktRUN_INDICATOR )\r
276                 {\r
277                         /* The other task has not yet executed. */\r
278                         vTaskDelay( bktSHORT_WAIT );\r
279                 }\r
280                 /* Make sure the other task is blocked on the queue. */\r
281                 vTaskDelay( bktSHORT_WAIT );\r
282                 xRunIndicator = 0;\r
283 \r
284                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
285                 {\r
286                         /* Now when we make space on the queue the other task should wake\r
287                         but not execute as this task has higher priority. */                            \r
288                         if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
289                         {\r
290                                 xErrorOccurred = pdTRUE;\r
291                         }\r
292 \r
293                         /* Now fill the queue again before the other task gets a chance to\r
294                         execute.  If the other task had executed we would find the queue\r
295                         full ourselves, and the other task have set xRunIndicator. */\r
296                         if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
297                         {\r
298                                 xErrorOccurred = pdTRUE;\r
299                         }\r
300 \r
301                         if( xRunIndicator == bktRUN_INDICATOR )\r
302                         {\r
303                                 /* The other task should not have executed. */\r
304                                 xErrorOccurred = pdTRUE;\r
305                         }\r
306 \r
307                         /* Raise the priority of the other task so it executes and blocks\r
308                         on the queue again. */\r
309                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
310 \r
311                         /* The other task should now have re-blocked without exiting the\r
312                         queue function. */\r
313                         if( xRunIndicator == bktRUN_INDICATOR )\r
314                         {\r
315                                 /* The other task should not have executed outside of the\r
316                                 queue function. */\r
317                                 xErrorOccurred = pdTRUE;\r
318                         }\r
319 \r
320                         /* Set the priority back down. */\r
321                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );                  \r
322                 }\r
323 \r
324                 /* Let the other task timeout.  When it unblockes it will check that it\r
325                 unblocked at the correct time, then suspend itself. */\r
326                 while( xRunIndicator != bktRUN_INDICATOR )\r
327                 {\r
328                         vTaskDelay( bktSHORT_WAIT );\r
329                 }\r
330                 vTaskDelay( bktSHORT_WAIT );\r
331                 xRunIndicator = 0;\r
332 \r
333                 #if configUSE_PREEMPTION == 0\r
334                         taskYIELD();\r
335                 #endif\r
336 \r
337                 /*********************************************************************\r
338         Test 4\r
339 \r
340                 As per test 3 - but with the send and receive the other way around.\r
341                 The other task blocks attempting to read from the queue.\r
342 \r
343                 Empty the queue.  We should find that it is full. */\r
344                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
345                 {\r
346                         if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
347                         {\r
348                                 xErrorOccurred = pdTRUE;\r
349                         }\r
350                 }\r
351                 \r
352                 /* Wake the other task so it blocks attempting to read from  the\r
353                 already empty queue. */\r
354                 vTaskResume( xSecondary );\r
355 \r
356                 /* We need to wait a little to ensure the other task executes. */\r
357                 while( xRunIndicator != bktRUN_INDICATOR )\r
358                 {\r
359                         vTaskDelay( bktSHORT_WAIT );\r
360                 }\r
361                 vTaskDelay( bktSHORT_WAIT );\r
362                 xRunIndicator = 0;\r
363 \r
364                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
365                 {\r
366                         /* Now when we place an item on the queue the other task should\r
367                         wake but not execute as this task has higher priority. */                               \r
368                         if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
369                         {\r
370                                 xErrorOccurred = pdTRUE;\r
371                         }\r
372 \r
373                         /* Now empty the queue again before the other task gets a chance to\r
374                         execute.  If the other task had executed we would find the queue\r
375                         empty ourselves, and the other task would be suspended. */\r
376                         if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
377                         {\r
378                                 xErrorOccurred = pdTRUE;\r
379                         }\r
380 \r
381                         if( xRunIndicator == bktRUN_INDICATOR )\r
382                         {\r
383                                 /* The other task should not have executed. */\r
384                                 xErrorOccurred = pdTRUE;\r
385                         }\r
386 \r
387                         /* Raise the priority of the other task so it executes and blocks\r
388                         on the queue again. */\r
389                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
390 \r
391                         /* The other task should now have re-blocked without exiting the\r
392                         queue function. */\r
393                         if( xRunIndicator == bktRUN_INDICATOR )\r
394                         {\r
395                                 /* The other task should not have executed outside of the\r
396                                 queue function. */\r
397                                 xErrorOccurred = pdTRUE;\r
398                         }\r
399                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );                  \r
400                 }\r
401 \r
402                 /* Let the other task timeout.  When it unblockes it will check that it\r
403                 unblocked at the correct time, then suspend itself. */\r
404                 while( xRunIndicator != bktRUN_INDICATOR )\r
405                 {\r
406                         vTaskDelay( bktSHORT_WAIT );\r
407                 }\r
408                 vTaskDelay( bktSHORT_WAIT );\r
409 \r
410                 xPrimaryCycles++;\r
411         }\r
412 }\r
413 /*-----------------------------------------------------------*/\r
414 \r
415 static void vSecondaryBlockTimeTestTask( void *pvParameters )\r
416 {\r
417 portTickType xTimeWhenBlocking, xBlockedTime;\r
418 portBASE_TYPE xData;\r
419 \r
420         #ifdef USE_STDIO\r
421         void vPrintDisplayMessage( const portCHAR * const * ppcMessageToSend );\r
422         \r
423                 const portCHAR * const pcTaskStartMsg = "Alt secondary block time test started.\r\n";\r
424 \r
425                 /* Queue a message for printing to say the task has started. */\r
426                 vPrintDisplayMessage( &pcTaskStartMsg );\r
427         #endif\r
428 \r
429         ( void ) pvParameters;\r
430 \r
431         for( ;; )\r
432         {\r
433                 /*********************************************************************\r
434         Test 1 and 2\r
435 \r
436                 This task does does not participate in these tests. */\r
437                 vTaskSuspend( NULL );\r
438 \r
439                 /*********************************************************************\r
440         Test 3\r
441 \r
442                 The first thing we do is attempt to read from the queue.  It should be\r
443                 full so we block.  Note the time before we block so we can check the\r
444                 wake time is as per that expected. */\r
445                 portENTER_CRITICAL();\r
446                 {\r
447                         xTimeWhenBlocking = xTaskGetTickCount();\r
448                         \r
449                         /* We should unblock after bktTIME_TO_BLOCK having not received\r
450                         anything on the queue. */\r
451                         xData = 0;\r
452                         xRunIndicator = bktRUN_INDICATOR;\r
453                         if( xQueueAltSendToBack( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )\r
454                         {\r
455                                 xErrorOccurred = pdTRUE;\r
456                         }\r
457 \r
458                         /* How long were we inside the send function? */\r
459                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
460                 }\r
461                 portEXIT_CRITICAL();\r
462 \r
463                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
464                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
465                 {\r
466                         xErrorOccurred = pdTRUE;\r
467                 }\r
468 \r
469                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN\r
470                 either.  A margin is permitted as we would not necessarily run as\r
471                 soon as we unblocked. */\r
472                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
473                 {\r
474                         xErrorOccurred = pdTRUE;\r
475                 }\r
476 \r
477                 /* Suspend ready for test 3. */\r
478                 xRunIndicator = bktRUN_INDICATOR;\r
479                 vTaskSuspend( NULL );\r
480 \r
481                 /*********************************************************************\r
482         Test 4\r
483 \r
484                 As per test three, but with the send and receive reversed. */\r
485                 portENTER_CRITICAL();\r
486                 {\r
487                         xTimeWhenBlocking = xTaskGetTickCount();\r
488                         \r
489                         /* We should unblock after bktTIME_TO_BLOCK having not received\r
490                         anything on the queue. */\r
491                         xRunIndicator = bktRUN_INDICATOR;\r
492                         if( xQueueAltReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )\r
493                         {\r
494                                 xErrorOccurred = pdTRUE;\r
495                         }\r
496 \r
497                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
498                 }\r
499                 portEXIT_CRITICAL();\r
500 \r
501                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
502                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
503                 {\r
504                         xErrorOccurred = pdTRUE;\r
505                 }\r
506 \r
507                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN\r
508                 either.  A margin is permitted as we would not necessarily run as soon\r
509                 as we unblocked. */\r
510                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
511                 {\r
512                         xErrorOccurred = pdTRUE;\r
513                 }\r
514 \r
515                 xRunIndicator = bktRUN_INDICATOR;\r
516 \r
517                 xSecondaryCycles++;\r
518         }\r
519 }\r
520 /*-----------------------------------------------------------*/\r
521 \r
522 portBASE_TYPE xAreAltBlockTimeTestTasksStillRunning( void )\r
523 {\r
524 static portBASE_TYPE xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;\r
525 portBASE_TYPE xReturn = pdPASS;\r
526 \r
527         /* Have both tasks performed at least one cycle since this function was\r
528         last called? */\r
529         if( xPrimaryCycles == xLastPrimaryCycleCount )\r
530         {\r
531                 xReturn = pdFAIL;\r
532         }\r
533 \r
534         if( xSecondaryCycles == xLastSecondaryCycleCount )\r
535         {\r
536                 xReturn = pdFAIL;\r
537         }\r
538 \r
539         if( xErrorOccurred == pdTRUE )\r
540         {\r
541                 xReturn = pdFAIL;\r
542         }\r
543 \r
544         xLastSecondaryCycleCount = xSecondaryCycles;\r
545         xLastPrimaryCycleCount = xPrimaryCycles;\r
546 \r
547         return xReturn;\r
548 }\r