]> begriffs open source - cmsis-freertos/blob - Demo/Common/Minimal/blocktim.c
Updated pack to FreeRTOS 10.4.6
[cmsis-freertos] / Demo / Common / Minimal / blocktim.c
1 /*
2  * FreeRTOS V202111.00
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of
6  * this software and associated documentation files (the "Software"), to deal in
7  * the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9  * the Software, and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in all
13  * copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * http://www.FreeRTOS.org
23  * http://aws.amazon.com/freertos
24  *
25  * 1 tab == 4 spaces!
26  */
27
28 /*
29  * This file contains some test scenarios that ensure tasks do not exit queue
30  * send or receive functions prematurely.  A description of the tests is
31  * included within the code.
32  */
33
34 /* Kernel includes. */
35 #include "FreeRTOS.h"
36 #include "task.h"
37 #include "queue.h"
38
39 /* Demo includes. */
40 #include "blocktim.h"
41
42 /* Task priorities and stack sizes.  Allow these to be overridden. */
43 #ifndef bktPRIMARY_PRIORITY
44     #define bktPRIMARY_PRIORITY    ( configMAX_PRIORITIES - 3 )
45 #endif
46
47 #ifndef bktSECONDARY_PRIORITY
48     #define bktSECONDARY_PRIORITY    ( configMAX_PRIORITIES - 4 )
49 #endif
50
51 #ifndef bktBLOCK_TIME_TASK_STACK_SIZE
52     #define bktBLOCK_TIME_TASK_STACK_SIZE    configMINIMAL_STACK_SIZE
53 #endif
54
55 /* Task behaviour. */
56 #define bktQUEUE_LENGTH          ( 5 )
57 #define bktSHORT_WAIT            pdMS_TO_TICKS( ( TickType_t ) 20 )
58 #define bktPRIMARY_BLOCK_TIME    ( 10 )
59 #define bktALLOWABLE_MARGIN      ( 15 )
60 #define bktTIME_TO_BLOCK         ( 175 )
61 #define bktDONT_BLOCK            ( ( TickType_t ) 0 )
62 #define bktRUN_INDICATOR         ( ( UBaseType_t ) 0x55 )
63
64 /* In case the demo does not have software timers enabled, as this file uses
65  * the configTIMER_TASK_PRIORITY setting. */
66 #ifndef configTIMER_TASK_PRIORITY
67     #define configTIMER_TASK_PRIORITY    ( configMAX_PRIORITIES - 1 )
68 #endif
69
70 /*-----------------------------------------------------------*/
71
72 /*
73  * The two test tasks.  Their behaviour is commented within the functions.
74  */
75 static void vPrimaryBlockTimeTestTask( void * pvParameters );
76 static void vSecondaryBlockTimeTestTask( void * pvParameters );
77
78 /*
79  * Very basic tests to verify the block times are as expected.
80  */
81 static void prvBasicDelayTests( void );
82
83 /*-----------------------------------------------------------*/
84
85 /* The queue on which the tasks block. */
86 static QueueHandle_t xTestQueue;
87
88 /* Handle to the secondary task is required by the primary task for calls
89  * to vTaskSuspend/Resume(). */
90 static TaskHandle_t xSecondary;
91
92 /* Used to ensure that tasks are still executing without error. */
93 static volatile BaseType_t xPrimaryCycles = 0, xSecondaryCycles = 0;
94 static volatile BaseType_t xErrorOccurred = pdFALSE;
95
96 /* Provides a simple mechanism for the primary task to know when the
97  * secondary task has executed. */
98 static volatile UBaseType_t xRunIndicator;
99
100 /*-----------------------------------------------------------*/
101
102 void vCreateBlockTimeTasks( void )
103 {
104     /* Create the queue on which the two tasks block. */
105     xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( BaseType_t ) );
106
107     if( xTestQueue != NULL )
108     {
109         /* vQueueAddToRegistry() adds the queue to the queue registry, if one
110          * is in use.  The queue registry is provided as a means for kernel aware
111          * debuggers to locate queues and has no purpose if a kernel aware
112          * debugger is not being used.  The call to vQueueAddToRegistry() will be
113          * removed by the pre-processor if configQUEUE_REGISTRY_SIZE is not
114          * defined or is defined to be less than 1. */
115         vQueueAddToRegistry( xTestQueue, "Block_Time_Queue" );
116
117         /* Create the two test tasks. */
118         xTaskCreate( vPrimaryBlockTimeTestTask, "BTest1", bktBLOCK_TIME_TASK_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL );
119         xTaskCreate( vSecondaryBlockTimeTestTask, "BTest2", bktBLOCK_TIME_TASK_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary );
120     }
121 }
122 /*-----------------------------------------------------------*/
123
124 static void vPrimaryBlockTimeTestTask( void * pvParameters )
125 {
126     BaseType_t xItem, xData;
127     TickType_t xTimeWhenBlocking;
128     TickType_t xTimeToBlock, xBlockedTime;
129
130     ( void ) pvParameters;
131
132     for( ; ; )
133     {
134         /*********************************************************************
135          * Test 0
136          *
137          * Basic vTaskDelay() and vTaskDelayUntil() tests. */
138         prvBasicDelayTests();
139
140         /*********************************************************************
141          * Test 1
142          *
143          * Simple block time wakeup test on queue receives. */
144         for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
145         {
146             /* The queue is empty. Attempt to read from the queue using a block
147              * time.  When we wake, ensure the delta in time is as expected. */
148             xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem );
149
150             xTimeWhenBlocking = xTaskGetTickCount();
151
152             /* We should unblock after xTimeToBlock having not received
153              * anything on the queue. */
154             if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )
155             {
156                 xErrorOccurred = pdTRUE;
157             }
158
159             /* How long were we blocked for? */
160             xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
161
162             if( xBlockedTime < xTimeToBlock )
163             {
164                 /* Should not have blocked for less than we requested. */
165                 xErrorOccurred = pdTRUE;
166             }
167
168             if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
169             {
170                 /* Should not have blocked for longer than we requested,
171                  * although we would not necessarily run as soon as we were
172                  * unblocked so a margin is allowed. */
173                 xErrorOccurred = pdTRUE;
174             }
175         }
176
177         /*********************************************************************
178          *  Test 2
179          *
180          *  Simple block time wakeup test on queue sends.
181          *
182          *  First fill the queue.  It should be empty so all sends should pass. */
183         for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
184         {
185             if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
186             {
187                 xErrorOccurred = pdTRUE;
188             }
189
190             #if configUSE_PREEMPTION == 0
191                 taskYIELD();
192             #endif
193         }
194
195         for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
196         {
197             /* The queue is full. Attempt to write to the queue using a block
198              * time.  When we wake, ensure the delta in time is as expected. */
199             xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem );
200
201             xTimeWhenBlocking = xTaskGetTickCount();
202
203             /* We should unblock after xTimeToBlock having not received
204              * anything on the queue. */
205             if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )
206             {
207                 xErrorOccurred = pdTRUE;
208             }
209
210             /* How long were we blocked for? */
211             xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
212
213             if( xBlockedTime < xTimeToBlock )
214             {
215                 /* Should not have blocked for less than we requested. */
216                 xErrorOccurred = pdTRUE;
217             }
218
219             if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
220             {
221                 /* Should not have blocked for longer than we requested,
222                  * although we would not necessarily run as soon as we were
223                  * unblocked so a margin is allowed. */
224                 xErrorOccurred = pdTRUE;
225             }
226         }
227
228         /*********************************************************************
229          * Test 3
230          *
231          * Wake the other task, it will block attempting to post to the queue.
232          * When we read from the queue the other task will wake, but before it
233          * can run we will post to the queue again.  When the other task runs it
234          * will find the queue still full, even though it was woken.  It should
235          * recognise that its block time has not expired and return to block for
236          * the remains of its block time.
237          *
238          * Wake the other task so it blocks attempting to post to the already
239          * full queue. */
240         xRunIndicator = 0;
241         vTaskResume( xSecondary );
242
243         /* We need to wait a little to ensure the other task executes. */
244         while( xRunIndicator != bktRUN_INDICATOR )
245         {
246             /* The other task has not yet executed. */
247             vTaskDelay( bktSHORT_WAIT );
248         }
249
250         /* Make sure the other task is blocked on the queue. */
251         vTaskDelay( bktSHORT_WAIT );
252         xRunIndicator = 0;
253
254         for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
255         {
256             /* Now when we make space on the queue the other task should wake
257              * but not execute as this task has higher priority. */
258             if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
259             {
260                 xErrorOccurred = pdTRUE;
261             }
262
263             /* Now fill the queue again before the other task gets a chance to
264              * execute.  If the other task had executed we would find the queue
265              * full ourselves, and the other task have set xRunIndicator. */
266             if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
267             {
268                 xErrorOccurred = pdTRUE;
269             }
270
271             if( xRunIndicator == bktRUN_INDICATOR )
272             {
273                 /* The other task should not have executed. */
274                 xErrorOccurred = pdTRUE;
275             }
276
277             /* Raise the priority of the other task so it executes and blocks
278              * on the queue again. */
279             vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
280
281             /* The other task should now have re-blocked without exiting the
282              * queue function. */
283             if( xRunIndicator == bktRUN_INDICATOR )
284             {
285                 /* The other task should not have executed outside of the
286                  * queue function. */
287                 xErrorOccurred = pdTRUE;
288             }
289
290             /* Set the priority back down. */
291             vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
292         }
293
294         /* Let the other task timeout.  When it unblockes it will check that it
295          * unblocked at the correct time, then suspend itself. */
296         while( xRunIndicator != bktRUN_INDICATOR )
297         {
298             vTaskDelay( bktSHORT_WAIT );
299         }
300
301         vTaskDelay( bktSHORT_WAIT );
302         xRunIndicator = 0;
303
304         /*********************************************************************
305          * Test 4
306          *
307          * As per test 3 - but with the send and receive the other way around.
308          * The other task blocks attempting to read from the queue.
309          *
310          * Empty the queue.  We should find that it is full. */
311         for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
312         {
313             if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
314             {
315                 xErrorOccurred = pdTRUE;
316             }
317         }
318
319         /* Wake the other task so it blocks attempting to read from  the
320          * already      empty queue. */
321         vTaskResume( xSecondary );
322
323         /* We need to wait a little to ensure the other task executes. */
324         while( xRunIndicator != bktRUN_INDICATOR )
325         {
326             vTaskDelay( bktSHORT_WAIT );
327         }
328
329         vTaskDelay( bktSHORT_WAIT );
330         xRunIndicator = 0;
331
332         for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
333         {
334             /* Now when we place an item on the queue the other task should
335              * wake but not execute as this task has higher priority. */
336             if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
337             {
338                 xErrorOccurred = pdTRUE;
339             }
340
341             /* Now empty the queue again before the other task gets a chance to
342              * execute.  If the other task had executed we would find the queue
343              * empty ourselves, and the other task would be suspended. */
344             if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
345             {
346                 xErrorOccurred = pdTRUE;
347             }
348
349             if( xRunIndicator == bktRUN_INDICATOR )
350             {
351                 /* The other task should not have executed. */
352                 xErrorOccurred = pdTRUE;
353             }
354
355             /* Raise the priority of the other task so it executes and blocks
356              * on the queue again. */
357             vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
358
359             /* The other task should now have re-blocked without exiting the
360              * queue function. */
361             if( xRunIndicator == bktRUN_INDICATOR )
362             {
363                 /* The other task should not have executed outside of the
364                  * queue function. */
365                 xErrorOccurred = pdTRUE;
366             }
367
368             vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
369         }
370
371         /* Let the other task timeout.  When it unblockes it will check that it
372          * unblocked at the correct time, then suspend itself. */
373         while( xRunIndicator != bktRUN_INDICATOR )
374         {
375             vTaskDelay( bktSHORT_WAIT );
376         }
377
378         vTaskDelay( bktSHORT_WAIT );
379
380         xPrimaryCycles++;
381     }
382 }
383 /*-----------------------------------------------------------*/
384
385 static void vSecondaryBlockTimeTestTask( void * pvParameters )
386 {
387     TickType_t xTimeWhenBlocking, xBlockedTime;
388     BaseType_t xData;
389
390     ( void ) pvParameters;
391
392     for( ; ; )
393     {
394         /*********************************************************************
395          * Test 0, 1 and 2
396          *
397          * This task does not participate in these tests. */
398         vTaskSuspend( NULL );
399
400         /*********************************************************************
401          * Test 3
402          *
403          * The first thing we do is attempt to read from the queue.  It should be
404          * full so we block.  Note the time before we block so we can check the
405          * wake time is as per that expected. */
406         xTimeWhenBlocking = xTaskGetTickCount();
407
408         /* We should unblock after bktTIME_TO_BLOCK having not sent anything to
409          * the queue. */
410         xData = 0;
411         xRunIndicator = bktRUN_INDICATOR;
412
413         if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )
414         {
415             xErrorOccurred = pdTRUE;
416         }
417
418         /* How long were we inside the send function? */
419         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
420
421         /* We should not have blocked for less time than bktTIME_TO_BLOCK. */
422         if( xBlockedTime < bktTIME_TO_BLOCK )
423         {
424             xErrorOccurred = pdTRUE;
425         }
426
427         /* We should of not blocked for much longer than bktALLOWABLE_MARGIN
428          * either.  A margin is permitted as we would not necessarily run as
429          * soon as we unblocked. */
430         if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )
431         {
432             xErrorOccurred = pdTRUE;
433         }
434
435         /* Suspend ready for test 3. */
436         xRunIndicator = bktRUN_INDICATOR;
437         vTaskSuspend( NULL );
438
439         /*********************************************************************
440          * Test 4
441          *
442          * As per test three, but with the send and receive reversed. */
443         xTimeWhenBlocking = xTaskGetTickCount();
444
445         /* We should unblock after bktTIME_TO_BLOCK having not received
446          * anything on the queue. */
447         xRunIndicator = bktRUN_INDICATOR;
448
449         if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )
450         {
451             xErrorOccurred = pdTRUE;
452         }
453
454         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
455
456         /* We should not have blocked for less time than bktTIME_TO_BLOCK. */
457         if( xBlockedTime < bktTIME_TO_BLOCK )
458         {
459             xErrorOccurred = pdTRUE;
460         }
461
462         /* We should of not blocked for much longer than bktALLOWABLE_MARGIN
463          * either.  A margin is permitted as we would not necessarily run as soon
464          * as we unblocked. */
465         if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )
466         {
467             xErrorOccurred = pdTRUE;
468         }
469
470         xRunIndicator = bktRUN_INDICATOR;
471
472         xSecondaryCycles++;
473     }
474 }
475 /*-----------------------------------------------------------*/
476
477 static void prvBasicDelayTests( void )
478 {
479     TickType_t xPreTime, xPostTime, x, xLastUnblockTime, xExpectedUnblockTime;
480     const TickType_t xPeriod = 75, xCycles = 5, xAllowableMargin = ( bktALLOWABLE_MARGIN >> 1 ), xHalfPeriod = xPeriod / ( TickType_t ) 2;
481     BaseType_t xDidBlock;
482
483     /* Temporarily increase priority so the timing is more accurate, but not so
484      * high as to disrupt the timer tests. */
485     vTaskPrioritySet( NULL, configTIMER_TASK_PRIORITY - 1 );
486
487     /* Crude check to too see that vTaskDelay() blocks for the expected
488      * period. */
489     xPreTime = xTaskGetTickCount();
490     vTaskDelay( bktTIME_TO_BLOCK );
491     xPostTime = xTaskGetTickCount();
492
493     /* The priority is higher, so the allowable margin is halved when compared
494      * to the other tests in this file. */
495     if( ( xPostTime - xPreTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) )
496     {
497         xErrorOccurred = pdTRUE;
498     }
499
500     /* Now crude tests to check the vTaskDelayUntil() functionality. */
501     xPostTime = xTaskGetTickCount();
502     xLastUnblockTime = xPostTime;
503
504     for( x = 0; x < xCycles; x++ )
505     {
506         /* Calculate the next expected unblock time from the time taken before
507          * this loop was entered. */
508         xExpectedUnblockTime = xPostTime + ( x * xPeriod );
509
510         vTaskDelayUntil( &xLastUnblockTime, xPeriod );
511
512         if( ( xTaskGetTickCount() - xExpectedUnblockTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) )
513         {
514             xErrorOccurred = pdTRUE;
515         }
516
517         xPrimaryCycles++;
518     }
519
520     /* Crude tests for return value of xTaskDelayUntil().  First a standard block
521      * should return that the task does block. */
522     xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod );
523
524     if( xDidBlock != pdTRUE )
525     {
526         xErrorOccurred = pdTRUE;
527     }
528
529     /* Now delay a few ticks so repeating the above block period will not block for
530      * the full amount of time, but will still block. */
531     vTaskDelay( xHalfPeriod );
532     xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod );
533
534     if( xDidBlock != pdTRUE )
535     {
536         xErrorOccurred = pdTRUE;
537     }
538
539     /* This time block for longer than xPeriod before calling xTaskDelayUntil() so
540      * the call to xTaskDelayUntil() should not block. */
541     vTaskDelay( xPeriod );
542     xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod );
543
544     if( xDidBlock != pdFALSE )
545     {
546         xErrorOccurred = pdTRUE;
547     }
548
549     /* Catch up. */
550     xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod );
551
552     if( xDidBlock != pdTRUE )
553     {
554         xErrorOccurred = pdTRUE;
555     }
556
557     /* Again block for slightly longer than a period so ensure the time is in the
558      * past next time xTaskDelayUntil() gets called. */
559     vTaskDelay( xPeriod + xAllowableMargin );
560     xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod );
561
562     if( xDidBlock != pdFALSE )
563     {
564         xErrorOccurred = pdTRUE;
565     }
566
567     /* Reset to the original task priority ready for the other tests. */
568     vTaskPrioritySet( NULL, bktPRIMARY_PRIORITY );
569 }
570 /*-----------------------------------------------------------*/
571
572 BaseType_t xAreBlockTimeTestTasksStillRunning( void )
573 {
574     static BaseType_t xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;
575     BaseType_t xReturn = pdPASS;
576
577     /* Have both tasks performed at least one cycle since this function was
578      * last called? */
579     if( xPrimaryCycles == xLastPrimaryCycleCount )
580     {
581         xReturn = pdFAIL;
582     }
583
584     if( xSecondaryCycles == xLastSecondaryCycleCount )
585     {
586         xReturn = pdFAIL;
587     }
588
589     if( xErrorOccurred == pdTRUE )
590     {
591         xReturn = pdFAIL;
592     }
593
594     xLastSecondaryCycleCount = xSecondaryCycles;
595     xLastPrimaryCycleCount = xPrimaryCycles;
596
597     return xReturn;
598 }