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