croutine.c 17 KB

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