]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Common/Full/events.c
Update to MIT licensed FreeRTOS V10.0.0 - see https://www.freertos.org/History.txt
[freertos] / FreeRTOS / Demo / Common / Full / events.c
1 /*\r
2  * FreeRTOS Kernel V10.0.0\r
3  * Copyright (C) 2017 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. If you wish to use our Amazon\r
14  * FreeRTOS name, please do so in a fair use way that does not cause confusion.\r
15  *\r
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
18  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
19  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
22  *\r
23  * http://www.FreeRTOS.org\r
24  * http://aws.amazon.com/freertos\r
25  *\r
26  * 1 tab == 4 spaces!\r
27  */\r
28 \r
29 /**\r
30  * This file exercises the event mechanism whereby more than one task is\r
31  * blocked waiting for the same event.\r
32  *\r
33  * The demo creates five tasks - four 'event' tasks, and a controlling task.\r
34  * The event tasks have various different priorities and all block on reading\r
35  * the same queue.  The controlling task writes data to the queue, then checks\r
36  * to see which of the event tasks read the data from the queue.  The\r
37  * controlling task has the lowest priority of all the tasks so is guaranteed\r
38  * to always get preempted immediately upon writing to the queue.\r
39  *\r
40  * By selectively suspending and resuming the event tasks the controlling task\r
41  * can check that the highest priority task that is blocked on the queue is the\r
42  * task that reads the posted data from the queue.\r
43  *\r
44  * Two of the event tasks share the same priority.  When neither of these tasks\r
45  * are suspended they should alternate - one reading one message from the queue,\r
46  * the other the next message, etc.\r
47  */\r
48 \r
49 /* Standard includes. */\r
50 #include <stdlib.h>\r
51 #include <stdio.h>\r
52 #include <string.h>\r
53 \r
54 /* Scheduler include files. */\r
55 #include "FreeRTOS.h"\r
56 #include "task.h"\r
57 #include "queue.h"\r
58 \r
59 /* Demo program include files. */\r
60 #include "mevents.h"\r
61 #include "print.h"\r
62 \r
63 /* Demo specific constants. */\r
64 #define evtSTACK_SIZE           ( ( unsigned portBASE_TYPE ) configMINIMAL_STACK_SIZE )\r
65 #define evtNUM_TASKS            ( 4 )\r
66 #define evtQUEUE_LENGTH         ( ( unsigned portBASE_TYPE ) 3 )\r
67 #define evtNO_DELAY                                             0\r
68 \r
69 /* Just indexes used to uniquely identify the tasks.  Note that two tasks are\r
70 'highest' priority. */\r
71 #define evtHIGHEST_PRIORITY_INDEX_2             3\r
72 #define evtHIGHEST_PRIORITY_INDEX_1             2\r
73 #define evtMEDIUM_PRIORITY_INDEX                1\r
74 #define evtLOWEST_PRIORITY_INDEX                0\r
75 \r
76 /* Each event task increments one of these counters each time it reads data\r
77 from the queue. */\r
78 static volatile portBASE_TYPE xTaskCounters[ evtNUM_TASKS ] = { 0, 0, 0, 0 };\r
79 \r
80 /* Each time the controlling task posts onto the queue it increments the \r
81 expected count of the task that it expected to read the data from the queue \r
82 (i.e. the task with the highest priority that should be blocked on the queue).  \r
83 \r
84 xExpectedTaskCounters are incremented from the controlling task, and \r
85 xTaskCounters are incremented from the individual event tasks - therefore\r
86 comparing xTaskCounters to xExpectedTaskCounters shows whether or not the \r
87 correct task was unblocked by the post. */\r
88 static portBASE_TYPE xExpectedTaskCounters[ evtNUM_TASKS ] = { 0, 0, 0, 0 };\r
89 \r
90 /* Handles to the four event tasks.  These are required to suspend and resume\r
91 the tasks. */\r
92 static TaskHandle_t xCreatedTasks[ evtNUM_TASKS ];\r
93 \r
94 /* The single queue onto which the controlling task posts, and the four event\r
95 tasks block. */\r
96 static QueueHandle_t xQueue;\r
97 \r
98 /* Flag used to indicate whether or not an error has occurred at any time.\r
99 An error is either the queue being full when not expected, or an unexpected\r
100 task reading data from the queue. */\r
101 static portBASE_TYPE xHealthStatus = pdPASS;\r
102 \r
103 /*-----------------------------------------------------------*/\r
104 \r
105 /* Function that implements the event task.  This is created four times. */\r
106 static void prvMultiEventTask( void *pvParameters );\r
107 \r
108 /* Function that implements the controlling task. */\r
109 static void prvEventControllerTask( void *pvParameters );\r
110 \r
111 /* This is a utility function that posts data to the queue, then compares \r
112 xExpectedTaskCounters with xTaskCounters to ensure everything worked as \r
113 expected.\r
114 \r
115 The event tasks all have higher priorities the controlling task.  Therefore\r
116 the controlling task will always get preempted between writhing to the queue\r
117 and checking the task counters. \r
118 \r
119 @param xExpectedTask  The index to the task that the controlling task thinks\r
120                       should be the highest priority task waiting for data, and\r
121                                           therefore the task that will unblock.\r
122                                           \r
123 @param  xIncrement    The number of items that should be written to the queue.\r
124 */\r
125 static void prvCheckTaskCounters( portBASE_TYPE xExpectedTask, portBASE_TYPE xIncrement );\r
126 \r
127 /* This is just incremented each cycle of the controlling tasks function so\r
128 the main application can ensure the test is still running. */\r
129 static portBASE_TYPE xCheckVariable = 0;\r
130 \r
131 /*-----------------------------------------------------------*/\r
132 \r
133 void vStartMultiEventTasks( void )\r
134 {\r
135         /* Create the queue to be used for all the communications. */\r
136         xQueue = xQueueCreate( evtQUEUE_LENGTH, ( unsigned portBASE_TYPE ) sizeof( unsigned portBASE_TYPE ) );\r
137 \r
138         /* Start the controlling task.  This has the idle priority to ensure it is\r
139         always preempted by the event tasks. */\r
140         xTaskCreate( prvEventControllerTask, "EvntCTRL", evtSTACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );\r
141 \r
142         /* Start the four event tasks.  Note that two have priority 3, one \r
143         priority 2 and the other priority 1. */\r
144         xTaskCreate( prvMultiEventTask, "Event0", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 0 ] ), 1, &( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] ) );\r
145         xTaskCreate( prvMultiEventTask, "Event1", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 1 ] ), 2, &( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] ) );\r
146         xTaskCreate( prvMultiEventTask, "Event2", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 2 ] ), 3, &( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] ) );\r
147         xTaskCreate( prvMultiEventTask, "Event3", evtSTACK_SIZE, ( void * ) &( xTaskCounters[ 3 ] ), 3, &( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] ) );\r
148 }\r
149 /*-----------------------------------------------------------*/\r
150 \r
151 static void prvMultiEventTask( void *pvParameters )\r
152 {\r
153 portBASE_TYPE *pxCounter;\r
154 unsigned portBASE_TYPE uxDummy;\r
155 const char * const pcTaskStartMsg = "Multi event task started.\r\n";\r
156 \r
157         /* The variable this task will increment is passed in as a parameter. */\r
158         pxCounter = ( portBASE_TYPE * ) pvParameters;\r
159 \r
160         vPrintDisplayMessage( &pcTaskStartMsg );\r
161 \r
162         for( ;; )\r
163         {\r
164                 /* Block on the queue. */\r
165                 if( xQueueReceive( xQueue, &uxDummy, portMAX_DELAY ) )\r
166                 {\r
167                         /* We unblocked by reading the queue - so simply increment\r
168                         the counter specific to this task instance. */\r
169                         ( *pxCounter )++;\r
170                 }\r
171                 else\r
172                 {\r
173                         xHealthStatus = pdFAIL;\r
174                 }\r
175         }\r
176 }\r
177 /*-----------------------------------------------------------*/\r
178 \r
179 static void prvEventControllerTask( void *pvParameters )\r
180 {\r
181 const char * const pcTaskStartMsg = "Multi event controller task started.\r\n";\r
182 portBASE_TYPE xDummy = 0;\r
183 \r
184         /* Just to stop warnings. */\r
185         ( void ) pvParameters;\r
186 \r
187         vPrintDisplayMessage( &pcTaskStartMsg );\r
188 \r
189         for( ;; )\r
190         {\r
191                 /* All tasks are blocked on the queue.  When a message is posted one of\r
192                 the two tasks that share the highest priority should unblock to read\r
193                 the queue.  The next message written should unblock the other task with\r
194                 the same high priority, and so on in order.   No other task should \r
195                 unblock to read data as they have lower priorities. */\r
196 \r
197                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );\r
198                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_2, 1 );\r
199                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );\r
200                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_2, 1 );\r
201                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );\r
202 \r
203                 /* For the rest of these tests we don't need the second 'highest' \r
204                 priority task - so it is suspended. */\r
205                 vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] );\r
206 \r
207 \r
208 \r
209                 /* Now suspend the other highest priority task.  The medium priority \r
210                 task will then be the task with the highest priority that remains \r
211                 blocked on the queue. */\r
212                 vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
213                 \r
214                 /* This time, when we post onto the queue we will expect the medium\r
215                 priority task to unblock and preempt us. */\r
216                 prvCheckTaskCounters( evtMEDIUM_PRIORITY_INDEX, 1 );\r
217 \r
218                 /* Now try resuming the highest priority task while the scheduler is\r
219                 suspended.  The task should start executing as soon as the scheduler\r
220                 is resumed - therefore when we post to the queue again, the highest\r
221                 priority task should again preempt us. */\r
222                 vTaskSuspendAll();\r
223                         vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
224                 xTaskResumeAll();\r
225                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );\r
226                 \r
227                 /* Now we are going to suspend the high and medium priority tasks.  The\r
228                 low priority task should then preempt us.  Again the task suspension is \r
229                 done with the whole scheduler suspended just for test purposes. */\r
230                 vTaskSuspendAll();\r
231                         vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
232                         vTaskSuspend( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );\r
233                 xTaskResumeAll();\r
234                 prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, 1 );\r
235                 \r
236                 /* Do the same basic test another few times - selectively suspending\r
237                 and resuming tasks and each time calling prvCheckTaskCounters() passing\r
238                 to the function the number of the task we expected to be unblocked by \r
239                 the     post. */\r
240 \r
241                 vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
242                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );\r
243                 \r
244                 vTaskSuspendAll(); /* Just for test. */\r
245                         vTaskSuspendAll(); /* Just for test. */\r
246                                 vTaskSuspendAll(); /* Just for even more test. */\r
247                                         vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
248                                 xTaskResumeAll();\r
249                         xTaskResumeAll();\r
250                 xTaskResumeAll();\r
251                 prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, 1 );\r
252                 \r
253                 vTaskResume( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );\r
254                 prvCheckTaskCounters( evtMEDIUM_PRIORITY_INDEX, 1 );\r
255                 \r
256                 vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
257                 prvCheckTaskCounters( evtHIGHEST_PRIORITY_INDEX_1, 1 );\r
258 \r
259                 /* Now a slight change, first suspend all tasks. */\r
260                 vTaskSuspend( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
261                 vTaskSuspend( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );\r
262                 vTaskSuspend( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );\r
263                 \r
264                 /* Now when we resume the low priority task and write to the queue 3 \r
265                 times.  We expect the low priority task to service the queue three\r
266                 times. */\r
267                 vTaskResume( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );\r
268                 prvCheckTaskCounters( evtLOWEST_PRIORITY_INDEX, evtQUEUE_LENGTH );\r
269                 \r
270                 /* Again suspend all tasks (only the low priority task is not suspended\r
271                 already). */\r
272                 vTaskSuspend( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );\r
273                 \r
274                 /* This time we are going to suspend the scheduler, resume the low\r
275                 priority task, then resume the high priority task.  In this state we\r
276                 will write to the queue three times.  When the scheduler is resumed\r
277                 we expect the high priority task to service all three messages. */\r
278                 vTaskSuspendAll();\r
279                 {\r
280                         vTaskResume( xCreatedTasks[ evtLOWEST_PRIORITY_INDEX ] );\r
281                         vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_1 ] );\r
282                         \r
283                         for( xDummy = 0; xDummy < evtQUEUE_LENGTH; xDummy++ )\r
284                         {\r
285                                 if( xQueueSend( xQueue, &xDummy, evtNO_DELAY ) != pdTRUE )\r
286                                 {\r
287                                         xHealthStatus = pdFAIL;\r
288                                 }\r
289                         }                       \r
290                         \r
291                         /* The queue should not have been serviced yet!.  The scheduler\r
292                         is still suspended. */\r
293                         if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) )\r
294                         {\r
295                                 xHealthStatus = pdFAIL;\r
296                         }\r
297                 }\r
298                 xTaskResumeAll();\r
299 \r
300                 /* We should have been preempted by resuming the scheduler - so by the\r
301                 time we are running again we expect the high priority task to have \r
302                 removed three items from the queue. */\r
303                 xExpectedTaskCounters[ evtHIGHEST_PRIORITY_INDEX_1 ] += evtQUEUE_LENGTH;\r
304                 if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) )\r
305                 {\r
306                         xHealthStatus = pdFAIL;\r
307                 }\r
308                 \r
309                 /* The medium priority and second high priority tasks are still \r
310                 suspended.  Make sure to resume them before starting again. */\r
311                 vTaskResume( xCreatedTasks[ evtMEDIUM_PRIORITY_INDEX ] );\r
312                 vTaskResume( xCreatedTasks[ evtHIGHEST_PRIORITY_INDEX_2 ] );\r
313 \r
314                 /* Just keep incrementing to show the task is still executing. */\r
315                 xCheckVariable++;\r
316         }\r
317 }\r
318 /*-----------------------------------------------------------*/\r
319 \r
320 static void prvCheckTaskCounters( portBASE_TYPE xExpectedTask, portBASE_TYPE xIncrement )\r
321 {\r
322 portBASE_TYPE xDummy = 0;\r
323 \r
324         /* Write to the queue the requested number of times.  The data written is\r
325         not important. */\r
326         for( xDummy = 0; xDummy < xIncrement; xDummy++ )\r
327         {\r
328                 if( xQueueSend( xQueue, &xDummy, evtNO_DELAY ) != pdTRUE )\r
329                 {\r
330                         /* Did not expect to ever find the queue full. */\r
331                         xHealthStatus = pdFAIL;\r
332                 }\r
333         }\r
334 \r
335         /* All the tasks blocked on the queue have a priority higher than the \r
336         controlling task.  Writing to the queue will therefore have caused this\r
337         task to be preempted.  By the time this line executes the event task will\r
338         have executed and incremented its counter.  Increment the expected counter\r
339         to the same value. */\r
340         ( xExpectedTaskCounters[ xExpectedTask ] ) += xIncrement;\r
341 \r
342         /* Check the actual counts and expected counts really are the same. */\r
343         if( memcmp( ( void * ) xExpectedTaskCounters, ( void * ) xTaskCounters, sizeof( xExpectedTaskCounters ) ) )\r
344         {\r
345                 /* The counters were not the same.  This means a task we did not expect\r
346                 to unblock actually did unblock. */\r
347                 xHealthStatus = pdFAIL;\r
348         }\r
349 }\r
350 /*-----------------------------------------------------------*/\r
351 \r
352 portBASE_TYPE xAreMultiEventTasksStillRunning( void )\r
353 {\r
354 static portBASE_TYPE xPreviousCheckVariable = 0;\r
355 \r
356         /* Called externally to periodically check that this test is still\r
357         operational. */\r
358 \r
359         if( xPreviousCheckVariable == xCheckVariable )\r
360         {\r
361                 xHealthStatus = pdFAIL;\r
362         }\r
363         \r
364         xPreviousCheckVariable = xCheckVariable;\r
365         \r
366         return xHealthStatus;   \r
367 }\r
368 \r
369 \r