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 ( ( ( TickType_t ) 20 ) / portTICK_PERIOD_MS )
\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 ( ( unsigned portBASE_TYPE ) 0x55 )
\r
60 /* The queue on which the tasks block. */
\r
61 static QueueHandle_t xTestQueue;
\r
63 /* Handle to the secondary task is required by the primary task for calls
\r
64 to vTaskSuspend/Resume(). */
\r
65 static TaskHandle_t xSecondary;
\r
67 /* Used to ensure that tasks are still executing without error. */
\r
68 static volatile portBASE_TYPE xPrimaryCycles = 0, xSecondaryCycles = 0;
\r
69 static volatile portBASE_TYPE xErrorOccurred = pdFALSE;
\r
71 /* Provides a simple mechanism for the primary task to know when the
\r
72 secondary task has executed. */
\r
73 static volatile unsigned portBASE_TYPE xRunIndicator;
\r
75 /* The two test tasks. Their behaviour is commented within the files. */
\r
76 static void vPrimaryBlockTimeTestTask( void *pvParameters );
\r
77 static void vSecondaryBlockTimeTestTask( void *pvParameters );
\r
79 /*-----------------------------------------------------------*/
\r
81 void vCreateBlockTimeTasks( void )
\r
83 /* Create the queue on which the two tasks block. */
\r
84 xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( portBASE_TYPE ) );
\r
86 /* vQueueAddToRegistry() adds the queue to the queue registry, if one is
\r
87 in use. The queue registry is provided as a means for kernel aware
\r
88 debuggers to locate queues and has no purpose if a kernel aware debugger
\r
89 is not being used. The call to vQueueAddToRegistry() will be removed
\r
90 by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is
\r
91 defined to be less than 1. */
\r
92 vQueueAddToRegistry( xTestQueue, ( signed char * ) "Block_Time_Queue" );
\r
94 /* Create the two test tasks. */
\r
95 xTaskCreate( vPrimaryBlockTimeTestTask, "BTest1", configMINIMAL_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL );
\r
96 xTaskCreate( vSecondaryBlockTimeTestTask, "BTest2", configMINIMAL_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary );
\r
98 /*-----------------------------------------------------------*/
\r
100 static void vPrimaryBlockTimeTestTask( void *pvParameters )
\r
102 portBASE_TYPE xItem, xData;
\r
103 TickType_t xTimeWhenBlocking;
\r
104 TickType_t xTimeToBlock, xBlockedTime;
\r
106 ( void ) pvParameters;
\r
110 /*********************************************************************
\r
113 Simple block time wakeup test on queue receives. */
\r
114 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
116 /* The queue is empty. Attempt to read from the queue using a block
\r
117 time. When we wake, ensure the delta in time is as expected. */
\r
118 xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;
\r
120 xTimeWhenBlocking = xTaskGetTickCount();
\r
122 /* We should unblock after xTimeToBlock having not received
\r
123 anything on the queue. */
\r
124 if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )
\r
126 xErrorOccurred = pdTRUE;
\r
129 /* How long were we blocked for? */
\r
130 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
\r
132 if( xBlockedTime < xTimeToBlock )
\r
134 /* Should not have blocked for less than we requested. */
\r
135 xErrorOccurred = pdTRUE;
\r
138 if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
\r
140 /* Should not have blocked for longer than we requested,
\r
141 although we would not necessarily run as soon as we were
\r
142 unblocked so a margin is allowed. */
\r
143 xErrorOccurred = pdTRUE;
\r
147 /*********************************************************************
\r
150 Simple block time wakeup test on queue sends.
\r
152 First fill the queue. It should be empty so all sends should pass. */
\r
153 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
155 if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
\r
157 xErrorOccurred = pdTRUE;
\r
160 #if configUSE_PREEMPTION == 0
\r
165 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
167 /* The queue is full. Attempt to write to the queue using a block
\r
168 time. When we wake, ensure the delta in time is as expected. */
\r
169 xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;
\r
171 xTimeWhenBlocking = xTaskGetTickCount();
\r
173 /* We should unblock after xTimeToBlock having not received
\r
174 anything on the queue. */
\r
175 if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )
\r
177 xErrorOccurred = pdTRUE;
\r
180 /* How long were we blocked for? */
\r
181 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
\r
183 if( xBlockedTime < xTimeToBlock )
\r
185 /* Should not have blocked for less than we requested. */
\r
186 xErrorOccurred = pdTRUE;
\r
189 if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )
\r
191 /* Should not have blocked for longer than we requested,
\r
192 although we would not necessarily run as soon as we were
\r
193 unblocked so a margin is allowed. */
\r
194 xErrorOccurred = pdTRUE;
\r
198 /*********************************************************************
\r
201 Wake the other task, it will block attempting to post to the queue.
\r
202 When we read from the queue the other task will wake, but before it
\r
203 can run we will post to the queue again. When the other task runs it
\r
204 will find the queue still full, even though it was woken. It should
\r
205 recognise that its block time has not expired and return to block for
\r
206 the remains of its block time.
\r
208 Wake the other task so it blocks attempting to post to the already
\r
211 vTaskResume( xSecondary );
\r
213 /* We need to wait a little to ensure the other task executes. */
\r
214 while( xRunIndicator != bktRUN_INDICATOR )
\r
216 /* The other task has not yet executed. */
\r
217 vTaskDelay( bktSHORT_WAIT );
\r
219 /* Make sure the other task is blocked on the queue. */
\r
220 vTaskDelay( bktSHORT_WAIT );
\r
223 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
225 /* Now when we make space on the queue the other task should wake
\r
226 but not execute as this task has higher priority. */
\r
227 if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
\r
229 xErrorOccurred = pdTRUE;
\r
232 /* Now fill the queue again before the other task gets a chance to
\r
233 execute. If the other task had executed we would find the queue
\r
234 full ourselves, and the other task have set xRunIndicator. */
\r
235 if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
\r
237 xErrorOccurred = pdTRUE;
\r
240 if( xRunIndicator == bktRUN_INDICATOR )
\r
242 /* The other task should not have executed. */
\r
243 xErrorOccurred = pdTRUE;
\r
246 /* Raise the priority of the other task so it executes and blocks
\r
247 on the queue again. */
\r
248 vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
\r
250 /* The other task should now have re-blocked without exiting the
\r
252 if( xRunIndicator == bktRUN_INDICATOR )
\r
254 /* The other task should not have executed outside of the
\r
256 xErrorOccurred = pdTRUE;
\r
259 /* Set the priority back down. */
\r
260 vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
\r
263 /* Let the other task timeout. When it unblockes it will check that it
\r
264 unblocked at the correct time, then suspend itself. */
\r
265 while( xRunIndicator != bktRUN_INDICATOR )
\r
267 vTaskDelay( bktSHORT_WAIT );
\r
269 vTaskDelay( bktSHORT_WAIT );
\r
273 /*********************************************************************
\r
276 As per test 3 - but with the send and receive the other way around.
\r
277 The other task blocks attempting to read from the queue.
\r
279 Empty the queue. We should find that it is full. */
\r
280 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
282 if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
\r
284 xErrorOccurred = pdTRUE;
\r
288 /* Wake the other task so it blocks attempting to read from the
\r
289 already empty queue. */
\r
290 vTaskResume( xSecondary );
\r
292 /* We need to wait a little to ensure the other task executes. */
\r
293 while( xRunIndicator != bktRUN_INDICATOR )
\r
295 vTaskDelay( bktSHORT_WAIT );
\r
297 vTaskDelay( bktSHORT_WAIT );
\r
300 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )
\r
302 /* Now when we place an item on the queue the other task should
\r
303 wake but not execute as this task has higher priority. */
\r
304 if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )
\r
306 xErrorOccurred = pdTRUE;
\r
309 /* Now empty the queue again before the other task gets a chance to
\r
310 execute. If the other task had executed we would find the queue
\r
311 empty ourselves, and the other task would be suspended. */
\r
312 if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )
\r
314 xErrorOccurred = pdTRUE;
\r
317 if( xRunIndicator == bktRUN_INDICATOR )
\r
319 /* The other task should not have executed. */
\r
320 xErrorOccurred = pdTRUE;
\r
323 /* Raise the priority of the other task so it executes and blocks
\r
324 on the queue again. */
\r
325 vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );
\r
327 /* The other task should now have re-blocked without exiting the
\r
329 if( xRunIndicator == bktRUN_INDICATOR )
\r
331 /* The other task should not have executed outside of the
\r
333 xErrorOccurred = pdTRUE;
\r
335 vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );
\r
338 /* Let the other task timeout. When it unblockes it will check that it
\r
339 unblocked at the correct time, then suspend itself. */
\r
340 while( xRunIndicator != bktRUN_INDICATOR )
\r
342 vTaskDelay( bktSHORT_WAIT );
\r
344 vTaskDelay( bktSHORT_WAIT );
\r
349 /*-----------------------------------------------------------*/
\r
351 static void vSecondaryBlockTimeTestTask( void *pvParameters )
\r
353 TickType_t xTimeWhenBlocking, xBlockedTime;
\r
354 portBASE_TYPE xData;
\r
356 ( void ) pvParameters;
\r
360 /*********************************************************************
\r
363 This task does does not participate in these tests. */
\r
364 vTaskSuspend( NULL );
\r
366 /*********************************************************************
\r
369 The first thing we do is attempt to read from the queue. It should be
\r
370 full so we block. Note the time before we block so we can check the
\r
371 wake time is as per that expected. */
\r
372 xTimeWhenBlocking = xTaskGetTickCount();
\r
374 /* We should unblock after bktTIME_TO_BLOCK having not sent
\r
375 anything to the queue. */
\r
377 xRunIndicator = bktRUN_INDICATOR;
\r
378 if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )
\r
380 xErrorOccurred = pdTRUE;
\r
383 /* How long were we inside the send function? */
\r
384 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
\r
386 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */
\r
387 if( xBlockedTime < bktTIME_TO_BLOCK )
\r
389 xErrorOccurred = pdTRUE;
\r
392 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN
\r
393 either. A margin is permitted as we would not necessarily run as
\r
394 soon as we unblocked. */
\r
395 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )
\r
397 xErrorOccurred = pdTRUE;
\r
400 /* Suspend ready for test 3. */
\r
401 xRunIndicator = bktRUN_INDICATOR;
\r
402 vTaskSuspend( NULL );
\r
404 /*********************************************************************
\r
407 As per test three, but with the send and receive reversed. */
\r
408 xTimeWhenBlocking = xTaskGetTickCount();
\r
410 /* We should unblock after bktTIME_TO_BLOCK having not received
\r
411 anything on the queue. */
\r
412 xRunIndicator = bktRUN_INDICATOR;
\r
413 if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )
\r
415 xErrorOccurred = pdTRUE;
\r
418 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;
\r
420 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */
\r
421 if( xBlockedTime < bktTIME_TO_BLOCK )
\r
423 xErrorOccurred = pdTRUE;
\r
426 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN
\r
427 either. A margin is permitted as we would not necessarily run as soon
\r
428 as we unblocked. */
\r
429 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )
\r
431 xErrorOccurred = pdTRUE;
\r
434 xRunIndicator = bktRUN_INDICATOR;
\r
436 xSecondaryCycles++;
\r
439 /*-----------------------------------------------------------*/
\r
441 portBASE_TYPE xAreBlockTimeTestTasksStillRunning( void )
\r
443 static portBASE_TYPE xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;
\r
444 portBASE_TYPE xReturn = pdPASS;
\r
446 /* Have both tasks performed at least one cycle since this function was
\r
448 if( xPrimaryCycles == xLastPrimaryCycleCount )
\r
453 if( xSecondaryCycles == xLastSecondaryCycleCount )
\r
458 if( xErrorOccurred == pdTRUE )
\r
463 xLastSecondaryCycleCount = xSecondaryCycles;
\r
464 xLastPrimaryCycleCount = xPrimaryCycles;
\r