2 * FreeRTOS Kernel V10.1.0
\r
3 * Copyright (C) 2018 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. 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 /* Task behaviour. */
\r
52 #define bktQUEUE_LENGTH ( 5 )
\r
53 #define bktSHORT_WAIT pdMS_TO_TICKS( ( TickType_t ) 20 )
\r
54 #define bktPRIMARY_BLOCK_TIME ( 10 )
\r
55 #define bktALLOWABLE_MARGIN ( 15 )
\r
56 #define bktTIME_TO_BLOCK ( 175 )
\r
57 #define bktDONT_BLOCK ( ( TickType_t ) 0 )
\r
58 #define bktRUN_INDICATOR ( ( UBaseType_t ) 0x55 )
\r
60 /* In case the demo does not have software timers enabled, as this file uses
\r
61 the configTIMER_TASK_PRIORITY setting. */
\r
62 #ifndef configTIMER_TASK_PRIORITY
\r
63 #define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 )
\r
66 /*-----------------------------------------------------------*/
\r
69 * The two test tasks. Their behaviour is commented within the functions.
\r
71 static void vPrimaryBlockTimeTestTask( void *pvParameters );
\r
72 static void vSecondaryBlockTimeTestTask( void *pvParameters );
\r
75 * Very basic tests to verify the block times are as expected.
\r
77 static void prvBasicDelayTests( void );
\r
79 /*-----------------------------------------------------------*/
\r
81 /* The queue on which the tasks block. */
\r
82 static QueueHandle_t xTestQueue;
\r
84 /* Handle to the secondary task is required by the primary task for calls
\r
85 to vTaskSuspend/Resume(). */
\r
86 static TaskHandle_t xSecondary;
\r
88 /* Used to ensure that tasks are still executing without error. */
\r
89 static volatile BaseType_t xPrimaryCycles = 0, xSecondaryCycles = 0;
\r
90 static volatile BaseType_t xErrorOccurred = pdFALSE;
\r
92 /* Provides a simple mechanism for the primary task to know when the
\r
93 secondary task has executed. */
\r
94 static volatile UBaseType_t xRunIndicator;
\r
96 /*-----------------------------------------------------------*/
\r
98 void vCreateBlockTimeTasks( void )
\r
100 /* Create the queue on which the two tasks block. */
\r
101 xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( BaseType_t ) );
\r
103 if( xTestQueue != NULL )
\r
105 /* vQueueAddToRegistry() adds the queue to the queue registry, if one
\r
106 is in use. The queue registry is provided as a means for kernel aware
\r
107 debuggers to locate queues and has no purpose if a kernel aware
\r
108 debugger is not being used. The call to vQueueAddToRegistry() will be
\r
109 removed by the pre-processor if configQUEUE_REGISTRY_SIZE is not
\r
110 defined or is defined to be less than 1. */
\r
111 vQueueAddToRegistry( xTestQueue, "Block_Time_Queue" );
\r
113 /* Create the two test tasks. */
\r
114 xTaskCreate( vPrimaryBlockTimeTestTask, "BTest1", configMINIMAL_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL );
\r
115 xTaskCreate( vSecondaryBlockTimeTestTask, "BTest2", configMINIMAL_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary );
\r
118 /*-----------------------------------------------------------*/
\r
120 static void vPrimaryBlockTimeTestTask( void *pvParameters )
\r
122 BaseType_t xItem, xData;
\r
123 TickType_t xTimeWhenBlocking;
\r
124 TickType_t xTimeToBlock, xBlockedTime;
\r
126 ( void ) pvParameters;
\r
130 /*********************************************************************
\r
133 Basic vTaskDelay() and vTaskDelayUntil() tests. */
\r
134 prvBasicDelayTests();
\r
137 /*********************************************************************
\r
140 Simple block time wakeup test on queue receives. */
\r
141 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
143 /* The queue is empty. Attempt to read from the queue using a block
\r
144 time. When we wake, ensure the delta in time is as expected. */
\r
145 xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem );
\r
147 xTimeWhenBlocking = xTaskGetTickCount();
\r
149 /* We should unblock after xTimeToBlock having not received
\r
150 anything on the queue. */
\r
151 if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )
\r
153 xErrorOccurred = pdTRUE;
\r
156 /* How long were we blocked for? */
\r
157 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
\r
159 if( xBlockedTime < xTimeToBlock )
\r
161 /* Should not have blocked for less than we requested. */
\r
162 xErrorOccurred = pdTRUE;
\r
165 if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
\r
167 /* Should not have blocked for longer than we requested,
\r
168 although we would not necessarily run as soon as we were
\r
169 unblocked so a margin is allowed. */
\r
170 xErrorOccurred = pdTRUE;
\r
174 /*********************************************************************
\r
177 Simple block time wakeup test on queue sends.
\r
179 First fill the queue. It should be empty so all sends should pass. */
\r
180 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
182 if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
\r
184 xErrorOccurred = pdTRUE;
\r
187 #if configUSE_PREEMPTION == 0
\r
192 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
194 /* The queue is full. Attempt to write to the queue using a block
\r
195 time. When we wake, ensure the delta in time is as expected. */
\r
196 xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem );
\r
198 xTimeWhenBlocking = xTaskGetTickCount();
\r
200 /* We should unblock after xTimeToBlock having not received
\r
201 anything on the queue. */
\r
202 if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )
\r
204 xErrorOccurred = pdTRUE;
\r
207 /* How long were we blocked for? */
\r
208 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
\r
210 if( xBlockedTime < xTimeToBlock )
\r
212 /* Should not have blocked for less than we requested. */
\r
213 xErrorOccurred = pdTRUE;
\r
216 if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
\r
218 /* Should not have blocked for longer than we requested,
\r
219 although we would not necessarily run as soon as we were
\r
220 unblocked so a margin is allowed. */
\r
221 xErrorOccurred = pdTRUE;
\r
225 /*********************************************************************
\r
228 Wake the other task, it will block attempting to post to the queue.
\r
229 When we read from the queue the other task will wake, but before it
\r
230 can run we will post to the queue again. When the other task runs it
\r
231 will find the queue still full, even though it was woken. It should
\r
232 recognise that its block time has not expired and return to block for
\r
233 the remains of its block time.
\r
235 Wake the other task so it blocks attempting to post to the already
\r
238 vTaskResume( xSecondary );
\r
240 /* We need to wait a little to ensure the other task executes. */
\r
241 while( xRunIndicator != bktRUN_INDICATOR )
\r
243 /* The other task has not yet executed. */
\r
244 vTaskDelay( bktSHORT_WAIT );
\r
246 /* Make sure the other task is blocked on the queue. */
\r
247 vTaskDelay( bktSHORT_WAIT );
\r
250 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
252 /* Now when we make space on the queue the other task should wake
\r
253 but not execute as this task has higher priority. */
\r
254 if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
\r
256 xErrorOccurred = pdTRUE;
\r
259 /* Now fill the queue again before the other task gets a chance to
\r
260 execute. If the other task had executed we would find the queue
\r
261 full ourselves, and the other task have set xRunIndicator. */
\r
262 if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
\r
264 xErrorOccurred = pdTRUE;
\r
267 if( xRunIndicator == bktRUN_INDICATOR )
\r
269 /* The other task should not have executed. */
\r
270 xErrorOccurred = pdTRUE;
\r
273 /* Raise the priority of the other task so it executes and blocks
\r
274 on the queue again. */
\r
275 vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
\r
277 /* The other task should now have re-blocked without exiting the
\r
279 if( xRunIndicator == bktRUN_INDICATOR )
\r
281 /* The other task should not have executed outside of the
\r
283 xErrorOccurred = pdTRUE;
\r
286 /* Set the priority back down. */
\r
287 vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
\r
290 /* Let the other task timeout. When it unblockes it will check that it
\r
291 unblocked at the correct time, then suspend itself. */
\r
292 while( xRunIndicator != bktRUN_INDICATOR )
\r
294 vTaskDelay( bktSHORT_WAIT );
\r
296 vTaskDelay( bktSHORT_WAIT );
\r
300 /*********************************************************************
\r
303 As per test 3 - but with the send and receive the other way around.
\r
304 The other task blocks attempting to read from the queue.
\r
306 Empty the queue. We should find that it is full. */
\r
307 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
309 if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
\r
311 xErrorOccurred = pdTRUE;
\r
315 /* Wake the other task so it blocks attempting to read from the
\r
316 already empty queue. */
\r
317 vTaskResume( xSecondary );
\r
319 /* We need to wait a little to ensure the other task executes. */
\r
320 while( xRunIndicator != bktRUN_INDICATOR )
\r
322 vTaskDelay( bktSHORT_WAIT );
\r
324 vTaskDelay( bktSHORT_WAIT );
\r
327 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
329 /* Now when we place an item on the queue the other task should
\r
330 wake but not execute as this task has higher priority. */
\r
331 if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
\r
333 xErrorOccurred = pdTRUE;
\r
336 /* Now empty the queue again before the other task gets a chance to
\r
337 execute. If the other task had executed we would find the queue
\r
338 empty ourselves, and the other task would be suspended. */
\r
339 if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
\r
341 xErrorOccurred = pdTRUE;
\r
344 if( xRunIndicator == bktRUN_INDICATOR )
\r
346 /* The other task should not have executed. */
\r
347 xErrorOccurred = pdTRUE;
\r
350 /* Raise the priority of the other task so it executes and blocks
\r
351 on the queue again. */
\r
352 vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
\r
354 /* The other task should now have re-blocked without exiting the
\r
356 if( xRunIndicator == bktRUN_INDICATOR )
\r
358 /* The other task should not have executed outside of the
\r
360 xErrorOccurred = pdTRUE;
\r
362 vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
\r
365 /* Let the other task timeout. When it unblockes it will check that it
\r
366 unblocked at the correct time, then suspend itself. */
\r
367 while( xRunIndicator != bktRUN_INDICATOR )
\r
369 vTaskDelay( bktSHORT_WAIT );
\r
371 vTaskDelay( bktSHORT_WAIT );
\r
376 /*-----------------------------------------------------------*/
\r
378 static void vSecondaryBlockTimeTestTask( void *pvParameters )
\r
380 TickType_t xTimeWhenBlocking, xBlockedTime;
\r
383 ( void ) pvParameters;
\r
387 /*********************************************************************
\r
390 This task does not participate in these tests. */
\r
391 vTaskSuspend( NULL );
\r
393 /*********************************************************************
\r
396 The first thing we do is attempt to read from the queue. It should be
\r
397 full so we block. Note the time before we block so we can check the
\r
398 wake time is as per that expected. */
\r
399 xTimeWhenBlocking = xTaskGetTickCount();
\r
401 /* We should unblock after bktTIME_TO_BLOCK having not sent anything to
\r
404 xRunIndicator = bktRUN_INDICATOR;
\r
405 if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )
\r
407 xErrorOccurred = pdTRUE;
\r
410 /* How long were we inside the send function? */
\r
411 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
\r
413 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */
\r
414 if( xBlockedTime < bktTIME_TO_BLOCK )
\r
416 xErrorOccurred = pdTRUE;
\r
419 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN
\r
420 either. A margin is permitted as we would not necessarily run as
\r
421 soon as we unblocked. */
\r
422 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )
\r
424 xErrorOccurred = pdTRUE;
\r
427 /* Suspend ready for test 3. */
\r
428 xRunIndicator = bktRUN_INDICATOR;
\r
429 vTaskSuspend( NULL );
\r
431 /*********************************************************************
\r
434 As per test three, but with the send and receive reversed. */
\r
435 xTimeWhenBlocking = xTaskGetTickCount();
\r
437 /* We should unblock after bktTIME_TO_BLOCK having not received
\r
438 anything on the queue. */
\r
439 xRunIndicator = bktRUN_INDICATOR;
\r
440 if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )
\r
442 xErrorOccurred = pdTRUE;
\r
445 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
\r
447 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */
\r
448 if( xBlockedTime < bktTIME_TO_BLOCK )
\r
450 xErrorOccurred = pdTRUE;
\r
453 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN
\r
454 either. A margin is permitted as we would not necessarily run as soon
\r
455 as we unblocked. */
\r
456 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )
\r
458 xErrorOccurred = pdTRUE;
\r
461 xRunIndicator = bktRUN_INDICATOR;
\r
463 xSecondaryCycles++;
\r
466 /*-----------------------------------------------------------*/
\r
468 static void prvBasicDelayTests( void )
\r
470 TickType_t xPreTime, xPostTime, x, xLastUnblockTime, xExpectedUnblockTime;
\r
471 const TickType_t xPeriod = 75, xCycles = 5, xAllowableMargin = ( bktALLOWABLE_MARGIN >> 1 );
\r
473 /* Temporarily increase priority so the timing is more accurate, but not so
\r
474 high as to disrupt the timer tests. */
\r
475 vTaskPrioritySet( NULL, configTIMER_TASK_PRIORITY - 1 );
\r
477 /* Crude check to too that vTaskDelay() blocks for the expected period. */
\r
478 xPreTime = xTaskGetTickCount();
\r
479 vTaskDelay( bktTIME_TO_BLOCK );
\r
480 xPostTime = xTaskGetTickCount();
\r
482 /* The priority is higher, so the allowable margin is halved when compared
\r
483 to the other tests in this file. */
\r
484 if( ( xPostTime - xPreTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) )
\r
486 xErrorOccurred = pdTRUE;
\r
489 /* Now crude tests to check the vTaskDelayUntil() functionality. */
\r
490 xPostTime = xTaskGetTickCount();
\r
491 xLastUnblockTime = xPostTime;
\r
493 for( x = 0; x < xCycles; x++ )
\r
495 /* Calculate the next expected unblock time from the time taken before
\r
496 this loop was entered. */
\r
497 xExpectedUnblockTime = xPostTime + ( x * xPeriod );
\r
499 vTaskDelayUntil( &xLastUnblockTime, xPeriod );
\r
501 if( ( xTaskGetTickCount() - xExpectedUnblockTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) )
\r
503 xErrorOccurred = pdTRUE;
\r
509 /* Reset to the original task priority ready for the other tests. */
\r
510 vTaskPrioritySet( NULL, bktPRIMARY_PRIORITY );
\r
512 /*-----------------------------------------------------------*/
\r
514 BaseType_t xAreBlockTimeTestTasksStillRunning( void )
\r
516 static BaseType_t xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;
\r
517 BaseType_t xReturn = pdPASS;
\r
519 /* Have both tasks performed at least one cycle since this function was
\r
521 if( xPrimaryCycles == xLastPrimaryCycleCount )
\r
526 if( xSecondaryCycles == xLastSecondaryCycleCount )
\r
531 if( xErrorOccurred == pdTRUE )
\r
536 xLastSecondaryCycleCount = xSecondaryCycles;
\r
537 xLastPrimaryCycleCount = xPrimaryCycles;
\r