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