]> git.sur5r.net Git - freertos/blob - Demo/Common/Minimal/blocktim.c
git-svn-id: https://svn.code.sf.net/p/freertos/code/trunk@82 1d2547de-c912-0410-9cb9...
[freertos] / Demo / Common / Minimal / blocktim.c
1 /*\r
2         FreeRTOS.org V4.3.0 - Copyright (C) 2003-2007 Richard Barry.\r
3 \r
4         This file is part of the FreeRTOS.org distribution.\r
5 \r
6         FreeRTOS.org is free software; you can redistribute it and/or modify\r
7         it under the terms of the GNU General Public License as published by\r
8         the Free Software Foundation; either version 2 of the License, or\r
9         (at your option) any later version.\r
10 \r
11         FreeRTOS.org is distributed in the hope that it will be useful,\r
12         but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14         GNU General Public License for more details.\r
15 \r
16         You should have received a copy of the GNU General Public License\r
17         along with FreeRTOS.org; if not, write to the Free Software\r
18         Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19 \r
20         A special exception to the GPL can be applied should you wish to distribute\r
21         a combined work that includes FreeRTOS.org, without being obliged to provide\r
22         the source code for any proprietary components.  See the licensing section \r
23         of http://www.FreeRTOS.org for full details of how and when the exception\r
24         can be applied.\r
25 \r
26         ***************************************************************************\r
27         See http://www.FreeRTOS.org for documentation, latest information, license \r
28         and contact details.  Please ensure to read the configuration and relevant \r
29         port sections of the online documentation.\r
30 \r
31         Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along\r
32         with commercial development and support options.\r
33         ***************************************************************************\r
34 */\r
35 \r
36 /*\r
37  * This file contains some test scenarios that ensure tasks do not exit queue\r
38  * send or receive functions prematurely.  A description of the tests is \r
39  * included within the code.\r
40  */\r
41 \r
42 /* Kernel includes. */\r
43 #include "FreeRTOS.h"\r
44 #include "task.h"\r
45 #include "queue.h"\r
46 \r
47 /* Task priorities. */\r
48 #define bktPRIMARY_PRIORITY                     ( 3 )\r
49 #define bktSECONDARY_PRIORITY           ( 2 )\r
50 \r
51 /* Task behaviour. */\r
52 #define bktQUEUE_LENGTH                         ( 5 )\r
53 #define bktSHORT_WAIT                           ( ( ( portTickType ) 20 ) / portTICK_RATE_MS )\r
54 #define bktPRIMARY_BLOCK_TIME           ( 10 )\r
55 #define bktALLOWABLE_MARGIN                     ( 12 )\r
56 #define bktTIME_TO_BLOCK                        ( 175 )\r
57 #define bktDONT_BLOCK                           ( ( portTickType ) 0 )\r
58 #define bktRUN_INDICATOR                        ( ( unsigned portBASE_TYPE ) 0x55 )\r
59 \r
60 /* The queue on which the tasks block. */\r
61 static xQueueHandle xTestQueue;\r
62 \r
63 /* Handle to the secondary task is required by the primary task for calls\r
64 to vTaskSuspend/Resume(). */\r
65 static xTaskHandle xSecondary;\r
66 \r
67 /* Used to ensure that tasks are still executing without error. */\r
68 static portBASE_TYPE xPrimaryCycles = 0, xSecondaryCycles = 0;\r
69 static portBASE_TYPE xErrorOccurred = pdFALSE;\r
70 \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
74 \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
78 \r
79 /*-----------------------------------------------------------*/\r
80 \r
81 void vCreateBlockTimeTasks( void )\r
82 {\r
83         /* Create the queue on which the two tasks block. */\r
84     xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( portBASE_TYPE ) );\r
85 \r
86         /* Create the two test tasks. */\r
87         xTaskCreate( vPrimaryBlockTimeTestTask, ( signed portCHAR * )"BTest1", configMINIMAL_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL );\r
88         xTaskCreate( vSecondaryBlockTimeTestTask, ( signed portCHAR * )"BTest2", configMINIMAL_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary );\r
89 }\r
90 /*-----------------------------------------------------------*/\r
91 \r
92 static void vPrimaryBlockTimeTestTask( void *pvParameters )\r
93 {\r
94 portBASE_TYPE xItem, xData;\r
95 portTickType xTimeWhenBlocking;\r
96 portTickType xTimeToBlock, xBlockedTime;\r
97 \r
98         for( ;; )\r
99         {\r
100                 /*********************************************************************\r
101         Test 1\r
102 \r
103         Simple block time wakeup test on queue receives. */\r
104                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
105                 {\r
106                         /* The queue is empty. Attempt to read from the queue using a block\r
107                         time.  When we wake, ensure the delta in time is as expected. */\r
108                         xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;\r
109 \r
110                         /* A critical section is used to minimise the jitter in the time\r
111                         measurements. */\r
112                         portENTER_CRITICAL();\r
113                         {\r
114                                 xTimeWhenBlocking = xTaskGetTickCount();\r
115                                 \r
116                                 /* We should unblock after xTimeToBlock having not received\r
117                                 anything on the queue. */\r
118                                 if( xQueueReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )\r
119                                 {\r
120                                         xErrorOccurred = pdTRUE;\r
121                                 }\r
122 \r
123                                 /* How long were we blocked for? */\r
124                                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
125                         }\r
126                         portEXIT_CRITICAL();\r
127 \r
128                         if( xBlockedTime < xTimeToBlock ) \r
129                         {\r
130                                 /* Should not have blocked for less than we requested. */\r
131                                 xErrorOccurred = pdTRUE;\r
132                         }\r
133 \r
134                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
135                         {\r
136                                 /* Should not have blocked for longer than we requested,\r
137                                 although we would not necessarily run as soon as we were \r
138                                 unblocked so a margin is allowed. */\r
139                                 xErrorOccurred = pdTRUE;\r
140                         }\r
141                 }\r
142 \r
143                 /*********************************************************************\r
144         Test 2\r
145 \r
146         Simple block time wakeup test on queue sends.\r
147 \r
148                 First fill the queue.  It should be empty so all sends should pass. */\r
149                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
150                 {\r
151                         if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
152                         {\r
153                                 xErrorOccurred = pdTRUE;\r
154                         }\r
155                 }\r
156 \r
157                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
158                 {\r
159                         /* The queue is full. Attempt to write to the queue using a block\r
160                         time.  When we wake, ensure the delta in time is as expected. */\r
161                         xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;\r
162 \r
163                         portENTER_CRITICAL();\r
164                         {\r
165                                 xTimeWhenBlocking = xTaskGetTickCount();\r
166                                 \r
167                                 /* We should unblock after xTimeToBlock having not received\r
168                                 anything on the queue. */\r
169                                 if( xQueueSend( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )\r
170                                 {\r
171                                         xErrorOccurred = pdTRUE;\r
172                                 }\r
173 \r
174                                 /* How long were we blocked for? */\r
175                                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
176                         }\r
177                         portEXIT_CRITICAL();\r
178 \r
179                         if( xBlockedTime < xTimeToBlock ) \r
180                         {\r
181                                 /* Should not have blocked for less than we requested. */\r
182                                 xErrorOccurred = pdTRUE;\r
183                         }\r
184 \r
185                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
186                         {\r
187                                 /* Should not have blocked for longer than we requested,\r
188                                 although we would not necessarily run as soon as we were \r
189                                 unblocked so a margin is allowed. */\r
190                                 xErrorOccurred = pdTRUE;\r
191                         }\r
192                 }\r
193 \r
194                 \r
195                 /*********************************************************************\r
196         Test 3\r
197 \r
198                 Wake the other task, it will block attempting to post to the queue.\r
199                 When we read from the queue the other task will wake, but before it\r
200                 can run we will post to the queue again.  When the other task runs it\r
201                 will find the queue still full, even though it was woken.  It should\r
202                 recognise that its block time has not expired and return to block for\r
203                 the remains of its block time.\r
204 \r
205                 Wake the other task so it blocks attempting to post to the already\r
206                 full queue. */\r
207                 xRunIndicator = 0;\r
208                 vTaskResume( xSecondary );\r
209 \r
210                 /* We need to wait a little to ensure the other task executes. */\r
211                 while( xRunIndicator != bktRUN_INDICATOR )\r
212                 {\r
213                         /* The other task has not yet executed. */\r
214                         vTaskDelay( bktSHORT_WAIT );\r
215                 }\r
216                 /* Make sure the other task is blocked on the queue. */\r
217                 vTaskDelay( bktSHORT_WAIT );\r
218                 xRunIndicator = 0;\r
219 \r
220                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
221                 {\r
222                         /* Now when we make space on the queue the other task should wake\r
223                         but not execute as this task has higher priority. */                            \r
224                         if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
225                         {\r
226                                 xErrorOccurred = pdTRUE;\r
227                         }\r
228 \r
229                         /* Now fill the queue again before the other task gets a chance to\r
230                         execute.  If the other task had executed we would find the queue \r
231                         full ourselves, and the other task have set xRunIndicator. */\r
232                         if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
233                         {\r
234                                 xErrorOccurred = pdTRUE;\r
235                         }\r
236 \r
237                         if( xRunIndicator == bktRUN_INDICATOR )\r
238                         {\r
239                                 /* The other task should not have executed. */\r
240                                 xErrorOccurred = pdTRUE;\r
241                         }\r
242 \r
243                         /* Raise the priority of the other task so it executes and blocks\r
244                         on the queue again. */\r
245                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
246 \r
247                         /* The other task should now have re-blocked without exiting the\r
248                         queue function. */\r
249                         if( xRunIndicator == bktRUN_INDICATOR )\r
250                         {\r
251                                 /* The other task should not have executed outside of the\r
252                                 queue function. */\r
253                                 xErrorOccurred = pdTRUE;\r
254                         }\r
255 \r
256                         /* Set the priority back down. */\r
257                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );                  \r
258                 }\r
259 \r
260                 /* Let the other task timeout.  When it unblockes it will check that it\r
261                 unblocked at the correct time, then suspend itself. */\r
262                 while( xRunIndicator != bktRUN_INDICATOR )\r
263                 {\r
264                         vTaskDelay( bktSHORT_WAIT );\r
265                 }\r
266                 vTaskDelay( bktSHORT_WAIT );\r
267                 xRunIndicator = 0;\r
268 \r
269 \r
270                 /*********************************************************************\r
271         Test 4\r
272 \r
273                 As per test 3 - but with the send and receive the other way around.  \r
274                 The other task blocks attempting to read from the queue.\r
275 \r
276                 Empty the queue.  We should find that it is full. */\r
277                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
278                 {\r
279                         if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
280                         {\r
281                                 xErrorOccurred = pdTRUE;\r
282                         }\r
283                 }\r
284                 \r
285                 /* Wake the other task so it blocks attempting to read from  the \r
286                 already empty queue. */\r
287                 vTaskResume( xSecondary );\r
288 \r
289                 /* We need to wait a little to ensure the other task executes. */\r
290                 while( xRunIndicator != bktRUN_INDICATOR )\r
291                 {\r
292                         vTaskDelay( bktSHORT_WAIT );\r
293                 }\r
294                 vTaskDelay( bktSHORT_WAIT );\r
295                 xRunIndicator = 0;\r
296 \r
297                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
298                 {\r
299                         /* Now when we place an item on the queue the other task should \r
300                         wake but not execute as this task has higher priority. */                               \r
301                         if( xQueueSend( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
302                         {\r
303                                 xErrorOccurred = pdTRUE;\r
304                         }\r
305 \r
306                         /* Now empty the queue again before the other task gets a chance to\r
307                         execute.  If the other task had executed we would find the queue \r
308                         empty ourselves, and the other task would be suspended. */\r
309                         if( xQueueReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
310                         {\r
311                                 xErrorOccurred = pdTRUE;\r
312                         }\r
313 \r
314                         if( xRunIndicator == bktRUN_INDICATOR )\r
315                         {\r
316                                 /* The other task should not have executed. */\r
317                                 xErrorOccurred = pdTRUE;\r
318                         }\r
319 \r
320                         /* Raise the priority of the other task so it executes and blocks\r
321                         on the queue again. */\r
322                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
323 \r
324                         /* The other task should now have re-blocked without exiting the \r
325                         queue function. */\r
326                         if( xRunIndicator == bktRUN_INDICATOR )\r
327                         {\r
328                                 /* The other task should not have executed outside of the\r
329                                 queue function. */\r
330                                 xErrorOccurred = pdTRUE;\r
331                         }\r
332                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );                  \r
333                 }\r
334 \r
335                 /* Let the other task timeout.  When it unblockes it will check that it\r
336                 unblocked at the correct time, then suspend itself. */\r
337                 while( xRunIndicator != bktRUN_INDICATOR )\r
338                 {\r
339                         vTaskDelay( bktSHORT_WAIT );\r
340                 }\r
341                 vTaskDelay( bktSHORT_WAIT );\r
342 \r
343                 xPrimaryCycles++;\r
344         }\r
345 }\r
346 /*-----------------------------------------------------------*/\r
347 \r
348 static void vSecondaryBlockTimeTestTask( void *pvParameters )\r
349 {\r
350 portTickType xTimeWhenBlocking, xBlockedTime;\r
351 portBASE_TYPE xData;\r
352 \r
353         for( ;; )\r
354         {\r
355                 /*********************************************************************\r
356         Test 1 and 2\r
357 \r
358                 This task does does not participate in these tests. */\r
359                 vTaskSuspend( NULL );\r
360 \r
361                 /*********************************************************************\r
362         Test 3\r
363 \r
364                 The first thing we do is attempt to read from the queue.  It should be\r
365                 full so we block.  Note the time before we block so we can check the\r
366                 wake time is as per that expected. */\r
367                 portENTER_CRITICAL();\r
368                 {\r
369                         xTimeWhenBlocking = xTaskGetTickCount();\r
370                         \r
371                         /* We should unblock after bktTIME_TO_BLOCK having not received\r
372                         anything on the queue. */\r
373                         xData = 0;\r
374                         xRunIndicator = bktRUN_INDICATOR;\r
375                         if( xQueueSend( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )\r
376                         {\r
377                                 xErrorOccurred = pdTRUE;\r
378                         }\r
379 \r
380                         /* How long were we inside the send function? */\r
381                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
382                 }\r
383                 portEXIT_CRITICAL();\r
384 \r
385                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
386                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
387                 {\r
388                         xErrorOccurred = pdTRUE;\r
389                 }\r
390 \r
391                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN \r
392                 either.  A margin is permitted as we would not necessarily run as\r
393                 soon as we unblocked. */\r
394                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
395                 {\r
396                         xErrorOccurred = pdTRUE;\r
397                 }\r
398 \r
399                 /* Suspend ready for test 3. */\r
400                 xRunIndicator = bktRUN_INDICATOR;\r
401                 vTaskSuspend( NULL );\r
402 \r
403                 /*********************************************************************\r
404         Test 4\r
405 \r
406                 As per test three, but with the send and receive reversed. */\r
407                 portENTER_CRITICAL();\r
408                 {\r
409                         xTimeWhenBlocking = xTaskGetTickCount();\r
410                         \r
411                         /* We should unblock after bktTIME_TO_BLOCK having not received\r
412                         anything on the queue. */\r
413                         xRunIndicator = bktRUN_INDICATOR;\r
414                         if( xQueueReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )\r
415                         {\r
416                                 xErrorOccurred = pdTRUE;\r
417                         }\r
418 \r
419                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
420                 }\r
421                 portEXIT_CRITICAL();\r
422 \r
423                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
424                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
425                 {\r
426                         xErrorOccurred = pdTRUE;\r
427                 }\r
428 \r
429                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN \r
430                 either.  A margin is permitted as we would not necessarily run as soon\r
431                 as we unblocked. */\r
432                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
433                 {\r
434                         xErrorOccurred = pdTRUE;\r
435                 }\r
436 \r
437                 xRunIndicator = bktRUN_INDICATOR;\r
438 \r
439                 xSecondaryCycles++;\r
440         }\r
441 }\r
442 /*-----------------------------------------------------------*/\r
443 \r
444 portBASE_TYPE xAreBlockTimeTestTasksStillRunning( void )\r
445 {\r
446 static portBASE_TYPE xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;\r
447 portBASE_TYPE xReturn = pdPASS;\r
448 \r
449         /* Have both tasks performed at least one cycle since this function was\r
450         last called? */\r
451         if( xPrimaryCycles == xLastPrimaryCycleCount )\r
452         {\r
453                 xReturn = pdFAIL;\r
454         }\r
455 \r
456         if( xSecondaryCycles == xLastSecondaryCycleCount )\r
457         {\r
458                 xReturn = pdFAIL;\r
459         }\r
460 \r
461         if( xErrorOccurred == pdTRUE )\r
462         {\r
463                 xReturn = pdFAIL;\r
464         }\r
465 \r
466         xLastSecondaryCycleCount = xSecondaryCycles;\r
467         xLastPrimaryCycleCount = xPrimaryCycles;\r
468 \r
469         return xReturn;\r
470 }\r