]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Common/Minimal/IntSemTest.c
effedfad889e6f7ab338297c567c8512cb26a190
[freertos] / FreeRTOS / Demo / Common / Minimal / IntSemTest.c
1 /*\r
2  * FreeRTOS Kernel V10.3.0\r
3  * Copyright (C) 2020 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 /*\r
30  * Demonstrates and tests mutexes being used from an interrupt.\r
31  */\r
32 \r
33 \r
34 #include <stdlib.h>\r
35 \r
36 /* Scheduler include files. */\r
37 #include "FreeRTOS.h"\r
38 #include "task.h"\r
39 #include "semphr.h"\r
40 \r
41 /* Demo program include files. */\r
42 #include "IntSemTest.h"\r
43 \r
44 /*-----------------------------------------------------------*/\r
45 \r
46 /* The priorities of the test tasks. */\r
47 #define intsemMASTER_PRIORITY           ( tskIDLE_PRIORITY )\r
48 #define intsemSLAVE_PRIORITY            ( tskIDLE_PRIORITY + 1 )\r
49 \r
50 /* The rate at which the tick hook will give the mutex. */\r
51 #define intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ( 100 )\r
52 \r
53 /* A block time of 0 means 'don't block'. */\r
54 #define intsemNO_BLOCK                          0\r
55 \r
56 /* The maximum count value for the counting semaphore given from an\r
57 interrupt. */\r
58 #define intsemMAX_COUNT                         3\r
59 \r
60 /*-----------------------------------------------------------*/\r
61 \r
62 /*\r
63  * The master is a task that receives a mutex that is given from an interrupt -\r
64  * although generally mutexes should not be used given in interrupts (and\r
65  * definitely never taken in an interrupt) there are some circumstances when it\r
66  * may be desirable.\r
67  *\r
68  * The slave task is just used by the master task to force priority inheritance\r
69  * on a mutex that is shared between the master and the slave - which is a\r
70  * separate mutex to that given by the interrupt.\r
71  */\r
72 static void vInterruptMutexSlaveTask( void *pvParameters );\r
73 static void vInterruptMutexMasterTask( void *pvParameters );\r
74 \r
75 /*\r
76  * A test whereby the master takes the shared and interrupt mutexes in that\r
77  * order, then gives them back in the same order, ensuring the priority\r
78  * inheritance is behaving as expected at each step.\r
79  */\r
80 static void prvTakeAndGiveInTheSameOrder( void );\r
81 \r
82 /*\r
83  * A test whereby the master takes the shared and interrupt mutexes in that\r
84  * order, then gives them back in the opposite order to which they were taken,\r
85  * ensuring the priority inheritance is behaving as expected at each step.\r
86  */\r
87 static void prvTakeAndGiveInTheOppositeOrder( void );\r
88 \r
89 /*\r
90  * A simple task that interacts with an interrupt using a counting semaphore,\r
91  * primarily for code coverage purposes.\r
92  */\r
93 static void vInterruptCountingSemaphoreTask( void *pvParameters );\r
94 \r
95 /*-----------------------------------------------------------*/\r
96 \r
97 /* Flag that will be latched to pdTRUE should any unexpected behaviour be\r
98 detected in any of the tasks. */\r
99 static volatile BaseType_t xErrorDetected = pdFALSE;\r
100 \r
101 /* Counters that are incremented on each cycle of a test.  This is used to\r
102 detect a stalled task - a test that is no longer running. */\r
103 static volatile uint32_t ulMasterLoops = 0, ulCountingSemaphoreLoops = 0;\r
104 \r
105 /* Handles of the test tasks that must be accessed from other test tasks. */\r
106 static TaskHandle_t xSlaveHandle;\r
107 \r
108 /* A mutex which is given from an interrupt - although generally mutexes should\r
109 not be used given in interrupts (and definitely never taken in an interrupt)\r
110 there are some circumstances when it may be desirable. */\r
111 static SemaphoreHandle_t xISRMutex = NULL;\r
112 \r
113 /* A counting semaphore which is given from an interrupt. */\r
114 static SemaphoreHandle_t xISRCountingSemaphore = NULL;\r
115 \r
116 /* A mutex which is shared between the master and slave tasks - the master\r
117 does both sharing of this mutex with the slave and receiving a mutex from the\r
118 interrupt. */\r
119 static SemaphoreHandle_t xMasterSlaveMutex = NULL;\r
120 \r
121 /* Flag that allows the master task to control when the interrupt gives or does\r
122 not give the mutex.  There is no mutual exclusion on this variable, but this is\r
123 only test code and it should be fine in the 32=bit test environment. */\r
124 static BaseType_t xOkToGiveMutex = pdFALSE, xOkToGiveCountingSemaphore = pdFALSE;\r
125 \r
126 /* Used to coordinate timing between tasks and the interrupt. */\r
127 const TickType_t xInterruptGivePeriod = pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS );\r
128 \r
129 /*-----------------------------------------------------------*/\r
130 \r
131 void vStartInterruptSemaphoreTasks( void )\r
132 {\r
133         /* Create the semaphores that are given from an interrupt. */\r
134         xISRMutex = xSemaphoreCreateMutex();\r
135         configASSERT( xISRMutex );\r
136         xISRCountingSemaphore = xSemaphoreCreateCounting( intsemMAX_COUNT, 0 );\r
137         configASSERT( xISRCountingSemaphore );\r
138 \r
139         /* Create the mutex that is shared between the master and slave tasks (the\r
140         master receives a mutex from an interrupt and shares a mutex with the\r
141         slave. */\r
142         xMasterSlaveMutex = xSemaphoreCreateMutex();\r
143         configASSERT( xMasterSlaveMutex );\r
144 \r
145         /* Create the tasks that share mutexes between then and with interrupts. */\r
146         xTaskCreate( vInterruptMutexSlaveTask, "IntMuS", configMINIMAL_STACK_SIZE, NULL, intsemSLAVE_PRIORITY, &xSlaveHandle );\r
147         xTaskCreate( vInterruptMutexMasterTask, "IntMuM", configMINIMAL_STACK_SIZE, NULL, intsemMASTER_PRIORITY, NULL );\r
148 \r
149         /* Create the task that blocks on the counting semaphore. */\r
150         xTaskCreate( vInterruptCountingSemaphoreTask, "IntCnt", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );\r
151 }\r
152 /*-----------------------------------------------------------*/\r
153 \r
154 static void vInterruptMutexMasterTask( void *pvParameters )\r
155 {\r
156         /* Just to avoid compiler warnings. */\r
157         ( void ) pvParameters;\r
158 \r
159         for( ;; )\r
160         {\r
161                 prvTakeAndGiveInTheSameOrder();\r
162 \r
163                 /* Ensure not to starve out other tests. */\r
164                 ulMasterLoops++;\r
165                 vTaskDelay( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS );\r
166 \r
167                 prvTakeAndGiveInTheOppositeOrder();\r
168 \r
169                 /* Ensure not to starve out other tests. */\r
170                 ulMasterLoops++;\r
171                 vTaskDelay( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS );\r
172         }\r
173 }\r
174 /*-----------------------------------------------------------*/\r
175 \r
176 static void prvTakeAndGiveInTheSameOrder( void )\r
177 {\r
178         /* Ensure the slave is suspended, and that this task is running at the\r
179         lower priority as expected as the start conditions. */\r
180         #if( INCLUDE_eTaskGetState == 1 )\r
181         {\r
182                 configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended );\r
183         }\r
184         #endif /* INCLUDE_eTaskGetState */\r
185 \r
186         if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY )\r
187         {\r
188                 xErrorDetected = pdTRUE;\r
189         }\r
190 \r
191         /* Take the semaphore that is shared with the slave. */\r
192         if( xSemaphoreTake( xMasterSlaveMutex, intsemNO_BLOCK ) != pdPASS )\r
193         {\r
194                 xErrorDetected = pdTRUE;\r
195         }\r
196 \r
197         /* This task now has the mutex.  Unsuspend the slave so it too\r
198         attempts to take the mutex. */\r
199         vTaskResume( xSlaveHandle );\r
200 \r
201         /* The slave has the higher priority so should now have executed and\r
202         blocked on the semaphore. */\r
203         #if( INCLUDE_eTaskGetState == 1 )\r
204         {\r
205                 configASSERT( eTaskGetState( xSlaveHandle ) == eBlocked );\r
206         }\r
207         #endif /* INCLUDE_eTaskGetState */\r
208 \r
209         /* This task should now have inherited the priority of the slave\r
210         task. */\r
211         if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )\r
212         {\r
213                 xErrorDetected = pdTRUE;\r
214         }\r
215 \r
216         /* Now wait a little longer than the time between ISR gives to also\r
217         obtain the ISR mutex. */\r
218         xOkToGiveMutex = pdTRUE;\r
219         if( xSemaphoreTake( xISRMutex, ( xInterruptGivePeriod * 2 ) ) != pdPASS )\r
220         {\r
221                 xErrorDetected = pdTRUE;\r
222         }\r
223         xOkToGiveMutex = pdFALSE;\r
224 \r
225         /* Attempting to take again immediately should fail as the mutex is\r
226         already held. */\r
227         if( xSemaphoreTake( xISRMutex, intsemNO_BLOCK ) != pdFAIL )\r
228         {\r
229                 xErrorDetected = pdTRUE;\r
230         }\r
231 \r
232         /* Should still be at the priority of the slave task. */\r
233         if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )\r
234         {\r
235                 xErrorDetected = pdTRUE;\r
236         }\r
237 \r
238         /* Give back the ISR semaphore to ensure the priority is not\r
239         disinherited as the shared mutex (which the higher priority task is\r
240         attempting to obtain) is still held. */\r
241         if( xSemaphoreGive( xISRMutex ) != pdPASS )\r
242         {\r
243                 xErrorDetected = pdTRUE;\r
244         }\r
245 \r
246         if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )\r
247         {\r
248                 xErrorDetected = pdTRUE;\r
249         }\r
250 \r
251         /* Finally give back the shared mutex.  This time the higher priority\r
252         task should run before this task runs again - so this task should have\r
253         disinherited the priority and the higher priority task should be in the\r
254         suspended state again. */\r
255         if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS )\r
256         {\r
257                 xErrorDetected = pdTRUE;\r
258         }\r
259 \r
260         if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY )\r
261         {\r
262                 xErrorDetected = pdTRUE;\r
263         }\r
264 \r
265         #if( INCLUDE_eTaskGetState == 1 )\r
266         {\r
267                 configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended );\r
268         }\r
269         #endif /* INCLUDE_eTaskGetState */\r
270 \r
271         /* Reset the mutex ready for the next round. */\r
272         xQueueReset( xISRMutex );\r
273 }\r
274 /*-----------------------------------------------------------*/\r
275 \r
276 static void prvTakeAndGiveInTheOppositeOrder( void )\r
277 {\r
278         /* Ensure the slave is suspended, and that this task is running at the\r
279         lower priority as expected as the start conditions. */\r
280         #if( INCLUDE_eTaskGetState == 1 )\r
281         {\r
282                 configASSERT( eTaskGetState( xSlaveHandle ) == eSuspended );\r
283         }\r
284         #endif /* INCLUDE_eTaskGetState */\r
285 \r
286         if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY )\r
287         {\r
288                 xErrorDetected = pdTRUE;\r
289         }\r
290 \r
291         /* Take the semaphore that is shared with the slave. */\r
292         if( xSemaphoreTake( xMasterSlaveMutex, intsemNO_BLOCK ) != pdPASS )\r
293         {\r
294                 xErrorDetected = pdTRUE;\r
295         }\r
296 \r
297         /* This task now has the mutex.  Unsuspend the slave so it too\r
298         attempts to take the mutex. */\r
299         vTaskResume( xSlaveHandle );\r
300 \r
301         /* The slave has the higher priority so should now have executed and\r
302         blocked on the semaphore. */\r
303         #if( INCLUDE_eTaskGetState == 1 )\r
304         {\r
305                 configASSERT( eTaskGetState( xSlaveHandle ) == eBlocked );\r
306         }\r
307         #endif /* INCLUDE_eTaskGetState */\r
308 \r
309         /* This task should now have inherited the priority of the slave\r
310         task. */\r
311         if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )\r
312         {\r
313                 xErrorDetected = pdTRUE;\r
314         }\r
315 \r
316         /* Now wait a little longer than the time between ISR gives to also\r
317         obtain the ISR mutex. */\r
318         xOkToGiveMutex = pdTRUE;\r
319         if( xSemaphoreTake( xISRMutex, ( xInterruptGivePeriod * 2 ) ) != pdPASS )\r
320         {\r
321                 xErrorDetected = pdTRUE;\r
322         }\r
323         xOkToGiveMutex = pdFALSE;\r
324 \r
325         /* Attempting to take again immediately should fail as the mutex is\r
326         already held. */\r
327         if( xSemaphoreTake( xISRMutex, intsemNO_BLOCK ) != pdFAIL )\r
328         {\r
329                 xErrorDetected = pdTRUE;\r
330         }\r
331 \r
332         /* Should still be at the priority of the slave task. */\r
333         if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )\r
334         {\r
335                 xErrorDetected = pdTRUE;\r
336         }\r
337 \r
338         /* Give back the shared semaphore to ensure the priority is not disinherited\r
339         as the ISR mutex is still held.  The higher priority slave task should run\r
340         before this task runs again. */\r
341         if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS )\r
342         {\r
343                 xErrorDetected = pdTRUE;\r
344         }\r
345 \r
346         /* Should still be at the priority of the slave task as this task still\r
347         holds one semaphore (this is a simplification in the priority inheritance\r
348         mechanism. */\r
349         if( uxTaskPriorityGet( NULL ) != intsemSLAVE_PRIORITY )\r
350         {\r
351                 xErrorDetected = pdTRUE;\r
352         }\r
353 \r
354         /* Give back the ISR semaphore, which should result in the priority being\r
355         disinherited as it was the last mutex held. */\r
356         if( xSemaphoreGive( xISRMutex ) != pdPASS )\r
357         {\r
358                 xErrorDetected = pdTRUE;\r
359         }\r
360 \r
361         if( uxTaskPriorityGet( NULL ) != intsemMASTER_PRIORITY )\r
362         {\r
363                 xErrorDetected = pdTRUE;\r
364         }\r
365 \r
366         /* Reset the mutex ready for the next round. */\r
367         xQueueReset( xISRMutex );\r
368 }\r
369 /*-----------------------------------------------------------*/\r
370 \r
371 static void vInterruptMutexSlaveTask( void *pvParameters )\r
372 {\r
373         /* Just to avoid compiler warnings. */\r
374         ( void ) pvParameters;\r
375 \r
376         for( ;; )\r
377         {\r
378                 /* This task starts by suspending itself so when it executes can be\r
379                 controlled by the master task. */\r
380                 vTaskSuspend( NULL );\r
381 \r
382                 /* This task will execute when the master task already holds the mutex.\r
383                 Attempting to take the mutex will place this task in the Blocked\r
384                 state. */\r
385                 if( xSemaphoreTake( xMasterSlaveMutex, portMAX_DELAY ) != pdPASS )\r
386                 {\r
387                         xErrorDetected = pdTRUE;\r
388                 }\r
389 \r
390                 if( xSemaphoreGive( xMasterSlaveMutex ) != pdPASS )\r
391                 {\r
392                         xErrorDetected = pdTRUE;\r
393                 }\r
394         }\r
395 }\r
396 /*-----------------------------------------------------------*/\r
397 \r
398 static void vInterruptCountingSemaphoreTask( void *pvParameters )\r
399 {\r
400 BaseType_t xCount;\r
401 const TickType_t xDelay = pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ) * ( intsemMAX_COUNT + 1 );\r
402 \r
403         ( void ) pvParameters;\r
404 \r
405         for( ;; )\r
406         {\r
407                 /* Expect to start with the counting semaphore empty. */\r
408                 if( uxQueueMessagesWaiting( ( QueueHandle_t ) xISRCountingSemaphore ) != 0 )\r
409                 {\r
410                         xErrorDetected = pdTRUE;\r
411                 }\r
412 \r
413                 /* Wait until it is expected that the interrupt will have filled the\r
414                 counting semaphore. */\r
415                 xOkToGiveCountingSemaphore = pdTRUE;\r
416                 vTaskDelay( xDelay );\r
417                 xOkToGiveCountingSemaphore = pdFALSE;\r
418 \r
419                 /* Now it is expected that the counting semaphore is full. */\r
420                 if( uxQueueMessagesWaiting( ( QueueHandle_t ) xISRCountingSemaphore ) != intsemMAX_COUNT )\r
421                 {\r
422                         xErrorDetected = pdTRUE;\r
423                 }\r
424 \r
425                 if( uxQueueSpacesAvailable( ( QueueHandle_t ) xISRCountingSemaphore ) != 0 )\r
426                 {\r
427                         xErrorDetected = pdTRUE;\r
428                 }\r
429 \r
430                 ulCountingSemaphoreLoops++;\r
431 \r
432                 /* Expect to be able to take the counting semaphore intsemMAX_COUNT\r
433                 times.  A block time of 0 is used as the semaphore should already be\r
434                 there. */\r
435                 xCount = 0;\r
436                 while( xSemaphoreTake( xISRCountingSemaphore, 0 ) == pdPASS )\r
437                 {\r
438                         xCount++;\r
439                 }\r
440 \r
441                 if( xCount != intsemMAX_COUNT )\r
442                 {\r
443                         xErrorDetected = pdTRUE;\r
444                 }\r
445 \r
446                 /* Now raise the priority of this task so it runs immediately that the\r
447                 semaphore is given from the interrupt. */\r
448                 vTaskPrioritySet( NULL, configMAX_PRIORITIES - 1 );\r
449 \r
450                 /* Block to wait for the semaphore to be given from the interrupt. */\r
451                 xOkToGiveCountingSemaphore = pdTRUE;\r
452                 xSemaphoreTake( xISRCountingSemaphore, portMAX_DELAY );\r
453                 xSemaphoreTake( xISRCountingSemaphore, portMAX_DELAY );\r
454                 xOkToGiveCountingSemaphore = pdFALSE;\r
455 \r
456                 /* Reset the priority so as not to disturbe other tests too much. */\r
457                 vTaskPrioritySet( NULL, tskIDLE_PRIORITY );\r
458 \r
459                 ulCountingSemaphoreLoops++;\r
460         }\r
461 }\r
462 /*-----------------------------------------------------------*/\r
463 \r
464 void vInterruptSemaphorePeriodicTest( void )\r
465 {\r
466 static TickType_t xLastGiveTime = 0;\r
467 BaseType_t xHigherPriorityTaskWoken = pdFALSE;\r
468 TickType_t xTimeNow;\r
469 \r
470         /* No mutual exclusion on xOkToGiveMutex, but this is only test code (and\r
471         only executed on a 32-bit architecture) so ignore that in this case. */\r
472         xTimeNow = xTaskGetTickCountFromISR();\r
473         if( ( ( TickType_t ) ( xTimeNow - xLastGiveTime ) ) >= pdMS_TO_TICKS( intsemINTERRUPT_MUTEX_GIVE_PERIOD_MS ) )\r
474         {\r
475                 configASSERT( xISRMutex );\r
476                 if( xOkToGiveMutex != pdFALSE )\r
477                 {\r
478                         /* Null is used as the second parameter in this give, and non-NULL\r
479                         in the other gives for code coverage reasons. */\r
480                         xSemaphoreGiveFromISR( xISRMutex, NULL );\r
481 \r
482                         /* Second give attempt should fail. */\r
483                         configASSERT( xSemaphoreGiveFromISR( xISRMutex, &xHigherPriorityTaskWoken ) == pdFAIL );\r
484                 }\r
485 \r
486                 if( xOkToGiveCountingSemaphore != pdFALSE )\r
487                 {\r
488                         xSemaphoreGiveFromISR( xISRCountingSemaphore, &xHigherPriorityTaskWoken );\r
489                 }\r
490                 xLastGiveTime = xTimeNow;\r
491         }\r
492 \r
493         /* Remove compiler warnings about the value being set but not used. */\r
494         ( void ) xHigherPriorityTaskWoken;\r
495 }\r
496 /*-----------------------------------------------------------*/\r
497 \r
498 /* This is called to check that all the created tasks are still running. */\r
499 BaseType_t xAreInterruptSemaphoreTasksStillRunning( void )\r
500 {\r
501 static uint32_t ulLastMasterLoopCounter = 0, ulLastCountingSemaphoreLoops = 0;\r
502 \r
503         /* If the demo tasks are running then it is expected that the loop counters\r
504         will have changed since this function was last called. */\r
505         if( ulLastMasterLoopCounter == ulMasterLoops )\r
506         {\r
507                 xErrorDetected = pdTRUE;\r
508         }\r
509 \r
510         ulLastMasterLoopCounter = ulMasterLoops;\r
511 \r
512         if( ulLastCountingSemaphoreLoops == ulCountingSemaphoreLoops )\r
513         {\r
514                 xErrorDetected = pdTRUE;\r
515         }\r
516 \r
517         ulLastCountingSemaphoreLoops = ulCountingSemaphoreLoops++;\r
518 \r
519         /* Errors detected in the task itself will have latched xErrorDetected\r
520         to true. */\r
521 \r
522         return ( BaseType_t ) !xErrorDetected;\r
523 }\r
524 \r
525 \r