]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Common/Minimal/blocktim.c
068ea5965ce48a5fa4f1142969d2f0b3700a5b5f
[freertos] / FreeRTOS / Demo / Common / Minimal / blocktim.c
1 /*\r
2     FreeRTOS V8.2.2 - Copyright (C) 2015 Real Time Engineers Ltd.\r
3     All rights reserved\r
4 \r
5     VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.\r
6 \r
7     This file is part of the FreeRTOS distribution.\r
8 \r
9     FreeRTOS is free software; you can redistribute it and/or modify it under\r
10     the terms of the GNU General Public License (version 2) as published by the\r
11     Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.\r
12 \r
13     ***************************************************************************\r
14     >>!   NOTE: The modification to the GPL is included to allow you to     !<<\r
15     >>!   distribute a combined work that includes FreeRTOS without being   !<<\r
16     >>!   obliged to provide the source code for proprietary components     !<<\r
17     >>!   outside of the FreeRTOS kernel.                                   !<<\r
18     ***************************************************************************\r
19 \r
20     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY\r
21     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\r
22     FOR A PARTICULAR PURPOSE.  Full license text is available on the following\r
23     link: http://www.freertos.org/a00114.html\r
24 \r
25     ***************************************************************************\r
26      *                                                                       *\r
27      *    FreeRTOS provides completely free yet professionally developed,    *\r
28      *    robust, strictly quality controlled, supported, and cross          *\r
29      *    platform software that is more than just the market leader, it     *\r
30      *    is the industry's de facto standard.                               *\r
31      *                                                                       *\r
32      *    Help yourself get started quickly while simultaneously helping     *\r
33      *    to support the FreeRTOS project by purchasing a FreeRTOS           *\r
34      *    tutorial book, reference manual, or both:                          *\r
35      *    http://www.FreeRTOS.org/Documentation                              *\r
36      *                                                                       *\r
37     ***************************************************************************\r
38 \r
39     http://www.FreeRTOS.org/FAQHelp.html - Having a problem?  Start by reading\r
40     the FAQ page "My application does not run, what could be wrong?".  Have you\r
41     defined configASSERT()?\r
42 \r
43     http://www.FreeRTOS.org/support - In return for receiving this top quality\r
44     embedded software for free we request you assist our global community by\r
45     participating in the support forum.\r
46 \r
47     http://www.FreeRTOS.org/training - Investing in training allows your team to\r
48     be as productive as possible as early as possible.  Now you can receive\r
49     FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers\r
50     Ltd, and the world's leading authority on the world's leading RTOS.\r
51 \r
52     http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,\r
53     including FreeRTOS+Trace - an indispensable productivity tool, a DOS\r
54     compatible FAT file system, and our tiny thread aware UDP/IP stack.\r
55 \r
56     http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.\r
57     Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.\r
58 \r
59     http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High\r
60     Integrity Systems ltd. to sell under the OpenRTOS brand.  Low cost OpenRTOS\r
61     licenses offer ticketed support, indemnification and commercial middleware.\r
62 \r
63     http://www.SafeRTOS.com - High Integrity Systems also provide a safety\r
64     engineered and independently SIL3 certified version for use in safety and\r
65     mission critical applications that require provable dependability.\r
66 \r
67     1 tab == 4 spaces!\r
68 */\r
69 \r
70 /*\r
71  * This file contains some test scenarios that ensure tasks do not exit queue\r
72  * send or receive functions prematurely.  A description of the tests is\r
73  * included within the code.\r
74  */\r
75 \r
76 /* Kernel includes. */\r
77 #include "FreeRTOS.h"\r
78 #include "task.h"\r
79 #include "queue.h"\r
80 \r
81 /* Demo includes. */\r
82 #include "blocktim.h"\r
83 \r
84 /* Task priorities.  Allow these to be overridden. */\r
85 #ifndef bktPRIMARY_PRIORITY\r
86         #define bktPRIMARY_PRIORITY             ( configMAX_PRIORITIES - 3 )\r
87 #endif\r
88 \r
89 #ifndef bktSECONDARY_PRIORITY\r
90         #define bktSECONDARY_PRIORITY   ( configMAX_PRIORITIES - 4 )\r
91 #endif\r
92 \r
93 /* Task behaviour. */\r
94 #define bktQUEUE_LENGTH                         ( 5 )\r
95 #define bktSHORT_WAIT                           ( ( ( TickType_t ) 20 ) / portTICK_PERIOD_MS )\r
96 #define bktPRIMARY_BLOCK_TIME           ( 10 )\r
97 #define bktALLOWABLE_MARGIN                     ( 15 )\r
98 #define bktTIME_TO_BLOCK                        ( 175 )\r
99 #define bktDONT_BLOCK                           ( ( TickType_t ) 0 )\r
100 #define bktRUN_INDICATOR                        ( ( UBaseType_t ) 0x55 )\r
101 \r
102 /* The queue on which the tasks block. */\r
103 static QueueHandle_t xTestQueue;\r
104 \r
105 /* Handle to the secondary task is required by the primary task for calls\r
106 to vTaskSuspend/Resume(). */\r
107 static TaskHandle_t xSecondary;\r
108 \r
109 /* Used to ensure that tasks are still executing without error. */\r
110 static volatile BaseType_t xPrimaryCycles = 0, xSecondaryCycles = 0;\r
111 static volatile BaseType_t xErrorOccurred = pdFALSE;\r
112 \r
113 /* Provides a simple mechanism for the primary task to know when the\r
114 secondary task has executed. */\r
115 static volatile UBaseType_t xRunIndicator;\r
116 \r
117 /* The two test tasks.  Their behaviour is commented within the files. */\r
118 static void vPrimaryBlockTimeTestTask( void *pvParameters );\r
119 static void vSecondaryBlockTimeTestTask( void *pvParameters );\r
120 \r
121 /*-----------------------------------------------------------*/\r
122 \r
123 void vCreateBlockTimeTasks( void )\r
124 {\r
125         /* Create the queue on which the two tasks block. */\r
126     xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( BaseType_t ) );\r
127 \r
128         /* vQueueAddToRegistry() adds the queue to the queue registry, if one is\r
129         in use.  The queue registry is provided as a means for kernel aware\r
130         debuggers to locate queues and has no purpose if a kernel aware debugger\r
131         is not being used.  The call to vQueueAddToRegistry() will be removed\r
132         by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is\r
133         defined to be less than 1. */\r
134         vQueueAddToRegistry( xTestQueue, "Block_Time_Queue" );\r
135 \r
136         /* Create the two test tasks. */\r
137         xTaskCreate( vPrimaryBlockTimeTestTask, "BTest1", configMINIMAL_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL );\r
138         xTaskCreate( vSecondaryBlockTimeTestTask, "BTest2", configMINIMAL_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary );\r
139 }\r
140 /*-----------------------------------------------------------*/\r
141 \r
142 static void vPrimaryBlockTimeTestTask( void *pvParameters )\r
143 {\r
144 BaseType_t xItem, xData;\r
145 TickType_t xTimeWhenBlocking;\r
146 TickType_t xTimeToBlock, xBlockedTime;\r
147 \r
148         ( void ) pvParameters;\r
149 \r
150         for( ;; )\r
151         {\r
152                 /*********************************************************************\r
153         Test 1\r
154 \r
155         Simple block time wakeup test on queue receives. */\r
156                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
157                 {\r
158                         /* The queue is empty. Attempt to read from the queue using a block\r
159                         time.  When we wake, ensure the delta in time is as expected. */\r
160                         xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem );\r
161 \r
162                         xTimeWhenBlocking = xTaskGetTickCount();\r
163 \r
164                         /* We should unblock after xTimeToBlock having not received\r
165                         anything on the queue. */\r
166                         if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )\r
167                         {\r
168                                 xErrorOccurred = pdTRUE;\r
169                         }\r
170 \r
171                         /* How long were we blocked for? */\r
172                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
173 \r
174                         if( xBlockedTime < xTimeToBlock )\r
175                         {\r
176                                 /* Should not have blocked for less than we requested. */\r
177                                 xErrorOccurred = pdTRUE;\r
178                         }\r
179 \r
180                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
181                         {\r
182                                 /* Should not have blocked for longer than we requested,\r
183                                 although we would not necessarily run as soon as we were\r
184                                 unblocked so a margin is allowed. */\r
185                                 xErrorOccurred = pdTRUE;\r
186                         }\r
187                 }\r
188 \r
189                 /*********************************************************************\r
190         Test 2\r
191 \r
192         Simple block time wakeup test on queue sends.\r
193 \r
194                 First fill the queue.  It should be empty so all sends should pass. */\r
195                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
196                 {\r
197                         if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
198                         {\r
199                                 xErrorOccurred = pdTRUE;\r
200                         }\r
201 \r
202                         #if configUSE_PREEMPTION == 0\r
203                                 taskYIELD();\r
204                         #endif\r
205                 }\r
206 \r
207                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
208                 {\r
209                         /* The queue is full. Attempt to write to the queue using a block\r
210                         time.  When we wake, ensure the delta in time is as expected. */\r
211                         xTimeToBlock = ( TickType_t ) ( bktPRIMARY_BLOCK_TIME << xItem );\r
212 \r
213                         xTimeWhenBlocking = xTaskGetTickCount();\r
214 \r
215                         /* We should unblock after xTimeToBlock having not received\r
216                         anything on the queue. */\r
217                         if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )\r
218                         {\r
219                                 xErrorOccurred = pdTRUE;\r
220                         }\r
221 \r
222                         /* How long were we blocked for? */\r
223                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
224 \r
225                         if( xBlockedTime < xTimeToBlock )\r
226                         {\r
227                                 /* Should not have blocked for less than we requested. */\r
228                                 xErrorOccurred = pdTRUE;\r
229                         }\r
230 \r
231                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
232                         {\r
233                                 /* Should not have blocked for longer than we requested,\r
234                                 although we would not necessarily run as soon as we were\r
235                                 unblocked so a margin is allowed. */\r
236                                 xErrorOccurred = pdTRUE;\r
237                         }\r
238                 }\r
239 \r
240                 /*********************************************************************\r
241         Test 3\r
242 \r
243                 Wake the other task, it will block attempting to post to the queue.\r
244                 When we read from the queue the other task will wake, but before it\r
245                 can run we will post to the queue again.  When the other task runs it\r
246                 will find the queue still full, even though it was woken.  It should\r
247                 recognise that its block time has not expired and return to block for\r
248                 the remains of its block time.\r
249 \r
250                 Wake the other task so it blocks attempting to post to the already\r
251                 full queue. */\r
252                 xRunIndicator = 0;\r
253                 vTaskResume( xSecondary );\r
254 \r
255                 /* We need to wait a little to ensure the other task executes. */\r
256                 while( xRunIndicator != bktRUN_INDICATOR )\r
257                 {\r
258                         /* The other task has not yet executed. */\r
259                         vTaskDelay( bktSHORT_WAIT );\r
260                 }\r
261                 /* Make sure the other task is blocked on the queue. */\r
262                 vTaskDelay( bktSHORT_WAIT );\r
263                 xRunIndicator = 0;\r
264 \r
265                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
266                 {\r
267                         /* Now when we make space on the queue the other task should wake\r
268                         but not execute as this task has higher priority. */\r
269                         if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
270                         {\r
271                                 xErrorOccurred = pdTRUE;\r
272                         }\r
273 \r
274                         /* Now fill the queue again before the other task gets a chance to\r
275                         execute.  If the other task had executed we would find the queue\r
276                         full ourselves, and the other task have set xRunIndicator. */\r
277                         if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
278                         {\r
279                                 xErrorOccurred = pdTRUE;\r
280                         }\r
281 \r
282                         if( xRunIndicator == bktRUN_INDICATOR )\r
283                         {\r
284                                 /* The other task should not have executed. */\r
285                                 xErrorOccurred = pdTRUE;\r
286                         }\r
287 \r
288                         /* Raise the priority of the other task so it executes and blocks\r
289                         on the queue again. */\r
290                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
291 \r
292                         /* The other task should now have re-blocked without exiting the\r
293                         queue function. */\r
294                         if( xRunIndicator == bktRUN_INDICATOR )\r
295                         {\r
296                                 /* The other task should not have executed outside of the\r
297                                 queue function. */\r
298                                 xErrorOccurred = pdTRUE;\r
299                         }\r
300 \r
301                         /* Set the priority back down. */\r
302                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );\r
303                 }\r
304 \r
305                 /* Let the other task timeout.  When it unblockes it will check that it\r
306                 unblocked at the correct time, then suspend itself. */\r
307                 while( xRunIndicator != bktRUN_INDICATOR )\r
308                 {\r
309                         vTaskDelay( bktSHORT_WAIT );\r
310                 }\r
311                 vTaskDelay( bktSHORT_WAIT );\r
312                 xRunIndicator = 0;\r
313 \r
314 \r
315                 /*********************************************************************\r
316         Test 4\r
317 \r
318                 As per test 3 - but with the send and receive the other way around.\r
319                 The other task blocks attempting to read from the queue.\r
320 \r
321                 Empty the queue.  We should find that it is full. */\r
322                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
323                 {\r
324                         if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
325                         {\r
326                                 xErrorOccurred = pdTRUE;\r
327                         }\r
328                 }\r
329 \r
330                 /* Wake the other task so it blocks attempting to read from  the\r
331                 already empty queue. */\r
332                 vTaskResume( xSecondary );\r
333 \r
334                 /* We need to wait a little to ensure the other task executes. */\r
335                 while( xRunIndicator != bktRUN_INDICATOR )\r
336                 {\r
337                         vTaskDelay( bktSHORT_WAIT );\r
338                 }\r
339                 vTaskDelay( bktSHORT_WAIT );\r
340                 xRunIndicator = 0;\r
341 \r
342                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
343                 {\r
344                         /* Now when we place an item on the queue the other task should\r
345                         wake but not execute as this task has higher priority. */\r
346                         if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
347                         {\r
348                                 xErrorOccurred = pdTRUE;\r
349                         }\r
350 \r
351                         /* Now empty the queue again before the other task gets a chance to\r
352                         execute.  If the other task had executed we would find the queue\r
353                         empty ourselves, and the other task would be suspended. */\r
354                         if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
355                         {\r
356                                 xErrorOccurred = pdTRUE;\r
357                         }\r
358 \r
359                         if( xRunIndicator == bktRUN_INDICATOR )\r
360                         {\r
361                                 /* The other task should not have executed. */\r
362                                 xErrorOccurred = pdTRUE;\r
363                         }\r
364 \r
365                         /* Raise the priority of the other task so it executes and blocks\r
366                         on the queue again. */\r
367                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
368 \r
369                         /* The other task should now have re-blocked without exiting the\r
370                         queue function. */\r
371                         if( xRunIndicator == bktRUN_INDICATOR )\r
372                         {\r
373                                 /* The other task should not have executed outside of the\r
374                                 queue function. */\r
375                                 xErrorOccurred = pdTRUE;\r
376                         }\r
377                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );\r
378                 }\r
379 \r
380                 /* Let the other task timeout.  When it unblockes it will check that it\r
381                 unblocked at the correct time, then suspend itself. */\r
382                 while( xRunIndicator != bktRUN_INDICATOR )\r
383                 {\r
384                         vTaskDelay( bktSHORT_WAIT );\r
385                 }\r
386                 vTaskDelay( bktSHORT_WAIT );\r
387 \r
388                 xPrimaryCycles++;\r
389         }\r
390 }\r
391 /*-----------------------------------------------------------*/\r
392 \r
393 static void vSecondaryBlockTimeTestTask( void *pvParameters )\r
394 {\r
395 TickType_t xTimeWhenBlocking, xBlockedTime;\r
396 BaseType_t xData;\r
397 \r
398         ( void ) pvParameters;\r
399 \r
400         for( ;; )\r
401         {\r
402                 /*********************************************************************\r
403         Test 1 and 2\r
404 \r
405                 This task does does not participate in these tests. */\r
406                 vTaskSuspend( NULL );\r
407 \r
408                 /*********************************************************************\r
409         Test 3\r
410 \r
411                 The first thing we do is attempt to read from the queue.  It should be\r
412                 full so we block.  Note the time before we block so we can check the\r
413                 wake time is as per that expected. */\r
414                 xTimeWhenBlocking = xTaskGetTickCount();\r
415 \r
416                 /* We should unblock after bktTIME_TO_BLOCK having not sent\r
417                 anything to the queue. */\r
418                 xData = 0;\r
419                 xRunIndicator = bktRUN_INDICATOR;\r
420                 if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )\r
421                 {\r
422                         xErrorOccurred = pdTRUE;\r
423                 }\r
424 \r
425                 /* How long were we inside the send function? */\r
426                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
427 \r
428                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
429                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
430                 {\r
431                         xErrorOccurred = pdTRUE;\r
432                 }\r
433 \r
434                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN\r
435                 either.  A margin is permitted as we would not necessarily run as\r
436                 soon as we unblocked. */\r
437                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
438                 {\r
439                         xErrorOccurred = pdTRUE;\r
440                 }\r
441 \r
442                 /* Suspend ready for test 3. */\r
443                 xRunIndicator = bktRUN_INDICATOR;\r
444                 vTaskSuspend( NULL );\r
445 \r
446                 /*********************************************************************\r
447         Test 4\r
448 \r
449                 As per test three, but with the send and receive reversed. */\r
450                 xTimeWhenBlocking = xTaskGetTickCount();\r
451 \r
452                 /* We should unblock after bktTIME_TO_BLOCK having not received\r
453                 anything on the queue. */\r
454                 xRunIndicator = bktRUN_INDICATOR;\r
455                 if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )\r
456                 {\r
457                         xErrorOccurred = pdTRUE;\r
458                 }\r
459 \r
460                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
461 \r
462                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
463                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
464                 {\r
465                         xErrorOccurred = pdTRUE;\r
466                 }\r
467 \r
468                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN\r
469                 either.  A margin is permitted as we would not necessarily run as soon\r
470                 as we unblocked. */\r
471                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
472                 {\r
473                         xErrorOccurred = pdTRUE;\r
474                 }\r
475 \r
476                 xRunIndicator = bktRUN_INDICATOR;\r
477 \r
478                 xSecondaryCycles++;\r
479         }\r
480 }\r
481 /*-----------------------------------------------------------*/\r
482 \r
483 BaseType_t xAreBlockTimeTestTasksStillRunning( void )\r
484 {\r
485 static BaseType_t xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;\r
486 BaseType_t xReturn = pdPASS;\r
487 \r
488         /* Have both tasks performed at least one cycle since this function was\r
489         last called? */\r
490         if( xPrimaryCycles == xLastPrimaryCycleCount )\r
491         {\r
492                 xReturn = pdFAIL;\r
493         }\r
494 \r
495         if( xSecondaryCycles == xLastSecondaryCycleCount )\r
496         {\r
497                 xReturn = pdFAIL;\r
498         }\r
499 \r
500         if( xErrorOccurred == pdTRUE )\r
501         {\r
502                 xReturn = pdFAIL;\r
503         }\r
504 \r
505         xLastSecondaryCycleCount = xSecondaryCycles;\r
506         xLastPrimaryCycleCount = xPrimaryCycles;\r
507 \r
508         return xReturn;\r
509 }\r