2 * FreeRTOS Kernel V10.2.1
\r
3 * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
\r
5 * Permission is hereby granted, free of charge, to any person obtaining a copy of
\r
6 * this software and associated documentation files (the "Software"), to deal in
\r
7 * the Software without restriction, including without limitation the rights to
\r
8 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
\r
9 * the Software, and to permit persons to whom the Software is furnished to do so,
\r
10 * subject to the following conditions:
\r
12 * The above copyright notice and this permission notice shall be included in all
\r
13 * copies or substantial portions of the Software.
\r
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
\r
17 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
\r
18 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
\r
19 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
\r
20 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
22 * http://www.FreeRTOS.org
\r
23 * http://aws.amazon.com/freertos
\r
25 * 1 tab == 4 spaces!
\r
29 * This file contains some test scenarios that ensure tasks do not exit queue
\r
30 * send or receive functions prematurely. A description of the tests is
\r
31 * included within the code.
\r
34 /* Kernel includes. */
\r
35 #include "FreeRTOS.h"
\r
39 /* Demo includes. */
\r
40 #include "blocktim.h"
\r
42 /* Task priorities and stack sizes. Allow these to be overridden. */
\r
43 #ifndef bktPRIMARY_PRIORITY
\r
44 #define bktPRIMARY_PRIORITY ( configMAX_PRIORITIES - 3 )
\r
47 #ifndef bktSECONDARY_PRIORITY
\r
48 #define bktSECONDARY_PRIORITY ( configMAX_PRIORITIES - 4 )
\r
51 #ifndef bktBLOCK_TIME_TASK_STACK_SIZE
\r
52 #define bktBLOCK_TIME_TASK_STACK_SIZE configMINIMAL_STACK_SIZE
\r
55 /* Task behaviour. */
\r
56 #define bktQUEUE_LENGTH ( 5 )
\r
57 #define bktSHORT_WAIT pdMS_TO_TICKS( ( TickType_t ) 20 )
\r
58 #define bktPRIMARY_BLOCK_TIME ( 10 )
\r
59 #define bktALLOWABLE_MARGIN ( 15 )
\r
60 #define bktTIME_TO_BLOCK ( 175 )
\r
61 #define bktDONT_BLOCK ( ( TickType_t ) 0 )
\r
62 #define bktRUN_INDICATOR ( ( UBaseType_t ) 0x55 )
\r
64 /* In case the demo does not have software timers enabled, as this file uses
\r
65 the configTIMER_TASK_PRIORITY setting. */
\r
66 #ifndef configTIMER_TASK_PRIORITY
\r
67 #define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 )
\r
70 /*-----------------------------------------------------------*/
\r
73 * The two test tasks. Their behaviour is commented within the functions.
\r
75 static void vPrimaryBlockTimeTestTask( void *pvParameters );
\r
76 static void vSecondaryBlockTimeTestTask( void *pvParameters );
\r
79 * Very basic tests to verify the block times are as expected.
\r
81 static void prvBasicDelayTests( void );
\r
83 /*-----------------------------------------------------------*/
\r
85 /* The queue on which the tasks block. */
\r
86 static QueueHandle_t xTestQueue;
\r
88 /* Handle to the secondary task is required by the primary task for calls
\r
89 to vTaskSuspend/Resume(). */
\r
90 static TaskHandle_t xSecondary;
\r
92 /* Used to ensure that tasks are still executing without error. */
\r
93 static volatile BaseType_t xPrimaryCycles = 0, xSecondaryCycles = 0;
\r
94 static volatile BaseType_t xErrorOccurred = pdFALSE;
\r
96 /* Provides a simple mechanism for the primary task to know when the
\r
97 secondary task has executed. */
\r
98 static volatile UBaseType_t xRunIndicator;
\r
100 /*-----------------------------------------------------------*/
\r
102 void vCreateBlockTimeTasks( void )
\r
104 /* Create the queue on which the two tasks block. */
\r
105 xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( BaseType_t ) );
\r
107 if( xTestQueue != NULL )
\r
109 /* vQueueAddToRegistry() adds the queue to the queue registry, if one
\r
110 is in use. The queue registry is provided as a means for kernel aware
\r
111 debuggers to locate queues and has no purpose if a kernel aware
\r
112 debugger is not being used. The call to vQueueAddToRegistry() will be
\r
113 removed by the pre-processor if configQUEUE_REGISTRY_SIZE is not
\r
114 defined or is defined to be less than 1. */
\r
115 vQueueAddToRegistry( xTestQueue, "Block_Time_Queue" );
\r
117 /* Create the two test tasks. */
\r
118 xTaskCreate( vPrimaryBlockTimeTestTask, "BTest1", bktBLOCK_TIME_TASK_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL );
\r
119 xTaskCreate( vSecondaryBlockTimeTestTask, "BTest2", bktBLOCK_TIME_TASK_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary );
\r
122 /*-----------------------------------------------------------*/
\r
124 static void vPrimaryBlockTimeTestTask( void *pvParameters )
\r
126 BaseType_t xItem, xData;
\r
127 TickType_t xTimeWhenBlocking;
\r
128 TickType_t xTimeToBlock, xBlockedTime;
\r
130 ( void ) pvParameters;
\r
134 /*********************************************************************
\r
137 Basic vTaskDelay() and vTaskDelayUntil() tests. */
\r
138 prvBasicDelayTests();
\r
141 /*********************************************************************
\r
144 Simple block time wakeup test on queue receives. */
\r
145 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
147 /* The queue is empty. Attempt to read from the queue using a block
\r
148 time. When we wake, ensure the delta in time is as expected. */
\r
149 xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem );
\r
151 xTimeWhenBlocking = xTaskGetTickCount();
\r
153 /* We should unblock after xTimeToBlock having not received
\r
154 anything on the queue. */
\r
155 if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )
\r
157 xErrorOccurred = pdTRUE;
\r
160 /* How long were we blocked for? */
\r
161 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
\r
163 if( xBlockedTime < xTimeToBlock )
\r
165 /* Should not have blocked for less than we requested. */
\r
166 xErrorOccurred = pdTRUE;
\r
169 if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
\r
171 /* Should not have blocked for longer than we requested,
\r
172 although we would not necessarily run as soon as we were
\r
173 unblocked so a margin is allowed. */
\r
174 xErrorOccurred = pdTRUE;
\r
178 /*********************************************************************
\r
181 Simple block time wakeup test on queue sends.
\r
183 First fill the queue. It should be empty so all sends should pass. */
\r
184 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
186 if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
\r
188 xErrorOccurred = pdTRUE;
\r
191 #if configUSE_PREEMPTION == 0
\r
196 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
198 /* The queue is full. Attempt to write to the queue using a block
\r
199 time. When we wake, ensure the delta in time is as expected. */
\r
200 xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem );
\r
202 xTimeWhenBlocking = xTaskGetTickCount();
\r
204 /* We should unblock after xTimeToBlock having not received
\r
205 anything on the queue. */
\r
206 if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )
\r
208 xErrorOccurred = pdTRUE;
\r
211 /* How long were we blocked for? */
\r
212 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
\r
214 if( xBlockedTime < xTimeToBlock )
\r
216 /* Should not have blocked for less than we requested. */
\r
217 xErrorOccurred = pdTRUE;
\r
220 if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
\r
222 /* Should not have blocked for longer than we requested,
\r
223 although we would not necessarily run as soon as we were
\r
224 unblocked so a margin is allowed. */
\r
225 xErrorOccurred = pdTRUE;
\r
229 /*********************************************************************
\r
232 Wake the other task, it will block attempting to post to the queue.
\r
233 When we read from the queue the other task will wake, but before it
\r
234 can run we will post to the queue again. When the other task runs it
\r
235 will find the queue still full, even though it was woken. It should
\r
236 recognise that its block time has not expired and return to block for
\r
237 the remains of its block time.
\r
239 Wake the other task so it blocks attempting to post to the already
\r
242 vTaskResume( xSecondary );
\r
244 /* We need to wait a little to ensure the other task executes. */
\r
245 while( xRunIndicator != bktRUN_INDICATOR )
\r
247 /* The other task has not yet executed. */
\r
248 vTaskDelay( bktSHORT_WAIT );
\r
250 /* Make sure the other task is blocked on the queue. */
\r
251 vTaskDelay( bktSHORT_WAIT );
\r
254 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
256 /* Now when we make space on the queue the other task should wake
\r
257 but not execute as this task has higher priority. */
\r
258 if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
\r
260 xErrorOccurred = pdTRUE;
\r
263 /* Now fill the queue again before the other task gets a chance to
\r
264 execute. If the other task had executed we would find the queue
\r
265 full ourselves, and the other task have set xRunIndicator. */
\r
266 if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
\r
268 xErrorOccurred = pdTRUE;
\r
271 if( xRunIndicator == bktRUN_INDICATOR )
\r
273 /* The other task should not have executed. */
\r
274 xErrorOccurred = pdTRUE;
\r
277 /* Raise the priority of the other task so it executes and blocks
\r
278 on the queue again. */
\r
279 vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
\r
281 /* The other task should now have re-blocked without exiting the
\r
283 if( xRunIndicator == bktRUN_INDICATOR )
\r
285 /* The other task should not have executed outside of the
\r
287 xErrorOccurred = pdTRUE;
\r
290 /* Set the priority back down. */
\r
291 vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
\r
294 /* Let the other task timeout. When it unblockes it will check that it
\r
295 unblocked at the correct time, then suspend itself. */
\r
296 while( xRunIndicator != bktRUN_INDICATOR )
\r
298 vTaskDelay( bktSHORT_WAIT );
\r
300 vTaskDelay( bktSHORT_WAIT );
\r
304 /*********************************************************************
\r
307 As per test 3 - but with the send and receive the other way around.
\r
308 The other task blocks attempting to read from the queue.
\r
310 Empty the queue. We should find that it is full. */
\r
311 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
313 if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
\r
315 xErrorOccurred = pdTRUE;
\r
319 /* Wake the other task so it blocks attempting to read from the
\r
320 already empty queue. */
\r
321 vTaskResume( xSecondary );
\r
323 /* We need to wait a little to ensure the other task executes. */
\r
324 while( xRunIndicator != bktRUN_INDICATOR )
\r
326 vTaskDelay( bktSHORT_WAIT );
\r
328 vTaskDelay( bktSHORT_WAIT );
\r
331 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
333 /* Now when we place an item on the queue the other task should
\r
334 wake but not execute as this task has higher priority. */
\r
335 if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
\r
337 xErrorOccurred = pdTRUE;
\r
340 /* Now empty the queue again before the other task gets a chance to
\r
341 execute. If the other task had executed we would find the queue
\r
342 empty ourselves, and the other task would be suspended. */
\r
343 if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
\r
345 xErrorOccurred = pdTRUE;
\r
348 if( xRunIndicator == bktRUN_INDICATOR )
\r
350 /* The other task should not have executed. */
\r
351 xErrorOccurred = pdTRUE;
\r
354 /* Raise the priority of the other task so it executes and blocks
\r
355 on the queue again. */
\r
356 vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
\r
358 /* The other task should now have re-blocked without exiting the
\r
360 if( xRunIndicator == bktRUN_INDICATOR )
\r
362 /* The other task should not have executed outside of the
\r
364 xErrorOccurred = pdTRUE;
\r
366 vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
\r
369 /* Let the other task timeout. When it unblockes it will check that it
\r
370 unblocked at the correct time, then suspend itself. */
\r
371 while( xRunIndicator != bktRUN_INDICATOR )
\r
373 vTaskDelay( bktSHORT_WAIT );
\r
375 vTaskDelay( bktSHORT_WAIT );
\r
380 /*-----------------------------------------------------------*/
\r
382 static void vSecondaryBlockTimeTestTask( void *pvParameters )
\r
384 TickType_t xTimeWhenBlocking, xBlockedTime;
\r
387 ( void ) pvParameters;
\r
391 /*********************************************************************
\r
394 This task does not participate in these tests. */
\r
395 vTaskSuspend( NULL );
\r
397 /*********************************************************************
\r
400 The first thing we do is attempt to read from the queue. It should be
\r
401 full so we block. Note the time before we block so we can check the
\r
402 wake time is as per that expected. */
\r
403 xTimeWhenBlocking = xTaskGetTickCount();
\r
405 /* We should unblock after bktTIME_TO_BLOCK having not sent anything to
\r
408 xRunIndicator = bktRUN_INDICATOR;
\r
409 if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )
\r
411 xErrorOccurred = pdTRUE;
\r
414 /* How long were we inside the send function? */
\r
415 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
\r
417 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */
\r
418 if( xBlockedTime < bktTIME_TO_BLOCK )
\r
420 xErrorOccurred = pdTRUE;
\r
423 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN
\r
424 either. A margin is permitted as we would not necessarily run as
\r
425 soon as we unblocked. */
\r
426 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )
\r
428 xErrorOccurred = pdTRUE;
\r
431 /* Suspend ready for test 3. */
\r
432 xRunIndicator = bktRUN_INDICATOR;
\r
433 vTaskSuspend( NULL );
\r
435 /*********************************************************************
\r
438 As per test three, but with the send and receive reversed. */
\r
439 xTimeWhenBlocking = xTaskGetTickCount();
\r
441 /* We should unblock after bktTIME_TO_BLOCK having not received
\r
442 anything on the queue. */
\r
443 xRunIndicator = bktRUN_INDICATOR;
\r
444 if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )
\r
446 xErrorOccurred = pdTRUE;
\r
449 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
\r
451 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */
\r
452 if( xBlockedTime < bktTIME_TO_BLOCK )
\r
454 xErrorOccurred = pdTRUE;
\r
457 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN
\r
458 either. A margin is permitted as we would not necessarily run as soon
\r
459 as we unblocked. */
\r
460 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )
\r
462 xErrorOccurred = pdTRUE;
\r
465 xRunIndicator = bktRUN_INDICATOR;
\r
467 xSecondaryCycles++;
\r
470 /*-----------------------------------------------------------*/
\r
472 static void prvBasicDelayTests( void )
\r
474 TickType_t xPreTime, xPostTime, x, xLastUnblockTime, xExpectedUnblockTime;
\r
475 const TickType_t xPeriod = 75, xCycles = 5, xAllowableMargin = ( bktALLOWABLE_MARGIN >> 1 );
\r
477 /* Temporarily increase priority so the timing is more accurate, but not so
\r
478 high as to disrupt the timer tests. */
\r
479 vTaskPrioritySet( NULL, configTIMER_TASK_PRIORITY - 1 );
\r
481 /* Crude check to too see that vTaskDelay() blocks for the expected
\r
483 xPreTime = xTaskGetTickCount();
\r
484 vTaskDelay( bktTIME_TO_BLOCK );
\r
485 xPostTime = xTaskGetTickCount();
\r
487 /* The priority is higher, so the allowable margin is halved when compared
\r
488 to the other tests in this file. */
\r
489 if( ( xPostTime - xPreTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) )
\r
491 xErrorOccurred = pdTRUE;
\r
494 /* Now crude tests to check the vTaskDelayUntil() functionality. */
\r
495 xPostTime = xTaskGetTickCount();
\r
496 xLastUnblockTime = xPostTime;
\r
498 for( x = 0; x < xCycles; x++ )
\r
500 /* Calculate the next expected unblock time from the time taken before
\r
501 this loop was entered. */
\r
502 xExpectedUnblockTime = xPostTime + ( x * xPeriod );
\r
504 vTaskDelayUntil( &xLastUnblockTime, xPeriod );
\r
506 if( ( xTaskGetTickCount() - xExpectedUnblockTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) )
\r
508 xErrorOccurred = pdTRUE;
\r
514 /* Reset to the original task priority ready for the other tests. */
\r
515 vTaskPrioritySet( NULL, bktPRIMARY_PRIORITY );
\r
517 /*-----------------------------------------------------------*/
\r
519 BaseType_t xAreBlockTimeTestTasksStillRunning( void )
\r
521 static BaseType_t xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;
\r
522 BaseType_t xReturn = pdPASS;
\r
524 /* Have both tasks performed at least one cycle since this function was
\r
526 if( xPrimaryCycles == xLastPrimaryCycleCount )
\r
531 if( xSecondaryCycles == xLastSecondaryCycleCount )
\r
536 if( xErrorOccurred == pdTRUE )
\r
541 xLastSecondaryCycleCount = xSecondaryCycles;
\r
542 xLastPrimaryCycleCount = xPrimaryCycles;
\r