3 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
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:
12 * The above copyright notice and this permission notice shall be included in all
13 * copies or substantial portions of the Software.
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.
22 * http://www.FreeRTOS.org
23 * http://aws.amazon.com/freertos
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.
34 /* Kernel includes. */
42 /* Task priorities and stack sizes. Allow these to be overridden. */
43 #ifndef bktPRIMARY_PRIORITY
44 #define bktPRIMARY_PRIORITY ( configMAX_PRIORITIES - 3 )
47 #ifndef bktSECONDARY_PRIORITY
48 #define bktSECONDARY_PRIORITY ( configMAX_PRIORITIES - 4 )
51 #ifndef bktBLOCK_TIME_TASK_STACK_SIZE
52 #define bktBLOCK_TIME_TASK_STACK_SIZE configMINIMAL_STACK_SIZE
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 )
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 )
70 /*-----------------------------------------------------------*/
73 * The two test tasks. Their behaviour is commented within the functions.
75 static void vPrimaryBlockTimeTestTask( void * pvParameters );
76 static void vSecondaryBlockTimeTestTask( void * pvParameters );
79 * Very basic tests to verify the block times are as expected.
81 static void prvBasicDelayTests( void );
83 /*-----------------------------------------------------------*/
85 /* The queue on which the tasks block. */
86 static QueueHandle_t xTestQueue;
88 /* Handle to the secondary task is required by the primary task for calls
89 * to vTaskSuspend/Resume(). */
90 static TaskHandle_t xSecondary;
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;
96 /* Provides a simple mechanism for the primary task to know when the
97 * secondary task has executed. */
98 static volatile UBaseType_t xRunIndicator;
100 /*-----------------------------------------------------------*/
102 void vCreateBlockTimeTasks( void )
104 /* Create the queue on which the two tasks block. */
105 xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( BaseType_t ) );
107 if( xTestQueue != NULL )
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" );
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 );
122 /*-----------------------------------------------------------*/
124 static void vPrimaryBlockTimeTestTask( void * pvParameters )
126 BaseType_t xItem, xData;
127 TickType_t xTimeWhenBlocking;
128 TickType_t xTimeToBlock, xBlockedTime;
130 ( void ) pvParameters;
134 /*********************************************************************
137 * Basic vTaskDelay() and vTaskDelayUntil() tests. */
138 prvBasicDelayTests();
140 /*********************************************************************
143 * Simple block time wakeup test on queue receives. */
144 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
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 );
150 xTimeWhenBlocking = xTaskGetTickCount();
152 /* We should unblock after xTimeToBlock having not received
153 * anything on the queue. */
154 if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )
156 xErrorOccurred = pdTRUE;
159 /* How long were we blocked for? */
160 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
162 if( xBlockedTime < xTimeToBlock )
164 /* Should not have blocked for less than we requested. */
165 xErrorOccurred = pdTRUE;
168 if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
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;
177 /*********************************************************************
180 * Simple block time wakeup test on queue sends.
182 * First fill the queue. It should be empty so all sends should pass. */
183 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
185 if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
187 xErrorOccurred = pdTRUE;
190 #if configUSE_PREEMPTION == 0
195 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
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 );
201 xTimeWhenBlocking = xTaskGetTickCount();
203 /* We should unblock after xTimeToBlock having not received
204 * anything on the queue. */
205 if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )
207 xErrorOccurred = pdTRUE;
210 /* How long were we blocked for? */
211 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
213 if( xBlockedTime < xTimeToBlock )
215 /* Should not have blocked for less than we requested. */
216 xErrorOccurred = pdTRUE;
219 if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
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;
228 /*********************************************************************
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.
238 * Wake the other task so it blocks attempting to post to the already
241 vTaskResume( xSecondary );
243 /* We need to wait a little to ensure the other task executes. */
244 while( xRunIndicator != bktRUN_INDICATOR )
246 /* The other task has not yet executed. */
247 vTaskDelay( bktSHORT_WAIT );
250 /* Make sure the other task is blocked on the queue. */
251 vTaskDelay( bktSHORT_WAIT );
254 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
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 )
260 xErrorOccurred = pdTRUE;
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 )
268 xErrorOccurred = pdTRUE;
271 if( xRunIndicator == bktRUN_INDICATOR )
273 /* The other task should not have executed. */
274 xErrorOccurred = pdTRUE;
277 /* Raise the priority of the other task so it executes and blocks
278 * on the queue again. */
279 vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
281 /* The other task should now have re-blocked without exiting the
283 if( xRunIndicator == bktRUN_INDICATOR )
285 /* The other task should not have executed outside of the
287 xErrorOccurred = pdTRUE;
290 /* Set the priority back down. */
291 vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
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 )
298 vTaskDelay( bktSHORT_WAIT );
301 vTaskDelay( bktSHORT_WAIT );
304 /*********************************************************************
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.
310 * Empty the queue. We should find that it is full. */
311 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
313 if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
315 xErrorOccurred = pdTRUE;
319 /* Wake the other task so it blocks attempting to read from the
320 * already empty queue. */
321 vTaskResume( xSecondary );
323 /* We need to wait a little to ensure the other task executes. */
324 while( xRunIndicator != bktRUN_INDICATOR )
326 vTaskDelay( bktSHORT_WAIT );
329 vTaskDelay( bktSHORT_WAIT );
332 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
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 )
338 xErrorOccurred = pdTRUE;
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 )
346 xErrorOccurred = pdTRUE;
349 if( xRunIndicator == bktRUN_INDICATOR )
351 /* The other task should not have executed. */
352 xErrorOccurred = pdTRUE;
355 /* Raise the priority of the other task so it executes and blocks
356 * on the queue again. */
357 vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
359 /* The other task should now have re-blocked without exiting the
361 if( xRunIndicator == bktRUN_INDICATOR )
363 /* The other task should not have executed outside of the
365 xErrorOccurred = pdTRUE;
368 vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
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 )
375 vTaskDelay( bktSHORT_WAIT );
378 vTaskDelay( bktSHORT_WAIT );
383 /*-----------------------------------------------------------*/
385 static void vSecondaryBlockTimeTestTask( void * pvParameters )
387 TickType_t xTimeWhenBlocking, xBlockedTime;
390 ( void ) pvParameters;
394 /*********************************************************************
397 * This task does not participate in these tests. */
398 vTaskSuspend( NULL );
400 /*********************************************************************
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();
408 /* We should unblock after bktTIME_TO_BLOCK having not sent anything to
411 xRunIndicator = bktRUN_INDICATOR;
413 if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )
415 xErrorOccurred = pdTRUE;
418 /* How long were we inside the send function? */
419 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
421 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */
422 if( xBlockedTime < bktTIME_TO_BLOCK )
424 xErrorOccurred = pdTRUE;
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 ) )
432 xErrorOccurred = pdTRUE;
435 /* Suspend ready for test 3. */
436 xRunIndicator = bktRUN_INDICATOR;
437 vTaskSuspend( NULL );
439 /*********************************************************************
442 * As per test three, but with the send and receive reversed. */
443 xTimeWhenBlocking = xTaskGetTickCount();
445 /* We should unblock after bktTIME_TO_BLOCK having not received
446 * anything on the queue. */
447 xRunIndicator = bktRUN_INDICATOR;
449 if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )
451 xErrorOccurred = pdTRUE;
454 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
456 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */
457 if( xBlockedTime < bktTIME_TO_BLOCK )
459 xErrorOccurred = pdTRUE;
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 ) )
467 xErrorOccurred = pdTRUE;
470 xRunIndicator = bktRUN_INDICATOR;
475 /*-----------------------------------------------------------*/
477 static void prvBasicDelayTests( void )
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;
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 );
487 /* Crude check to too see that vTaskDelay() blocks for the expected
489 xPreTime = xTaskGetTickCount();
490 vTaskDelay( bktTIME_TO_BLOCK );
491 xPostTime = xTaskGetTickCount();
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 ) )
497 xErrorOccurred = pdTRUE;
500 /* Now crude tests to check the vTaskDelayUntil() functionality. */
501 xPostTime = xTaskGetTickCount();
502 xLastUnblockTime = xPostTime;
504 for( x = 0; x < xCycles; x++ )
506 /* Calculate the next expected unblock time from the time taken before
507 * this loop was entered. */
508 xExpectedUnblockTime = xPostTime + ( x * xPeriod );
510 vTaskDelayUntil( &xLastUnblockTime, xPeriod );
512 if( ( xTaskGetTickCount() - xExpectedUnblockTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) )
514 xErrorOccurred = pdTRUE;
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 );
524 if( xDidBlock != pdTRUE )
526 xErrorOccurred = pdTRUE;
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 );
534 if( xDidBlock != pdTRUE )
536 xErrorOccurred = pdTRUE;
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 );
544 if( xDidBlock != pdFALSE )
546 xErrorOccurred = pdTRUE;
550 xDidBlock = xTaskDelayUntil( &xLastUnblockTime, xPeriod );
552 if( xDidBlock != pdTRUE )
554 xErrorOccurred = pdTRUE;
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 );
562 if( xDidBlock != pdFALSE )
564 xErrorOccurred = pdTRUE;
567 /* Reset to the original task priority ready for the other tests. */
568 vTaskPrioritySet( NULL, bktPRIMARY_PRIORITY );
570 /*-----------------------------------------------------------*/
572 BaseType_t xAreBlockTimeTestTasksStillRunning( void )
574 static BaseType_t xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;
575 BaseType_t xReturn = pdPASS;
577 /* Have both tasks performed at least one cycle since this function was
579 if( xPrimaryCycles == xLastPrimaryCycleCount )
584 if( xSecondaryCycles == xLastSecondaryCycleCount )
589 if( xErrorOccurred == pdTRUE )
594 xLastSecondaryCycleCount = xSecondaryCycles;
595 xLastPrimaryCycleCount = xPrimaryCycles;