]> begriffs open source - freertos/blob - croutine.c
Moved git-secrets check to a different workflow (#271)
[freertos] / croutine.c
1 /*\r
2  * FreeRTOS Kernel V10.4.3\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  * https://www.FreeRTOS.org\r
23  * https://github.com/FreeRTOS\r
24  *\r
25  */\r
26 \r
27 #include "FreeRTOS.h"\r
28 #include "task.h"\r
29 #include "croutine.h"\r
30 \r
31 /* Remove the whole file is co-routines are not being used. */\r
32 #if ( configUSE_CO_ROUTINES != 0 )\r
33 \r
34 /*\r
35  * Some kernel aware debuggers require data to be viewed to be global, rather\r
36  * than file scope.\r
37  */\r
38     #ifdef portREMOVE_STATIC_QUALIFIER\r
39         #define static\r
40     #endif\r
41 \r
42 \r
43 /* Lists for ready and blocked co-routines. --------------------*/\r
44     static List_t pxReadyCoRoutineLists[ configMAX_CO_ROUTINE_PRIORITIES ]; /*< Prioritised ready co-routines. */\r
45     static List_t xDelayedCoRoutineList1;                                   /*< Delayed co-routines. */\r
46     static List_t xDelayedCoRoutineList2;                                   /*< Delayed co-routines (two lists are used - one for delays that have overflowed the current tick count. */\r
47     static List_t * pxDelayedCoRoutineList = NULL;                          /*< Points to the delayed co-routine list currently being used. */\r
48     static List_t * pxOverflowDelayedCoRoutineList = NULL;                  /*< Points to the delayed co-routine list currently being used to hold co-routines that have overflowed the current tick count. */\r
49     static List_t xPendingReadyCoRoutineList;                               /*< Holds co-routines that have been readied by an external event.  They cannot be added directly to the ready lists as the ready lists cannot be accessed by interrupts. */\r
50 \r
51 /* Other file private variables. --------------------------------*/\r
52     CRCB_t * pxCurrentCoRoutine = NULL;\r
53     static UBaseType_t uxTopCoRoutineReadyPriority = 0;\r
54     static TickType_t xCoRoutineTickCount = 0, xLastTickCount = 0, xPassedTicks = 0;\r
55 \r
56 /* The initial state of the co-routine when it is created. */\r
57     #define corINITIAL_STATE    ( 0 )\r
58 \r
59 /*\r
60  * Place the co-routine represented by pxCRCB into the appropriate ready queue\r
61  * for the priority.  It is inserted at the end of the list.\r
62  *\r
63  * This macro accesses the co-routine ready lists and therefore must not be\r
64  * used from within an ISR.\r
65  */\r
66     #define prvAddCoRoutineToReadyQueue( pxCRCB )                                                                       \\r
67     {                                                                                                                   \\r
68         if( pxCRCB->uxPriority > uxTopCoRoutineReadyPriority )                                                          \\r
69         {                                                                                                               \\r
70             uxTopCoRoutineReadyPriority = pxCRCB->uxPriority;                                                           \\r
71         }                                                                                                               \\r
72         vListInsertEnd( ( List_t * ) &( pxReadyCoRoutineLists[ pxCRCB->uxPriority ] ), &( pxCRCB->xGenericListItem ) ); \\r
73     }\r
74 \r
75 /*\r
76  * Utility to ready all the lists used by the scheduler.  This is called\r
77  * automatically upon the creation of the first co-routine.\r
78  */\r
79     static void prvInitialiseCoRoutineLists( void );\r
80 \r
81 /*\r
82  * Co-routines that are readied by an interrupt cannot be placed directly into\r
83  * the ready lists (there is no mutual exclusion).  Instead they are placed in\r
84  * in the pending ready list in order that they can later be moved to the ready\r
85  * list by the co-routine scheduler.\r
86  */\r
87     static void prvCheckPendingReadyList( void );\r
88 \r
89 /*\r
90  * Macro that looks at the list of co-routines that are currently delayed to\r
91  * see if any require waking.\r
92  *\r
93  * Co-routines are stored in the queue in the order of their wake time -\r
94  * meaning once one co-routine has been found whose timer has not expired\r
95  * we need not look any further down the list.\r
96  */\r
97     static void prvCheckDelayedList( void );\r
98 \r
99 /*-----------------------------------------------------------*/\r
100 \r
101     BaseType_t xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode,\r
102                                  UBaseType_t uxPriority,\r
103                                  UBaseType_t uxIndex )\r
104     {\r
105         BaseType_t xReturn;\r
106         CRCB_t * pxCoRoutine;\r
107 \r
108         /* Allocate the memory that will store the co-routine control block. */\r
109         pxCoRoutine = ( CRCB_t * ) pvPortMalloc( sizeof( CRCB_t ) );\r
110 \r
111         if( pxCoRoutine )\r
112         {\r
113             /* If pxCurrentCoRoutine is NULL then this is the first co-routine to\r
114             * be created and the co-routine data structures need initialising. */\r
115             if( pxCurrentCoRoutine == NULL )\r
116             {\r
117                 pxCurrentCoRoutine = pxCoRoutine;\r
118                 prvInitialiseCoRoutineLists();\r
119             }\r
120 \r
121             /* Check the priority is within limits. */\r
122             if( uxPriority >= configMAX_CO_ROUTINE_PRIORITIES )\r
123             {\r
124                 uxPriority = configMAX_CO_ROUTINE_PRIORITIES - 1;\r
125             }\r
126 \r
127             /* Fill out the co-routine control block from the function parameters. */\r
128             pxCoRoutine->uxState = corINITIAL_STATE;\r
129             pxCoRoutine->uxPriority = uxPriority;\r
130             pxCoRoutine->uxIndex = uxIndex;\r
131             pxCoRoutine->pxCoRoutineFunction = pxCoRoutineCode;\r
132 \r
133             /* Initialise all the other co-routine control block parameters. */\r
134             vListInitialiseItem( &( pxCoRoutine->xGenericListItem ) );\r
135             vListInitialiseItem( &( pxCoRoutine->xEventListItem ) );\r
136 \r
137             /* Set the co-routine control block as a link back from the ListItem_t.\r
138              * This is so we can get back to the containing CRCB from a generic item\r
139              * in a list. */\r
140             listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xGenericListItem ), pxCoRoutine );\r
141             listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xEventListItem ), pxCoRoutine );\r
142 \r
143             /* Event lists are always in priority order. */\r
144             listSET_LIST_ITEM_VALUE( &( pxCoRoutine->xEventListItem ), ( ( TickType_t ) configMAX_CO_ROUTINE_PRIORITIES - ( TickType_t ) uxPriority ) );\r
145 \r
146             /* Now the co-routine has been initialised it can be added to the ready\r
147              * list at the correct priority. */\r
148             prvAddCoRoutineToReadyQueue( pxCoRoutine );\r
149 \r
150             xReturn = pdPASS;\r
151         }\r
152         else\r
153         {\r
154             xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;\r
155         }\r
156 \r
157         return xReturn;\r
158     }\r
159 /*-----------------------------------------------------------*/\r
160 \r
161     void vCoRoutineAddToDelayedList( TickType_t xTicksToDelay,\r
162                                      List_t * pxEventList )\r
163     {\r
164         TickType_t xTimeToWake;\r
165 \r
166         /* Calculate the time to wake - this may overflow but this is\r
167          * not a problem. */\r
168         xTimeToWake = xCoRoutineTickCount + xTicksToDelay;\r
169 \r
170         /* We must remove ourselves from the ready list before adding\r
171          * ourselves to the blocked list as the same list item is used for\r
172          * both lists. */\r
173         ( void ) uxListRemove( ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) );\r
174 \r
175         /* The list item will be inserted in wake time order. */\r
176         listSET_LIST_ITEM_VALUE( &( pxCurrentCoRoutine->xGenericListItem ), xTimeToWake );\r
177 \r
178         if( xTimeToWake < xCoRoutineTickCount )\r
179         {\r
180             /* Wake time has overflowed.  Place this item in the\r
181              * overflow list. */\r
182             vListInsert( ( List_t * ) pxOverflowDelayedCoRoutineList, ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) );\r
183         }\r
184         else\r
185         {\r
186             /* The wake time has not overflowed, so we can use the\r
187              * current block list. */\r
188             vListInsert( ( List_t * ) pxDelayedCoRoutineList, ( ListItem_t * ) &( pxCurrentCoRoutine->xGenericListItem ) );\r
189         }\r
190 \r
191         if( pxEventList )\r
192         {\r
193             /* Also add the co-routine to an event list.  If this is done then the\r
194              * function must be called with interrupts disabled. */\r
195             vListInsert( pxEventList, &( pxCurrentCoRoutine->xEventListItem ) );\r
196         }\r
197     }\r
198 /*-----------------------------------------------------------*/\r
199 \r
200     static void prvCheckPendingReadyList( void )\r
201     {\r
202         /* Are there any co-routines waiting to get moved to the ready list?  These\r
203          * are co-routines that have been readied by an ISR.  The ISR cannot access\r
204          * the ready lists itself. */\r
205         while( listLIST_IS_EMPTY( &xPendingReadyCoRoutineList ) == pdFALSE )\r
206         {\r
207             CRCB_t * pxUnblockedCRCB;\r
208 \r
209             /* The pending ready list can be accessed by an ISR. */\r
210             portDISABLE_INTERRUPTS();\r
211             {\r
212                 pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyCoRoutineList ) );\r
213                 ( void ) uxListRemove( &( pxUnblockedCRCB->xEventListItem ) );\r
214             }\r
215             portENABLE_INTERRUPTS();\r
216 \r
217             ( void ) uxListRemove( &( pxUnblockedCRCB->xGenericListItem ) );\r
218             prvAddCoRoutineToReadyQueue( pxUnblockedCRCB );\r
219         }\r
220     }\r
221 /*-----------------------------------------------------------*/\r
222 \r
223     static void prvCheckDelayedList( void )\r
224     {\r
225         CRCB_t * pxCRCB;\r
226 \r
227         xPassedTicks = xTaskGetTickCount() - xLastTickCount;\r
228 \r
229         while( xPassedTicks )\r
230         {\r
231             xCoRoutineTickCount++;\r
232             xPassedTicks--;\r
233 \r
234             /* If the tick count has overflowed we need to swap the ready lists. */\r
235             if( xCoRoutineTickCount == 0 )\r
236             {\r
237                 List_t * pxTemp;\r
238 \r
239                 /* Tick count has overflowed so we need to swap the delay lists.  If there are\r
240                  * any items in pxDelayedCoRoutineList here then there is an error! */\r
241                 pxTemp = pxDelayedCoRoutineList;\r
242                 pxDelayedCoRoutineList = pxOverflowDelayedCoRoutineList;\r
243                 pxOverflowDelayedCoRoutineList = pxTemp;\r
244             }\r
245 \r
246             /* See if this tick has made a timeout expire. */\r
247             while( listLIST_IS_EMPTY( pxDelayedCoRoutineList ) == pdFALSE )\r
248             {\r
249                 pxCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedCoRoutineList );\r
250 \r
251                 if( xCoRoutineTickCount < listGET_LIST_ITEM_VALUE( &( pxCRCB->xGenericListItem ) ) )\r
252                 {\r
253                     /* Timeout not yet expired. */\r
254                     break;\r
255                 }\r
256 \r
257                 portDISABLE_INTERRUPTS();\r
258                 {\r
259                     /* The event could have occurred just before this critical\r
260                      *  section.  If this is the case then the generic list item will\r
261                      *  have been moved to the pending ready list and the following\r
262                      *  line is still valid.  Also the pvContainer parameter will have\r
263                      *  been set to NULL so the following lines are also valid. */\r
264                     ( void ) uxListRemove( &( pxCRCB->xGenericListItem ) );\r
265 \r
266                     /* Is the co-routine waiting on an event also? */\r
267                     if( pxCRCB->xEventListItem.pxContainer )\r
268                     {\r
269                         ( void ) uxListRemove( &( pxCRCB->xEventListItem ) );\r
270                     }\r
271                 }\r
272                 portENABLE_INTERRUPTS();\r
273 \r
274                 prvAddCoRoutineToReadyQueue( pxCRCB );\r
275             }\r
276         }\r
277 \r
278         xLastTickCount = xCoRoutineTickCount;\r
279     }\r
280 /*-----------------------------------------------------------*/\r
281 \r
282     void vCoRoutineSchedule( void )\r
283     {\r
284         /* Only run a co-routine after prvInitialiseCoRoutineLists() has been\r
285          * called.  prvInitialiseCoRoutineLists() is called automatically when a\r
286          * co-routine is created. */\r
287         if( pxDelayedCoRoutineList != NULL )\r
288         {\r
289             /* See if any co-routines readied by events need moving to the ready lists. */\r
290             prvCheckPendingReadyList();\r
291 \r
292             /* See if any delayed co-routines have timed out. */\r
293             prvCheckDelayedList();\r
294 \r
295             /* Find the highest priority queue that contains ready co-routines. */\r
296             while( listLIST_IS_EMPTY( &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ) )\r
297             {\r
298                 if( uxTopCoRoutineReadyPriority == 0 )\r
299                 {\r
300                     /* No more co-routines to check. */\r
301                     return;\r
302                 }\r
303 \r
304                 --uxTopCoRoutineReadyPriority;\r
305             }\r
306 \r
307             /* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the co-routines\r
308              * of the same priority get an equal share of the processor time. */\r
309             listGET_OWNER_OF_NEXT_ENTRY( pxCurrentCoRoutine, &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) );\r
310 \r
311             /* Call the co-routine. */\r
312             ( pxCurrentCoRoutine->pxCoRoutineFunction )( pxCurrentCoRoutine, pxCurrentCoRoutine->uxIndex );\r
313         }\r
314     }\r
315 /*-----------------------------------------------------------*/\r
316 \r
317     static void prvInitialiseCoRoutineLists( void )\r
318     {\r
319         UBaseType_t uxPriority;\r
320 \r
321         for( uxPriority = 0; uxPriority < configMAX_CO_ROUTINE_PRIORITIES; uxPriority++ )\r
322         {\r
323             vListInitialise( ( List_t * ) &( pxReadyCoRoutineLists[ uxPriority ] ) );\r
324         }\r
325 \r
326         vListInitialise( ( List_t * ) &xDelayedCoRoutineList1 );\r
327         vListInitialise( ( List_t * ) &xDelayedCoRoutineList2 );\r
328         vListInitialise( ( List_t * ) &xPendingReadyCoRoutineList );\r
329 \r
330         /* Start with pxDelayedCoRoutineList using list1 and the\r
331          * pxOverflowDelayedCoRoutineList using list2. */\r
332         pxDelayedCoRoutineList = &xDelayedCoRoutineList1;\r
333         pxOverflowDelayedCoRoutineList = &xDelayedCoRoutineList2;\r
334     }\r
335 /*-----------------------------------------------------------*/\r
336 \r
337     BaseType_t xCoRoutineRemoveFromEventList( const List_t * pxEventList )\r
338     {\r
339         CRCB_t * pxUnblockedCRCB;\r
340         BaseType_t xReturn;\r
341 \r
342         /* This function is called from within an interrupt.  It can only access\r
343          * event lists and the pending ready list.  This function assumes that a\r
344          * check has already been made to ensure pxEventList is not empty. */\r
345         pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList );\r
346         ( void ) uxListRemove( &( pxUnblockedCRCB->xEventListItem ) );\r
347         vListInsertEnd( ( List_t * ) &( xPendingReadyCoRoutineList ), &( pxUnblockedCRCB->xEventListItem ) );\r
348 \r
349         if( pxUnblockedCRCB->uxPriority >= pxCurrentCoRoutine->uxPriority )\r
350         {\r
351             xReturn = pdTRUE;\r
352         }\r
353         else\r
354         {\r
355             xReturn = pdFALSE;\r
356         }\r
357 \r
358         return xReturn;\r
359     }\r
360 \r
361 #endif /* configUSE_CO_ROUTINES == 0 */\r