]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Common/Minimal/IntSemTest.c
Demo tasks only, with the aim of improving test coverage:
[freertos] / FreeRTOS / Demo / Common / Minimal / IntSemTest.c
1 /*\r
2     FreeRTOS V8.1.2 - Copyright (C) 2014 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     ***************************************************************************\r
8      *                                                                       *\r
9      *    FreeRTOS provides completely free yet professionally developed,    *\r
10      *    robust, strictly quality controlled, supported, and cross          *\r
11      *    platform software that has become a de facto standard.             *\r
12      *                                                                       *\r
13      *    Help yourself get started quickly and support the FreeRTOS         *\r
14      *    project by purchasing a FreeRTOS tutorial book, reference          *\r
15      *    manual, or both from: http://www.FreeRTOS.org/Documentation        *\r
16      *                                                                       *\r
17      *    Thank you!                                                         *\r
18      *                                                                       *\r
19     ***************************************************************************\r
20 \r
21     This file is part of the FreeRTOS distribution.\r
22 \r
23     FreeRTOS is free software; you can redistribute it and/or modify it under\r
24     the terms of the GNU General Public License (version 2) as published by the\r
25     Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.\r
26 \r
27     >>!   NOTE: The modification to the GPL is included to allow you to     !<<\r
28     >>!   distribute a combined work that includes FreeRTOS without being   !<<\r
29     >>!   obliged to provide the source code for proprietary components     !<<\r
30     >>!   outside of the FreeRTOS kernel.                                   !<<\r
31 \r
32     FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY\r
33     WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\r
34     FOR A PARTICULAR PURPOSE.  Full license text is available from the following\r
35     link: http://www.freertos.org/a00114.html\r
36 \r
37     1 tab == 4 spaces!\r
38 \r
39     ***************************************************************************\r
40      *                                                                       *\r
41      *    Having a problem?  Start by reading the FAQ "My application does   *\r
42      *    not run, what could be wrong?"                                     *\r
43      *                                                                       *\r
44      *    http://www.FreeRTOS.org/FAQHelp.html                               *\r
45      *                                                                       *\r
46     ***************************************************************************\r
47 \r
48     http://www.FreeRTOS.org - Documentation, books, training, latest versions,\r
49     license and Real Time Engineers Ltd. contact details.\r
50 \r
51     http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,\r
52     including FreeRTOS+Trace - an indispensable productivity tool, a DOS\r
53     compatible FAT file system, and our tiny thread aware UDP/IP stack.\r
54 \r
55     http://www.OpenRTOS.com - Real Time Engineers ltd license FreeRTOS to High\r
56     Integrity Systems to sell under the OpenRTOS brand.  Low cost OpenRTOS\r
57     licenses offer ticketed support, indemnification and middleware.\r
58 \r
59     http://www.SafeRTOS.com - High Integrity Systems also provide a safety\r
60     engineered and independently SIL3 certified version for use in safety and\r
61     mission critical applications that require provable dependability.\r
62 \r
63     1 tab == 4 spaces!\r
64 */\r
65 \r
66 \r
67 /*\r
68  * Demonstrates and tests mutexes being used from an interrupt.\r
69  */\r
70 \r
71 \r
72 #include <stdlib.h>\r
73 \r
74 /* Scheduler include files. */\r
75 #include "FreeRTOS.h"\r
76 #include "task.h"\r
77 #include "semphr.h"\r
78 \r
79 /* Demo program include files. */\r
80 #include "IntSemTest.h"\r
81 \r
82 /*-----------------------------------------------------------*/\r
83 \r
84 /* The priorities of the test tasks. */\r
85 #define intsemMASTER_PRIORITY           ( tskIDLE_PRIORITY )\r
86 #define intsemSLAVE_PRIORITY            ( tskIDLE_PRIORITY + 1 )\r
87 \r
88 /* The rate at which the tick hook will give the mutex. */\r
89 #define intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ( 100 )\r
90 \r
91 /* A block time of 0 means 'don't block'. */\r
92 #define intsemNO_BLOCK                          0\r
93 \r
94 /*-----------------------------------------------------------*/\r
95 \r
96 /*\r
97  * The master is a task that receives a mutex that is given from an interrupt -\r
98  * although generally mutexes should not be used given in interrupts (and\r
99  * definitely never taken in an interrupt) there are some circumstances when it\r
100  * may be desirable.\r
101  *\r
102  * The slave task is just used by the master task to force priority inheritance\r
103  * on a mutex that is shared between the master and the slave - which is a\r
104  * separate mutex to that given by the interrupt.\r
105  */\r
106 static void vInterruptMutexSlaveTask( void *pvParameters );\r
107 static void vInterruptMutexMasterTask( void *pvParameters );\r
108 \r
109 /*-----------------------------------------------------------*/\r
110 \r
111 /* Flag that will be latched to pdTRUE should any unexpected behaviour be\r
112 detected in any of the tasks. */\r
113 static volatile BaseType_t xErrorDetected = pdFALSE;\r
114 \r
115 /* Counters that are incremented on each cycle of a test.  This is used to\r
116 detect a stalled task - a test that is no longer running. */\r
117 static volatile uint32_t ulMasterLoops = 0;\r
118 \r
119 /* Handles of the test tasks that must be accessed from other test tasks. */\r
120 static TaskHandle_t xSlaveHandle;\r
121 \r
122 /* A mutex which is given from an interrupt - although generally mutexes should\r
123 not be used given in interrupts (and definitely never taken in an interrupt)\r
124 there are some circumstances when it may be desirable. */\r
125 static SemaphoreHandle_t xISRMutex = NULL;\r
126 \r
127 /* A mutex which is shared between the master and slave tasks - the master\r
128 does both sharing of this mutex with the slave and receiving a mutex from the\r
129 interrupt. */\r
130 static SemaphoreHandle_t xMasterSlaveMutex = NULL;\r
131 \r
132 /* Flag that allows the master task to control when the interrupt gives or does\r
133 not give the mutex.  There is no mutual exclusion on this variable, but this is\r
134 only test code and it should be fine in the 32=bit test environment. */\r
135 static BaseType_t xOkToGiveMutex = pdFALSE;\r
136 \r
137 /*-----------------------------------------------------------*/\r
138 \r
139 void vStartInterruptSemaphoreTasks( void )\r
140 {\r
141         /* Create the mutex that is given from an interrupt. */\r
142         xISRMutex = xSemaphoreCreateMutex();\r
143         configASSERT( xISRMutex );\r
144 \r
145         /* Create the mutex that is shared between the master and slave tasks (the\r
146         master receives a mutex from an interrupt and shares a mutex with the\r
147         slave. */\r
148         xMasterSlaveMutex = xSemaphoreCreateMutex();\r
149         configASSERT( xMasterSlaveMutex );\r
150 \r
151         /* Create the tasks that share mutexes between then and with interrupts. */\r
152         xTaskCreate( vInterruptMutexSlaveTask, "IntMuS", configMINIMAL_STACK_SIZE, NULL, intsemSLAVE_PRIORITY, &xSlaveHandle );\r
153         xTaskCreate( vInterruptMutexMasterTask, "IntMuM", configMINIMAL_STACK_SIZE, NULL, intsemMASTER_PRIORITY, NULL );\r
154 }\r
155 /*-----------------------------------------------------------*/\r
156 \r
157 static void vInterruptMutexMasterTask( void *pvParameters )\r
158 {\r
159 const TickType_t xInterruptGivePeriod = pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS );\r
160 \r
161         /* Just to avoid compiler warnings. */\r
162         ( void ) pvParameters;\r
163 \r
164         for( ;; )\r
165         {\r
166                 /* Ensure the slave is suspended, and that this task is running at the\r
167                 lower priority as expected as the start conditions. */\r
168                 #if( INCLUDE_eTaskGetState == 1 )\r
169                 {\r
170                         configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended );\r
171                 }\r
172                 #endif /* INCLUDE_eTaskGetState */\r
173 \r
174                 if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY )\r
175                 {\r
176                         xErrorDetected = pdTRUE;\r
177                 }\r
178 \r
179                 /* Take the semaphore that is shared with the slave. */\r
180                 if( xSemaphoreTake( xMasterSlaveMutex, intsemNO_BLOCK ) != pdPASS )\r
181                 {\r
182                         xErrorDetected = pdTRUE;\r
183                 }\r
184 \r
185                 /* This task now has the mutex.  Unsuspend the slave so it too\r
186                 attempts to take the mutex. */\r
187                 vTaskResume( xSlaveHandle );\r
188 \r
189                 /* The slave has the higher priority so should now have executed and\r
190                 blocked on the semaphore. */\r
191                 #if( INCLUDE_eTaskGetState == 1 )\r
192                 {\r
193                         configASSERT( eTaskGetState( xSlaveHandle ) == eBlocked );\r
194                 }\r
195                 #endif /* INCLUDE_eTaskGetState */\r
196 \r
197                 /* This task should now have inherited the priority of the slave\r
198                 task. */\r
199                 if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )\r
200                 {\r
201                         xErrorDetected = pdTRUE;\r
202                 }\r
203 \r
204                 /* Now wait a little longer than the time between ISR gives to also\r
205                 obtain the ISR mutex. */\r
206                 xOkToGiveMutex = pdTRUE;\r
207                 if( xSemaphoreTake( xISRMutex, ( xInterruptGivePeriod * 2 ) ) != pdPASS )\r
208                 {\r
209                         xErrorDetected = pdTRUE;\r
210                 }\r
211                 xOkToGiveMutex = pdFALSE;\r
212 \r
213                 /* Attempting to take again immediately should fail as the mutex is\r
214                 already held. */\r
215                 if( xSemaphoreTake( xISRMutex, intsemNO_BLOCK ) != pdFAIL )\r
216                 {\r
217                         xErrorDetected = pdTRUE;\r
218                 }\r
219 \r
220                 /* Should still be at the priority of the slave task. */\r
221                 if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )\r
222                 {\r
223                         xErrorDetected = pdTRUE;\r
224                 }\r
225 \r
226                 /* Give back the ISR semaphore to ensure the priority is not\r
227                 disinherited as the shared mutex (which the higher priority task is\r
228                 attempting to obtain) is still held. */\r
229                 if( xSemaphoreGive( xISRMutex ) != pdPASS )\r
230                 {\r
231                         xErrorDetected = pdTRUE;\r
232                 }\r
233 \r
234                 if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )\r
235                 {\r
236                         xErrorDetected = pdTRUE;\r
237                 }\r
238 \r
239                 /* Finally give back the shared mutex.  This time the higher priority\r
240                 task should run before this task runs again - so this task should have\r
241                 disinherited the priority and the higher priority task should be in the\r
242                 suspended state again. */\r
243                 if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS )\r
244                 {\r
245                         xErrorDetected = pdTRUE;\r
246                 }\r
247 \r
248                 if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY )\r
249                 {\r
250                         xErrorDetected = pdTRUE;\r
251                 }\r
252 \r
253                 #if( INCLUDE_eTaskGetState == 1 )\r
254                 {\r
255                         configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended );\r
256                 }\r
257                 #endif /* INCLUDE_eTaskGetState */\r
258 \r
259                 /* Ensure not to starve out other tests. */\r
260                 ulMasterLoops++;\r
261                 vTaskDelay( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS );\r
262 \r
263 \r
264                 /* Repeat exactly up to the point where the mutexes are given back.\r
265                 This time the shared mutex is given back first. */\r
266                 if( xSemaphoreTake( xMasterSlaveMutex, intsemNO_BLOCK ) != pdPASS )\r
267                 {\r
268                         xErrorDetected = pdTRUE;\r
269                 }\r
270 \r
271                 vTaskResume( xSlaveHandle );\r
272 \r
273                 #if( INCLUDE_eTaskGetState == 1 )\r
274                 {\r
275                         configASSERT( eTaskGetState( xSlaveHandle ) == eBlocked );\r
276                 }\r
277                 #endif /* INCLUDE_eTaskGetState */\r
278 \r
279                 if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )\r
280                 {\r
281                         xErrorDetected = pdTRUE;\r
282                 }\r
283 \r
284                 xOkToGiveMutex = pdTRUE;\r
285                 if( xSemaphoreTake( xISRMutex, ( xInterruptGivePeriod * 2 ) ) != pdPASS )\r
286                 {\r
287                         xErrorDetected = pdTRUE;\r
288                 }\r
289                 xOkToGiveMutex = pdFALSE;\r
290 \r
291                 if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )\r
292                 {\r
293                         xErrorDetected = pdTRUE;\r
294                 }\r
295 \r
296                 /* This is where the differences start as this time the shared mutex is\r
297                 given back first.  This time to the higher priority task should run\r
298                 before this task gets to the point of releasing the interrupt mutex - so\r
299                 this task should have disinherited the priority and the higher priority\r
300                 task should be in the suspended state again. */\r
301                 if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS )\r
302                 {\r
303                         xErrorDetected = pdTRUE;\r
304                 }\r
305 \r
306                 /* Give back the interrupt semaphore too, so the mutex held count goes\r
307                 back to 0.  The mutex will then have to be reset so the ISR can give it\r
308                 in the next cycle. */\r
309                 xSemaphoreGive( xISRMutex );\r
310                 xQueueReset( ( QueueHandle_t ) xISRMutex );\r
311 \r
312                 /* Ensure not to starve out other tests. */\r
313                 ulMasterLoops++;\r
314                 vTaskDelay( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS );\r
315         }\r
316 }\r
317 /*-----------------------------------------------------------*/\r
318 \r
319 static void vInterruptMutexSlaveTask( void *pvParameters )\r
320 {\r
321         /* Just to avoid compiler warnings. */\r
322         ( void ) pvParameters;\r
323 \r
324         for( ;; )\r
325         {\r
326                 /* This task starts by suspending itself so when it executes can be\r
327                 controlled by the master task. */\r
328                 vTaskSuspend( NULL );\r
329 \r
330                 /* This task will execute when the master task already holds the mutex.\r
331                 Attempting to take the mutex will place this task in the Blocked\r
332                 state. */\r
333                 if( xSemaphoreTake( xMasterSlaveMutex, portMAX_DELAY ) != pdPASS )\r
334                 {\r
335                         xErrorDetected = pdTRUE;\r
336                 }\r
337 \r
338                 if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS )\r
339                 {\r
340                         xErrorDetected = pdTRUE;\r
341                 }\r
342         }\r
343 }\r
344 /*-----------------------------------------------------------*/\r
345 \r
346 void vInterruptSemaphorePeriodicTest( void )\r
347 {\r
348 static TickType_t xLastGiveTime = 0;\r
349 TickType_t xTimeNow;\r
350 \r
351         /* No mutual exclusion on xOkToGiveMutex, but this is only test code (and\r
352         only executed on a 32-bit architecture) so ignore that in this case. */\r
353         xTimeNow = xTaskGetTickCountFromISR();\r
354         if( ( xTimeNow - xLastGiveTime ) >= pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ) )\r
355         {\r
356                 configASSERT( xISRMutex );\r
357                 if( xOkToGiveMutex != pdFALSE )\r
358                 {\r
359                         xSemaphoreGiveFromISR( xISRMutex, NULL );\r
360 \r
361                         /* Second give attempt should fail. */\r
362                         configASSERT( xSemaphoreGiveFromISR( xISRMutex, NULL ) == pdFAIL );\r
363                 }\r
364                 xLastGiveTime = xTimeNow;\r
365         }\r
366 }\r
367 /*-----------------------------------------------------------*/\r
368 \r
369 /* This is called to check that all the created tasks are still running. */\r
370 BaseType_t xAreInterruptSemaphoreTasksStillRunning( void )\r
371 {\r
372 static uint32_t ulLastMasterLoopCounter = 0;\r
373 \r
374         /* If the demo tasks are running then it is expected that the loop counters\r
375         will have changed since this function was last called. */\r
376         if( ulLastMasterLoopCounter == ulMasterLoops )\r
377         {\r
378                 xErrorDetected = pdTRUE;\r
379         }\r
380 \r
381         ulLastMasterLoopCounter = ulMasterLoops;\r
382 \r
383         /* Errors detected in the task itself will have latched xErrorDetected\r
384         to true. */\r
385 \r
386         return ( BaseType_t ) !xErrorDetected;\r
387 }\r
388 \r
389 \r