2022-01-29 16:49:56 +08:00
/*
* SPDX-FileCopyrightText: 2020 Amazon.com, Inc. or its affiliates
*
* SPDX-License-Identifier: MIT
*
* SPDX-FileContributor: 2016-2022 Espressif Systems (Shanghai) CO LTD
*/
2016-08-17 23:08:22 +08:00
/*
2021-08-24 12:06:21 +08:00
* FreeRTOS Kernel V10.4.3
2021-08-10 05:13:43 +08:00
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2019-11-28 15:27:47 -03:00
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
2021-08-10 05:13:43 +08:00
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
2019-11-28 15:27:47 -03:00
*
*/
2016-08-17 23:08:22 +08:00
# include "FreeRTOS.h"
# include "task.h"
# include "croutine.h"
2019-11-28 15:27:47 -03:00
/* Remove the whole file is co-routines are not being used. */
2021-08-10 05:13:43 +08:00
# if ( configUSE_CO_ROUTINES != 0 )
2019-11-28 15:27:47 -03:00
2016-08-17 23:08:22 +08:00
/*
* Some kernel aware debuggers require data to be viewed to be global, rather
* than file scope.
*/
2021-08-10 05:13:43 +08:00
# ifdef portREMOVE_STATIC_QUALIFIER
# define static
# endif
2016-08-17 23:08:22 +08:00
/* Lists for ready and blocked co-routines. --------------------*/
2021-08-10 05:13:43 +08:00
static List_t pxReadyCoRoutineLists [ configMAX_CO_ROUTINE_PRIORITIES ] ; /*< Prioritised ready co-routines. */
static List_t xDelayedCoRoutineList1 ; /*< Delayed co-routines. */
static List_t xDelayedCoRoutineList2 ; /*< Delayed co-routines (two lists are used - one for delays that have overflowed the current tick count. */
static List_t * pxDelayedCoRoutineList = NULL ; /*< Points to the delayed co-routine list currently being used. */
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. */
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. */
2016-08-17 23:08:22 +08:00
/* Other file private variables. --------------------------------*/
2021-08-10 05:13:43 +08:00
CRCB_t * pxCurrentCoRoutine = NULL ;
static UBaseType_t uxTopCoRoutineReadyPriority = 0 ;
static TickType_t xCoRoutineTickCount = 0 , xLastTickCount = 0 , xPassedTicks = 0 ;
2016-08-17 23:08:22 +08:00
/* The initial state of the co-routine when it is created. */
2021-08-10 05:13:43 +08:00
# define corINITIAL_STATE ( 0 )
2016-08-17 23:08:22 +08:00
/*
* Place the co-routine represented by pxCRCB into the appropriate ready queue
* for the priority. It is inserted at the end of the list.
*
* This macro accesses the co-routine ready lists and therefore must not be
* used from within an ISR.
*/
2021-08-10 05:13:43 +08:00
# define prvAddCoRoutineToReadyQueue( pxCRCB ) \
{ \
if( pxCRCB->uxPriority > uxTopCoRoutineReadyPriority ) \
{ \
uxTopCoRoutineReadyPriority = pxCRCB->uxPriority; \
} \
vListInsertEnd( ( List_t * ) &( pxReadyCoRoutineLists[ pxCRCB->uxPriority ] ), &( pxCRCB->xGenericListItem ) ); \
}
2016-08-17 23:08:22 +08:00
/*
* Utility to ready all the lists used by the scheduler. This is called
* automatically upon the creation of the first co-routine.
*/
2021-08-10 05:13:43 +08:00
static void prvInitialiseCoRoutineLists ( void ) ;
2016-08-17 23:08:22 +08:00
/*
* Co-routines that are readied by an interrupt cannot be placed directly into
* the ready lists (there is no mutual exclusion). Instead they are placed in
* in the pending ready list in order that they can later be moved to the ready
* list by the co-routine scheduler.
*/
2021-08-10 05:13:43 +08:00
static void prvCheckPendingReadyList ( void ) ;
2016-08-17 23:08:22 +08:00
/*
* Macro that looks at the list of co-routines that are currently delayed to
* see if any require waking.
*
* Co-routines are stored in the queue in the order of their wake time -
* meaning once one co-routine has been found whose timer has not expired
* we need not look any further down the list.
*/
2021-08-10 05:13:43 +08:00
static void prvCheckDelayedList ( void ) ;
2016-08-17 23:08:22 +08:00
/*-----------------------------------------------------------*/
2021-08-10 05:13:43 +08:00
BaseType_t xCoRoutineCreate ( crCOROUTINE_CODE pxCoRoutineCode ,
UBaseType_t uxPriority ,
UBaseType_t uxIndex )
{
BaseType_t xReturn ;
CRCB_t * pxCoRoutine ;
/* Allocate the memory that will store the co-routine control block. */
pxCoRoutine = ( CRCB_t * ) pvPortMalloc ( sizeof ( CRCB_t ) ) ;
if ( pxCoRoutine )
{
/* If pxCurrentCoRoutine is NULL then this is the first co-routine to
* be created and the co-routine data structures need initialising. */
if ( pxCurrentCoRoutine = = NULL )
{
pxCurrentCoRoutine = pxCoRoutine ;
prvInitialiseCoRoutineLists ( ) ;
}
/* Check the priority is within limits. */
if ( uxPriority > = configMAX_CO_ROUTINE_PRIORITIES )
{
uxPriority = configMAX_CO_ROUTINE_PRIORITIES - 1 ;
}
/* Fill out the co-routine control block from the function parameters. */
pxCoRoutine - > uxState = corINITIAL_STATE ;
pxCoRoutine - > uxPriority = uxPriority ;
pxCoRoutine - > uxIndex = uxIndex ;
pxCoRoutine - > pxCoRoutineFunction = pxCoRoutineCode ;
/* Initialise all the other co-routine control block parameters. */
vListInitialiseItem ( & ( pxCoRoutine - > xGenericListItem ) ) ;
vListInitialiseItem ( & ( pxCoRoutine - > xEventListItem ) ) ;
/* Set the co-routine control block as a link back from the ListItem_t.
* This is so we can get back to the containing CRCB from a generic item
* in a list. */
listSET_LIST_ITEM_OWNER ( & ( pxCoRoutine - > xGenericListItem ) , pxCoRoutine ) ;
listSET_LIST_ITEM_OWNER ( & ( pxCoRoutine - > xEventListItem ) , pxCoRoutine ) ;
/* Event lists are always in priority order. */
listSET_LIST_ITEM_VALUE ( & ( pxCoRoutine - > xEventListItem ) , ( ( TickType_t ) configMAX_CO_ROUTINE_PRIORITIES - ( TickType_t ) uxPriority ) ) ;
/* Now the co-routine has been initialised it can be added to the ready
* list at the correct priority. */
prvAddCoRoutineToReadyQueue ( pxCoRoutine ) ;
xReturn = pdPASS ;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ;
}
return xReturn ;
}
2016-08-17 23:08:22 +08:00
/*-----------------------------------------------------------*/
2021-08-10 05:13:43 +08:00
void vCoRoutineAddToDelayedList ( TickType_t xTicksToDelay ,
List_t * pxEventList )
{
TickType_t xTimeToWake ;
/* Calculate the time to wake - this may overflow but this is
* not a problem. */
xTimeToWake = xCoRoutineTickCount + xTicksToDelay ;
/* We must remove ourselves from the ready list before adding
* ourselves to the blocked list as the same list item is used for
* both lists. */
( void ) uxListRemove ( ( ListItem_t * ) & ( pxCurrentCoRoutine - > xGenericListItem ) ) ;
/* The list item will be inserted in wake time order. */
listSET_LIST_ITEM_VALUE ( & ( pxCurrentCoRoutine - > xGenericListItem ) , xTimeToWake ) ;
if ( xTimeToWake < xCoRoutineTickCount )
{
/* Wake time has overflowed. Place this item in the
* overflow list. */
vListInsert ( ( List_t * ) pxOverflowDelayedCoRoutineList , ( ListItem_t * ) & ( pxCurrentCoRoutine - > xGenericListItem ) ) ;
}
else
{
/* The wake time has not overflowed, so we can use the
* current block list. */
vListInsert ( ( List_t * ) pxDelayedCoRoutineList , ( ListItem_t * ) & ( pxCurrentCoRoutine - > xGenericListItem ) ) ;
}
if ( pxEventList )
{
/* Also add the co-routine to an event list. If this is done then the
* function must be called with interrupts disabled. */
vListInsert ( pxEventList , & ( pxCurrentCoRoutine - > xEventListItem ) ) ;
}
}
2016-08-17 23:08:22 +08:00
/*-----------------------------------------------------------*/
2021-08-10 05:13:43 +08:00
static void prvCheckPendingReadyList ( void )
{
/* Are there any co-routines waiting to get moved to the ready list? These
* are co-routines that have been readied by an ISR. The ISR cannot access
* the ready lists itself. */
while ( listLIST_IS_EMPTY ( & xPendingReadyCoRoutineList ) = = pdFALSE )
{
CRCB_t * pxUnblockedCRCB ;
/* The pending ready list can be accessed by an ISR. */
portDISABLE_INTERRUPTS ( ) ;
{
pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY ( ( & xPendingReadyCoRoutineList ) ) ;
( void ) uxListRemove ( & ( pxUnblockedCRCB - > xEventListItem ) ) ;
}
portENABLE_INTERRUPTS ( ) ;
( void ) uxListRemove ( & ( pxUnblockedCRCB - > xGenericListItem ) ) ;
prvAddCoRoutineToReadyQueue ( pxUnblockedCRCB ) ;
}
}
2016-08-17 23:08:22 +08:00
/*-----------------------------------------------------------*/
2021-08-10 05:13:43 +08:00
static void prvCheckDelayedList ( void )
{
CRCB_t * pxCRCB ;
xPassedTicks = xTaskGetTickCount ( ) - xLastTickCount ;
while ( xPassedTicks )
{
xCoRoutineTickCount + + ;
xPassedTicks - - ;
/* If the tick count has overflowed we need to swap the ready lists. */
if ( xCoRoutineTickCount = = 0 )
{
List_t * pxTemp ;
/* Tick count has overflowed so we need to swap the delay lists. If there are
* any items in pxDelayedCoRoutineList here then there is an error! */
pxTemp = pxDelayedCoRoutineList ;
pxDelayedCoRoutineList = pxOverflowDelayedCoRoutineList ;
pxOverflowDelayedCoRoutineList = pxTemp ;
}
/* See if this tick has made a timeout expire. */
while ( listLIST_IS_EMPTY ( pxDelayedCoRoutineList ) = = pdFALSE )
{
pxCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY ( pxDelayedCoRoutineList ) ;
if ( xCoRoutineTickCount < listGET_LIST_ITEM_VALUE ( & ( pxCRCB - > xGenericListItem ) ) )
{
/* Timeout not yet expired. */
break ;
}
portDISABLE_INTERRUPTS ( ) ;
{
/* The event could have occurred just before this critical
* section. If this is the case then the generic list item will
* have been moved to the pending ready list and the following
* line is still valid. Also the pvContainer parameter will have
* been set to NULL so the following lines are also valid. */
( void ) uxListRemove ( & ( pxCRCB - > xGenericListItem ) ) ;
/* Is the co-routine waiting on an event also? */
if ( pxCRCB - > xEventListItem . pxContainer )
{
( void ) uxListRemove ( & ( pxCRCB - > xEventListItem ) ) ;
}
}
portENABLE_INTERRUPTS ( ) ;
prvAddCoRoutineToReadyQueue ( pxCRCB ) ;
}
}
xLastTickCount = xCoRoutineTickCount ;
}
2016-08-17 23:08:22 +08:00
/*-----------------------------------------------------------*/
2021-08-10 05:13:43 +08:00
void vCoRoutineSchedule ( void )
{
2021-08-25 15:59:00 +08:00
/* Only run a co-routine after prvInitialiseCoRoutineLists() has been
* called. prvInitialiseCoRoutineLists() is called automatically when a
* co-routine is created. */
if ( pxDelayedCoRoutineList ! = NULL )
{
/* See if any co-routines readied by events need moving to the ready lists. */
prvCheckPendingReadyList ( ) ;
2021-08-10 05:13:43 +08:00
2021-08-25 15:59:00 +08:00
/* See if any delayed co-routines have timed out. */
prvCheckDelayedList ( ) ;
2021-08-10 05:13:43 +08:00
2021-08-25 15:59:00 +08:00
/* Find the highest priority queue that contains ready co-routines. */
while ( listLIST_IS_EMPTY ( & ( pxReadyCoRoutineLists [ uxTopCoRoutineReadyPriority ] ) ) )
2021-08-10 05:13:43 +08:00
{
2021-08-25 15:59:00 +08:00
if ( uxTopCoRoutineReadyPriority = = 0 )
{
/* No more co-routines to check. */
return ;
}
2021-08-10 05:13:43 +08:00
2021-08-25 15:59:00 +08:00
- - uxTopCoRoutineReadyPriority ;
}
2021-08-10 05:13:43 +08:00
2021-08-25 15:59:00 +08:00
/* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the co-routines
* of the same priority get an equal share of the processor time. */
listGET_OWNER_OF_NEXT_ENTRY ( pxCurrentCoRoutine , & ( pxReadyCoRoutineLists [ uxTopCoRoutineReadyPriority ] ) ) ;
2021-08-10 05:13:43 +08:00
2021-08-25 15:59:00 +08:00
/* Call the co-routine. */
( pxCurrentCoRoutine - > pxCoRoutineFunction ) ( pxCurrentCoRoutine , pxCurrentCoRoutine - > uxIndex ) ;
}
2021-08-10 05:13:43 +08:00
}
2016-08-17 23:08:22 +08:00
/*-----------------------------------------------------------*/
2021-08-10 05:13:43 +08:00
static void prvInitialiseCoRoutineLists ( void )
{
UBaseType_t uxPriority ;
2016-08-17 23:08:22 +08:00
2021-08-10 05:13:43 +08:00
for ( uxPriority = 0 ; uxPriority < configMAX_CO_ROUTINE_PRIORITIES ; uxPriority + + )
{
vListInitialise ( ( List_t * ) & ( pxReadyCoRoutineLists [ uxPriority ] ) ) ;
}
2016-08-17 23:08:22 +08:00
2021-08-10 05:13:43 +08:00
vListInitialise ( ( List_t * ) & xDelayedCoRoutineList1 ) ;
vListInitialise ( ( List_t * ) & xDelayedCoRoutineList2 ) ;
vListInitialise ( ( List_t * ) & xPendingReadyCoRoutineList ) ;
2016-08-17 23:08:22 +08:00
2021-08-10 05:13:43 +08:00
/* Start with pxDelayedCoRoutineList using list1 and the
* pxOverflowDelayedCoRoutineList using list2. */
pxDelayedCoRoutineList = & xDelayedCoRoutineList1 ;
pxOverflowDelayedCoRoutineList = & xDelayedCoRoutineList2 ;
}
2016-08-17 23:08:22 +08:00
/*-----------------------------------------------------------*/
2021-08-10 05:13:43 +08:00
BaseType_t xCoRoutineRemoveFromEventList ( const List_t * pxEventList )
{
CRCB_t * pxUnblockedCRCB ;
BaseType_t xReturn ;
/* This function is called from within an interrupt. It can only access
* event lists and the pending ready list. This function assumes that a
* check has already been made to ensure pxEventList is not empty. */
pxUnblockedCRCB = ( CRCB_t * ) listGET_OWNER_OF_HEAD_ENTRY ( pxEventList ) ;
( void ) uxListRemove ( & ( pxUnblockedCRCB - > xEventListItem ) ) ;
vListInsertEnd ( ( List_t * ) & ( xPendingReadyCoRoutineList ) , & ( pxUnblockedCRCB - > xEventListItem ) ) ;
if ( pxUnblockedCRCB - > uxPriority > = pxCurrentCoRoutine - > uxPriority )
{
xReturn = pdTRUE ;
}
else
{
xReturn = pdFALSE ;
}
return xReturn ;
}
2016-08-17 23:08:22 +08:00
2019-11-28 15:27:47 -03:00
# endif /* configUSE_CO_ROUTINES == 0 */