]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Common/Minimal/blocktim.c
Update version numbers ready for release.
[freertos] / FreeRTOS / Demo / Common / Minimal / blocktim.c
1 /*\r
2  * FreeRTOS Kernel V10.1.1\r
3  * Copyright (C) 2018 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\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
11  *\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
14  *\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
21  *\r
22  * http://www.FreeRTOS.org\r
23  * http://aws.amazon.com/freertos\r
24  *\r
25  * 1 tab == 4 spaces!\r
26  */\r
27 \r
28 /*\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
32  */\r
33 \r
34 /* Kernel includes. */\r
35 #include "FreeRTOS.h"\r
36 #include "task.h"\r
37 #include "queue.h"\r
38 \r
39 /* Demo includes. */\r
40 #include "blocktim.h"\r
41 \r
42 /* Task priorities.  Allow these to be overridden. */\r
43 #ifndef bktPRIMARY_PRIORITY\r
44         #define bktPRIMARY_PRIORITY             ( configMAX_PRIORITIES - 3 )\r
45 #endif\r
46 \r
47 #ifndef bktSECONDARY_PRIORITY\r
48         #define bktSECONDARY_PRIORITY   ( configMAX_PRIORITIES - 4 )\r
49 #endif\r
50 \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
59 \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
64 #endif\r
65 \r
66 /*-----------------------------------------------------------*/\r
67 \r
68 /*\r
69  * The two test tasks.  Their behaviour is commented within the functions.\r
70  */\r
71 static void vPrimaryBlockTimeTestTask( void *pvParameters );\r
72 static void vSecondaryBlockTimeTestTask( void *pvParameters );\r
73 \r
74 /*\r
75  * Very basic tests to verify the block times are as expected.\r
76  */\r
77 static void prvBasicDelayTests( void );\r
78 \r
79 /*-----------------------------------------------------------*/\r
80 \r
81 /* The queue on which the tasks block. */\r
82 static QueueHandle_t xTestQueue;\r
83 \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
87 \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
91 \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
95 \r
96 /*-----------------------------------------------------------*/\r
97 \r
98 void vCreateBlockTimeTasks( void )\r
99 {\r
100         /* Create the queue on which the two tasks block. */\r
101         xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( BaseType_t ) );\r
102 \r
103         if( xTestQueue != NULL )\r
104         {\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
112 \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
116         }\r
117 }\r
118 /*-----------------------------------------------------------*/\r
119 \r
120 static void vPrimaryBlockTimeTestTask( void *pvParameters )\r
121 {\r
122 BaseType_t xItem, xData;\r
123 TickType_t xTimeWhenBlocking;\r
124 TickType_t xTimeToBlock, xBlockedTime;\r
125 \r
126         ( void ) pvParameters;\r
127 \r
128         for( ;; )\r
129         {\r
130                 /*********************************************************************\r
131                 Test 0\r
132 \r
133                 Basic vTaskDelay() and vTaskDelayUntil() tests. */\r
134                 prvBasicDelayTests();\r
135 \r
136 \r
137                 /*********************************************************************\r
138                 Test 1\r
139 \r
140                 Simple block time wakeup test on queue receives. */\r
141                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
142                 {\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
146 \r
147                         xTimeWhenBlocking = xTaskGetTickCount();\r
148 \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
152                         {\r
153                                 xErrorOccurred = pdTRUE;\r
154                         }\r
155 \r
156                         /* How long were we blocked for? */\r
157                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
158 \r
159                         if( xBlockedTime < xTimeToBlock )\r
160                         {\r
161                                 /* Should not have blocked for less than we requested. */\r
162                                 xErrorOccurred = pdTRUE;\r
163                         }\r
164 \r
165                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
166                         {\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
171                         }\r
172                 }\r
173 \r
174                 /*********************************************************************\r
175                 Test 2\r
176 \r
177                 Simple block time wakeup test on queue sends.\r
178 \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
181                 {\r
182                         if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
183                         {\r
184                                 xErrorOccurred = pdTRUE;\r
185                         }\r
186 \r
187                         #if configUSE_PREEMPTION == 0\r
188                                 taskYIELD();\r
189                         #endif\r
190                 }\r
191 \r
192                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
193                 {\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
197 \r
198                         xTimeWhenBlocking = xTaskGetTickCount();\r
199 \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
203                         {\r
204                                 xErrorOccurred = pdTRUE;\r
205                         }\r
206 \r
207                         /* How long were we blocked for? */\r
208                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
209 \r
210                         if( xBlockedTime < xTimeToBlock )\r
211                         {\r
212                                 /* Should not have blocked for less than we requested. */\r
213                                 xErrorOccurred = pdTRUE;\r
214                         }\r
215 \r
216                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
217                         {\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
222                         }\r
223                 }\r
224 \r
225                 /*********************************************************************\r
226                 Test 3\r
227 \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
234 \r
235                 Wake the other task so it blocks attempting to post to the already\r
236                 full queue. */\r
237                 xRunIndicator = 0;\r
238                 vTaskResume( xSecondary );\r
239 \r
240                 /* We need to wait a little to ensure the other task executes. */\r
241                 while( xRunIndicator != bktRUN_INDICATOR )\r
242                 {\r
243                         /* The other task has not yet executed. */\r
244                         vTaskDelay( bktSHORT_WAIT );\r
245                 }\r
246                 /* Make sure the other task is blocked on the queue. */\r
247                 vTaskDelay( bktSHORT_WAIT );\r
248                 xRunIndicator = 0;\r
249 \r
250                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
251                 {\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
255                         {\r
256                                 xErrorOccurred = pdTRUE;\r
257                         }\r
258 \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
263                         {\r
264                                 xErrorOccurred = pdTRUE;\r
265                         }\r
266 \r
267                         if( xRunIndicator == bktRUN_INDICATOR )\r
268                         {\r
269                                 /* The other task should not have executed. */\r
270                                 xErrorOccurred = pdTRUE;\r
271                         }\r
272 \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
276 \r
277                         /* The other task should now have re-blocked without exiting the\r
278                         queue function. */\r
279                         if( xRunIndicator == bktRUN_INDICATOR )\r
280                         {\r
281                                 /* The other task should not have executed outside of the\r
282                                 queue function. */\r
283                                 xErrorOccurred = pdTRUE;\r
284                         }\r
285 \r
286                         /* Set the priority back down. */\r
287                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );\r
288                 }\r
289 \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
293                 {\r
294                         vTaskDelay( bktSHORT_WAIT );\r
295                 }\r
296                 vTaskDelay( bktSHORT_WAIT );\r
297                 xRunIndicator = 0;\r
298 \r
299 \r
300                 /*********************************************************************\r
301                 Test 4\r
302 \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
305 \r
306                 Empty the queue.  We should find that it is full. */\r
307                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
308                 {\r
309                         if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
310                         {\r
311                                 xErrorOccurred = pdTRUE;\r
312                         }\r
313                 }\r
314 \r
315                 /* Wake the other task so it blocks attempting to read from  the\r
316                 already empty queue. */\r
317                 vTaskResume( xSecondary );\r
318 \r
319                 /* We need to wait a little to ensure the other task executes. */\r
320                 while( xRunIndicator != bktRUN_INDICATOR )\r
321                 {\r
322                         vTaskDelay( bktSHORT_WAIT );\r
323                 }\r
324                 vTaskDelay( bktSHORT_WAIT );\r
325                 xRunIndicator = 0;\r
326 \r
327                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
328                 {\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
332                         {\r
333                                 xErrorOccurred = pdTRUE;\r
334                         }\r
335 \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
340                         {\r
341                                 xErrorOccurred = pdTRUE;\r
342                         }\r
343 \r
344                         if( xRunIndicator == bktRUN_INDICATOR )\r
345                         {\r
346                                 /* The other task should not have executed. */\r
347                                 xErrorOccurred = pdTRUE;\r
348                         }\r
349 \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
353 \r
354                         /* The other task should now have re-blocked without exiting the\r
355                         queue function. */\r
356                         if( xRunIndicator == bktRUN_INDICATOR )\r
357                         {\r
358                                 /* The other task should not have executed outside of the\r
359                                 queue function. */\r
360                                 xErrorOccurred = pdTRUE;\r
361                         }\r
362                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );\r
363                 }\r
364 \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
368                 {\r
369                         vTaskDelay( bktSHORT_WAIT );\r
370                 }\r
371                 vTaskDelay( bktSHORT_WAIT );\r
372 \r
373                 xPrimaryCycles++;\r
374         }\r
375 }\r
376 /*-----------------------------------------------------------*/\r
377 \r
378 static void vSecondaryBlockTimeTestTask( void *pvParameters )\r
379 {\r
380 TickType_t xTimeWhenBlocking, xBlockedTime;\r
381 BaseType_t xData;\r
382 \r
383         ( void ) pvParameters;\r
384 \r
385         for( ;; )\r
386         {\r
387                 /*********************************************************************\r
388                 Test 0, 1 and 2\r
389 \r
390                 This task does not participate in these tests. */\r
391                 vTaskSuspend( NULL );\r
392 \r
393                 /*********************************************************************\r
394                 Test 3\r
395 \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
400 \r
401                 /* We should unblock after bktTIME_TO_BLOCK having not sent anything to\r
402                 the queue. */\r
403                 xData = 0;\r
404                 xRunIndicator = bktRUN_INDICATOR;\r
405                 if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )\r
406                 {\r
407                         xErrorOccurred = pdTRUE;\r
408                 }\r
409 \r
410                 /* How long were we inside the send function? */\r
411                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
412 \r
413                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
414                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
415                 {\r
416                         xErrorOccurred = pdTRUE;\r
417                 }\r
418 \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
423                 {\r
424                         xErrorOccurred = pdTRUE;\r
425                 }\r
426 \r
427                 /* Suspend ready for test 3. */\r
428                 xRunIndicator = bktRUN_INDICATOR;\r
429                 vTaskSuspend( NULL );\r
430 \r
431                 /*********************************************************************\r
432         Test 4\r
433 \r
434                 As per test three, but with the send and receive reversed. */\r
435                 xTimeWhenBlocking = xTaskGetTickCount();\r
436 \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
441                 {\r
442                         xErrorOccurred = pdTRUE;\r
443                 }\r
444 \r
445                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
446 \r
447                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
448                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
449                 {\r
450                         xErrorOccurred = pdTRUE;\r
451                 }\r
452 \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
457                 {\r
458                         xErrorOccurred = pdTRUE;\r
459                 }\r
460 \r
461                 xRunIndicator = bktRUN_INDICATOR;\r
462 \r
463                 xSecondaryCycles++;\r
464         }\r
465 }\r
466 /*-----------------------------------------------------------*/\r
467 \r
468 static void prvBasicDelayTests( void )\r
469 {\r
470 TickType_t xPreTime, xPostTime, x, xLastUnblockTime, xExpectedUnblockTime;\r
471 const TickType_t xPeriod = 75, xCycles = 5, xAllowableMargin = ( bktALLOWABLE_MARGIN >> 1 );\r
472 \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
476 \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
481 \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
485         {\r
486                 xErrorOccurred = pdTRUE;\r
487         }\r
488 \r
489         /* Now crude tests to check the vTaskDelayUntil() functionality. */\r
490         xPostTime = xTaskGetTickCount();\r
491         xLastUnblockTime = xPostTime;\r
492 \r
493         for( x = 0; x < xCycles; x++ )\r
494         {\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
498 \r
499                 vTaskDelayUntil( &xLastUnblockTime, xPeriod );\r
500 \r
501                 if( ( xTaskGetTickCount() - xExpectedUnblockTime ) > ( bktTIME_TO_BLOCK + xAllowableMargin ) )\r
502                 {\r
503                         xErrorOccurred = pdTRUE;\r
504                 }\r
505 \r
506                 xPrimaryCycles++;\r
507         }\r
508 \r
509         /* Reset to the original task priority ready for the other tests. */\r
510         vTaskPrioritySet( NULL, bktPRIMARY_PRIORITY );\r
511 }\r
512 /*-----------------------------------------------------------*/\r
513 \r
514 BaseType_t xAreBlockTimeTestTasksStillRunning( void )\r
515 {\r
516 static BaseType_t xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;\r
517 BaseType_t xReturn = pdPASS;\r
518 \r
519         /* Have both tasks performed at least one cycle since this function was\r
520         last called? */\r
521         if( xPrimaryCycles == xLastPrimaryCycleCount )\r
522         {\r
523                 xReturn = pdFAIL;\r
524         }\r
525 \r
526         if( xSecondaryCycles == xLastSecondaryCycleCount )\r
527         {\r
528                 xReturn = pdFAIL;\r
529         }\r
530 \r
531         if( xErrorOccurred == pdTRUE )\r
532         {\r
533                 xReturn = pdFAIL;\r
534         }\r
535 \r
536         xLastSecondaryCycleCount = xSecondaryCycles;\r
537         xLastPrimaryCycleCount = xPrimaryCycles;\r
538 \r
539         return xReturn;\r
540 }\r