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