]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Common/Minimal/semtest.c
7303d598edab6f2202bf6bf0e1f6a08fc7e45b7f
[freertos] / FreeRTOS / Demo / Common / Minimal / semtest.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  * Creates two sets of two tasks.  The tasks within a set share a variable, access\r
30  * to which is guarded by a semaphore.\r
31  *\r
32  * Each task starts by attempting to obtain the semaphore.  On obtaining a\r
33  * semaphore a task checks to ensure that the guarded variable has an expected\r
34  * value.  It then clears the variable to zero before counting it back up to the\r
35  * expected value in increments of 1.  After each increment the variable is checked\r
36  * to ensure it contains the value to which it was just set. When the starting\r
37  * value is again reached the task releases the semaphore giving the other task in\r
38  * the set a chance to do exactly the same thing.  The starting value is high\r
39  * enough to ensure that a tick is likely to occur during the incrementing loop.\r
40  *\r
41  * An error is flagged if at any time during the process a shared variable is\r
42  * found to have a value other than that expected.  Such an occurrence would\r
43  * suggest an error in the mutual exclusion mechanism by which access to the\r
44  * variable is restricted.\r
45  *\r
46  * The first set of two tasks poll their semaphore.  The second set use blocking\r
47  * calls.\r
48  *\r
49  */\r
50 \r
51 \r
52 #include <stdlib.h>\r
53 \r
54 /* Scheduler include files. */\r
55 #include "FreeRTOS.h"\r
56 #include "task.h"\r
57 #include "semphr.h"\r
58 \r
59 /* Demo app include files. */\r
60 #include "semtest.h"\r
61 \r
62 /* The value to which the shared variables are counted. */\r
63 #define semtstBLOCKING_EXPECTED_VALUE           ( ( uint32_t ) 0xfff )\r
64 #define semtstNON_BLOCKING_EXPECTED_VALUE       ( ( uint32_t ) 0xff  )\r
65 \r
66 #define semtstSTACK_SIZE                        configMINIMAL_STACK_SIZE\r
67 \r
68 #define semtstNUM_TASKS                         ( 4 )\r
69 \r
70 #define semtstDELAY_FACTOR                      ( ( TickType_t ) 10 )\r
71 \r
72 /* The task function as described at the top of the file. */\r
73 static portTASK_FUNCTION_PROTO( prvSemaphoreTest, pvParameters );\r
74 \r
75 /* Structure used to pass parameters to each task. */\r
76 typedef struct SEMAPHORE_PARAMETERS\r
77 {\r
78         SemaphoreHandle_t xSemaphore;\r
79         volatile uint32_t *pulSharedVariable;\r
80         TickType_t xBlockTime;\r
81 } xSemaphoreParameters;\r
82 \r
83 /* Variables used to check that all the tasks are still running without errors. */\r
84 static volatile short sCheckVariables[ semtstNUM_TASKS ] = { 0 };\r
85 static volatile short sNextCheckVariable = 0;\r
86 \r
87 /*-----------------------------------------------------------*/\r
88 \r
89 void vStartSemaphoreTasks( UBaseType_t uxPriority )\r
90 {\r
91 xSemaphoreParameters *pxFirstSemaphoreParameters, *pxSecondSemaphoreParameters;\r
92 const TickType_t xBlockTime = ( TickType_t ) 100;\r
93 \r
94         /* Create the structure used to pass parameters to the first two tasks. */\r
95         pxFirstSemaphoreParameters = ( xSemaphoreParameters * ) pvPortMalloc( sizeof( xSemaphoreParameters ) );\r
96 \r
97         if( pxFirstSemaphoreParameters != NULL )\r
98         {\r
99                 /* Create the semaphore used by the first two tasks. */\r
100                 pxFirstSemaphoreParameters->xSemaphore = xSemaphoreCreateBinary();\r
101 \r
102                 if( pxFirstSemaphoreParameters->xSemaphore != NULL )\r
103                 {\r
104                         xSemaphoreGive( pxFirstSemaphoreParameters->xSemaphore );\r
105 \r
106                         /* Create the variable which is to be shared by the first two tasks. */\r
107                         pxFirstSemaphoreParameters->pulSharedVariable = ( uint32_t * ) pvPortMalloc( sizeof( uint32_t ) );\r
108 \r
109                         /* Initialise the share variable to the value the tasks expect. */\r
110                         *( pxFirstSemaphoreParameters->pulSharedVariable ) = semtstNON_BLOCKING_EXPECTED_VALUE;\r
111 \r
112                         /* The first two tasks do not block on semaphore calls. */\r
113                         pxFirstSemaphoreParameters->xBlockTime = ( TickType_t ) 0;\r
114 \r
115                         /* Spawn the first two tasks.  As they poll they operate at the idle priority. */\r
116                         xTaskCreate( prvSemaphoreTest, "PolSEM1", semtstSTACK_SIZE, ( void * ) pxFirstSemaphoreParameters, tskIDLE_PRIORITY, ( TaskHandle_t * ) NULL );\r
117                         xTaskCreate( prvSemaphoreTest, "PolSEM2", semtstSTACK_SIZE, ( void * ) pxFirstSemaphoreParameters, tskIDLE_PRIORITY, ( TaskHandle_t * ) NULL );\r
118 \r
119                         /* vQueueAddToRegistry() adds the semaphore to the registry, if one\r
120                         is in use.  The registry is provided as a means for kernel aware\r
121                         debuggers to locate semaphores and has no purpose if a kernel aware\r
122                         debugger is not being used.  The call to vQueueAddToRegistry() will\r
123                         be removed by the pre-processor if configQUEUE_REGISTRY_SIZE is not\r
124                         defined or is defined to be less than 1. */\r
125                         vQueueAddToRegistry( ( QueueHandle_t ) pxFirstSemaphoreParameters->xSemaphore, "Counting_Sem_1" );\r
126                 }\r
127         }\r
128 \r
129         /* Do exactly the same to create the second set of tasks, only this time\r
130         provide a block time for the semaphore calls. */\r
131         pxSecondSemaphoreParameters = ( xSemaphoreParameters * ) pvPortMalloc( sizeof( xSemaphoreParameters ) );\r
132         if( pxSecondSemaphoreParameters != NULL )\r
133         {\r
134                 pxSecondSemaphoreParameters->xSemaphore = xSemaphoreCreateBinary();\r
135 \r
136                 if( pxSecondSemaphoreParameters->xSemaphore != NULL )\r
137                 {\r
138                         xSemaphoreGive( pxSecondSemaphoreParameters->xSemaphore );\r
139 \r
140                         pxSecondSemaphoreParameters->pulSharedVariable = ( uint32_t * ) pvPortMalloc( sizeof( uint32_t ) );\r
141                         *( pxSecondSemaphoreParameters->pulSharedVariable ) = semtstBLOCKING_EXPECTED_VALUE;\r
142                         pxSecondSemaphoreParameters->xBlockTime = xBlockTime / portTICK_PERIOD_MS;\r
143 \r
144                         xTaskCreate( prvSemaphoreTest, "BlkSEM1", semtstSTACK_SIZE, ( void * ) pxSecondSemaphoreParameters, uxPriority, ( TaskHandle_t * ) NULL );\r
145                         xTaskCreate( prvSemaphoreTest, "BlkSEM2", semtstSTACK_SIZE, ( void * ) pxSecondSemaphoreParameters, uxPriority, ( TaskHandle_t * ) NULL );\r
146 \r
147                         /* vQueueAddToRegistry() adds the semaphore to the registry, if one\r
148                         is in use.  The registry is provided as a means for kernel aware\r
149                         debuggers to locate semaphores and has no purpose if a kernel aware\r
150                         debugger is not being used.  The call to vQueueAddToRegistry() will\r
151                         be removed by the pre-processor if configQUEUE_REGISTRY_SIZE is not\r
152                         defined or is defined to be less than 1. */\r
153                         vQueueAddToRegistry( ( QueueHandle_t ) pxSecondSemaphoreParameters->xSemaphore, "Counting_Sem_2" );\r
154                 }\r
155         }\r
156 }\r
157 /*-----------------------------------------------------------*/\r
158 \r
159 static portTASK_FUNCTION( prvSemaphoreTest, pvParameters )\r
160 {\r
161 xSemaphoreParameters *pxParameters;\r
162 volatile uint32_t *pulSharedVariable, ulExpectedValue;\r
163 uint32_t ulCounter;\r
164 short sError = pdFALSE, sCheckVariableToUse;\r
165 \r
166         /* See which check variable to use.  sNextCheckVariable is not semaphore\r
167         protected! */\r
168         portENTER_CRITICAL();\r
169                 sCheckVariableToUse = sNextCheckVariable;\r
170                 sNextCheckVariable++;\r
171         portEXIT_CRITICAL();\r
172 \r
173         /* A structure is passed in as the parameter.  This contains the shared\r
174         variable being guarded. */\r
175         pxParameters = ( xSemaphoreParameters * ) pvParameters;\r
176         pulSharedVariable = pxParameters->pulSharedVariable;\r
177 \r
178         /* If we are blocking we use a much higher count to ensure loads of context\r
179         switches occur during the count. */\r
180         if( pxParameters->xBlockTime > ( TickType_t ) 0 )\r
181         {\r
182                 ulExpectedValue = semtstBLOCKING_EXPECTED_VALUE;\r
183         }\r
184         else\r
185         {\r
186                 ulExpectedValue = semtstNON_BLOCKING_EXPECTED_VALUE;\r
187         }\r
188 \r
189         for( ;; )\r
190         {\r
191                 /* Try to obtain the semaphore. */\r
192                 if( xSemaphoreTake( pxParameters->xSemaphore, pxParameters->xBlockTime ) == pdPASS )\r
193                 {\r
194                         /* We have the semaphore and so expect any other tasks using the\r
195                         shared variable to have left it in the state we expect to find\r
196                         it. */\r
197                         if( *pulSharedVariable != ulExpectedValue )\r
198                         {\r
199                                 sError = pdTRUE;\r
200                         }\r
201 \r
202                         /* Clear the variable, then count it back up to the expected value\r
203                         before releasing the semaphore.  Would expect a context switch or\r
204                         two during this time. */\r
205                         for( ulCounter = ( uint32_t ) 0; ulCounter <= ulExpectedValue; ulCounter++ )\r
206                         {\r
207                                 *pulSharedVariable = ulCounter;\r
208                                 if( *pulSharedVariable != ulCounter )\r
209                                 {\r
210                                         sError = pdTRUE;\r
211                                 }\r
212                         }\r
213 \r
214                         /* Release the semaphore, and if no errors have occurred increment the check\r
215                         variable. */\r
216                         if(     xSemaphoreGive( pxParameters->xSemaphore ) == pdFALSE )\r
217                         {\r
218                                 sError = pdTRUE;\r
219                         }\r
220 \r
221                         if( sError == pdFALSE )\r
222                         {\r
223                                 if( sCheckVariableToUse < semtstNUM_TASKS )\r
224                                 {\r
225                                         ( sCheckVariables[ sCheckVariableToUse ] )++;\r
226                                 }\r
227                         }\r
228 \r
229                         /* If we have a block time then we are running at a priority higher\r
230                         than the idle priority.  This task takes a long time to complete\r
231                         a cycle (deliberately so to test the guarding) so will be starving\r
232                         out lower priority tasks.  Block for some time to allow give lower\r
233                         priority tasks some processor time. */\r
234                         vTaskDelay( pxParameters->xBlockTime * semtstDELAY_FACTOR );\r
235                 }\r
236                 else\r
237                 {\r
238                         if( pxParameters->xBlockTime == ( TickType_t ) 0 )\r
239                         {\r
240                                 /* We have not got the semaphore yet, so no point using the\r
241                                 processor.  We are not blocking when attempting to obtain the\r
242                                 semaphore. */\r
243                                 taskYIELD();\r
244                         }\r
245                 }\r
246         }\r
247 }\r
248 /*-----------------------------------------------------------*/\r
249 \r
250 /* This is called to check that all the created tasks are still running. */\r
251 BaseType_t xAreSemaphoreTasksStillRunning( void )\r
252 {\r
253 static short sLastCheckVariables[ semtstNUM_TASKS ] = { 0 };\r
254 BaseType_t xTask, xReturn = pdTRUE;\r
255 \r
256         for( xTask = 0; xTask < semtstNUM_TASKS; xTask++ )\r
257         {\r
258                 if( sLastCheckVariables[ xTask ] == sCheckVariables[ xTask ] )\r
259                 {\r
260                         xReturn = pdFALSE;\r
261                 }\r
262 \r
263                 sLastCheckVariables[ xTask ] = sCheckVariables[ xTask ];\r
264         }\r
265 \r
266         return xReturn;\r
267 }\r
268 \r
269 \r