varch/source/08_coroutine/coroutine.c
Lamdonn bf21a64ab2 Add the coroutine readme
Add feature to bind CoTask and CoTimer to the specified CoScheduler
Improve functional safety
TODO:
1. Compatibility with coroutine API highly intensive IO, sockets, etc
2. Fix running fail on arm64
2025-11-20 00:58:03 +08:00

1608 lines
51 KiB
C

/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file coroutine.c
* \unit coroutine
* \brief This is a C language coroutine library
* \author Lamdonn
* \version v0.2.0
* \license GPL-2.0
* \copyright Copyright (C) 2025 Lamdonn.
********************************************************************************************************/
#include "coroutine.h"
#if (COROUTINE_STACK_DEFAULT_SIZE < 32)
#error "COROUTINE_STACK_DEFAULT_SIZE must be greater than 32"
#endif
/**
* \brief: Coroutine scheduler state
* \note: Coroutine scheduler state is used to indicate the current state of the coroutine scheduler.
* The coroutine scheduler state is updated periodically with the given tick interval.
*/
#define COSCHEDULER_STATE_INTI (0) /**< Coroutine scheduler state initialize */
#define COSCHEDULER_STATE_EXIT (1) /**< Coroutine scheduler state exit */
#define COSCHEDULER_STATE_START (2) /**< Coroutine scheduler state start */
#define COSCHEDULER_STATE_RUNNING (3) /**< Coroutine scheduler state running */
#define COSCHEDULER_STATE_SCHEDULE (4) /**< Coroutine scheduler state schedule */
/**
* \brief: Coroutine task state
* \note: Coroutine task state is used to indicate the current state of the coroutine task.
* The coroutine task state is updated periodically with the given tick interval.
*/
#define COTASK_STATE_INIT (0) /**< Coroutine task state initialize */
#define COTASK_STATE_READY (1) /**< Coroutine task state ready */
#define COTASK_STATE_RUNNING (2) /**< Coroutine task state running */
#define COTASK_STATE_SUSPEND (3) /**< Coroutine task state suspend */
#define COTASK_STATE_DELETED (4) /**< Coroutine task state deleted */
/**
* \brief: Coroutine task flag
* \note: Coroutine task flag is used to indicate the current flag of the coroutine task.
* The coroutine task flag is updated periodically with the given tick interval.
*/
#define COTASK_FLAG_ALLOCED (0x01) /**< Coroutine task flag alloced */
#define COTASK_FLAG_DYNC_TCB (0x02) /**< Coroutine task flag dynamic tcb */
#define COTASK_FLAG_DYNC_STACK (0x04) /**< Coroutine task flag dynamic stack */
/**
* \brief: Coroutine task stack mark
* \note: Coroutine task stack mark is used to mark the end of the coroutine task stack.
* The coroutine task stack mark is used to check the stack overflow.
* During stack counting, it is determined whether the stack address is used without this MARK defined by the stack value.
*/
#define COTASK_STACK_MARK (0xAAAAAAAA)
/**
* \brief: Coroutine scheduler state set
* \param pScheduler: Coroutine scheduler
* \param state: Coroutine scheduler state
* \note: This function is used to set the coroutine scheduler state.
*/
#define COSCHEDULER_SET_STATE(pScheduler, state) longjmp((pScheduler)->env, (state))
/**
* \brief: Coroutine scheduler state get
* \param pScheduler: Coroutine scheduler
* \return: Coroutine scheduler state
* \note: This function is used to get the coroutine scheduler state.
*/
#define COSCHEDULER_GET_STATE(pScheduler) setjmp((pScheduler)->env)
/**
* \brief: Coroutine scheduler
*/
typedef struct CoScheduler CoScheduler;
/**
* \brief: Coroutine task run function
* \param pScheduler: Coroutine scheduler
* \note: This function must be called in the coroutine context, no matter
* whether the coroutine is running or not.
*/
typedef void (*CoTaskRun_t)(CoScheduler *pScheduler);
/**
* \brief: Coroutine task stack
* \note: Coroutine task stack is used to store the coroutine task context.
*/
struct CoStack
{
uint8_t base[COROUTINE_STACK_DEFAULT_SIZE]; /**< Coroutine task stack base address */
};
/**
* \brief: Coroutine task
* \note: Coroutine task is a coroutine unit, each coroutine task has its own stack,
* and can be scheduled by the coroutine scheduler.
*/
struct CoTask
{
CoTask_t next; /**< Double-link list, next coroutine task */
CoTask_t prev; /**< Double-link list, previous coroutine task */
CoScheduler *pScheduler; /**< Coroutine scheduler, point to the scheduler that created this coroutine task */
uint8_t state; /**< Coroutine task state */
void *stackBase; /**< Coroutine task stack base address */
size_t stackSize; /**< Coroutine task stack size */
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
size_t stackMaxUsed; /**< Coroutine task stack max used size */
#endif
uint64_t nextRunTick; /**< When the coroutine task is suspended, coroutine task next run tick */
uint32_t flag; /**< Coroutine task flag */
CoTaskEntry_t entry; /**< Coroutine task entry function */
void *arg; /**< Coroutine task argument address */
CoEvent *pEvent; /**< Coroutine task event, point to the event that the coroutine task is waiting for */
jmp_buf env; /**< Coroutine task environment, used for context switch */
};
/**
* \brief: Coroutine timer
* \note: Coroutine timer is a coroutine unit, each coroutine timer has its own period,
* and can be scheduled by the coroutine scheduler.
*/
struct CoTimer
{
CoTimer_t next; /**< Double-link list, next coroutine timer */
CoTimer_t prev; /**< Double-link list, previous coroutine timer */
CoScheduler *pScheduler; /**< Coroutine scheduler, point to the scheduler that created this coroutine timer */
uint64_t periodTick; /**< Coroutine timer period tick */
uint64_t nextRunTick; /**< Coroutine timer next run tick */
uint32_t flag; /**< Coroutine timer flag */
CoTimerEntry_t entry; /**< Coroutine timer entry function */
};
/**
* \brief: Coroutine scheduler
* \note: Coroutine scheduler is a coroutine unit, each coroutine scheduler has its own coroutine task list and coroutine timer list,
* and can be scheduled by the coroutine scheduler.
*/
struct CoScheduler
{
CoTask_t CoTaskCurrent; /**< Current coroutine task */
CoTask_t CoTaskList; /**< Coroutine task list head */
size_t CoTaskListSize; /**< Coroutine task list size */
CoTimer_t CoTimerCurrent; /**< Current coroutine timer */
CoTimer_t CoTimerList; /**< Coroutine timer list head */
size_t CoTimerListSize; /**< Coroutine timer list size */
void *stackTop; /**< Coroutine scheduler stack top address */
jmp_buf env; /**< Coroutine scheduler environment, used for context switch */
CoTick_t tick; /**< Coroutine scheduler tick function */
uint32_t tickInterval; /**< Coroutine scheduler tick interval, indicates how many us each tick is */
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
/** The results of each measurement period are added to this queue,
* so the load measured at the last `COROUTINE_LOADING_CALCULATE_QSIZE` time point is kept
* and averaged as the real-time load */
uint16_t loadQueueBase[COROUTINE_LOADING_CALCULATE_QSIZE]; /**< Coroutine scheduler loading queue base */
uint16_t loadQueueSize; /**< Coroutine scheduler loading queue size */
uint16_t loadQueueTail; /**< Coroutine scheduler loading queue tail */
uint16_t curLoad; /**< Coroutine scheduler current load */
uint16_t maxLoad; /**< Coroutine scheduler max load */
uint64_t uTick; /**< Coroutine scheduler measures and calculates the load once every `uTick` */
uint64_t sTick; /**< Coroutine scheduler tick that start measures */
uint64_t pTick; /**< Coroutine scheduler tick that previous start measurement task run */
uint64_t cTick; /**< Coroutine scheduler tick that current tick */
uint64_t rTick; /**< Coroutine scheduler tick that task running */
#endif
CoTaskRun_t CoTaskRun; /**< Coroutine scheduler task run function, prevents function compilation from being optimized inline */
};
#if (COROUTINE_SCHEDULER_NUMBER > 0)
static CoScheduler sCoScheduler[COROUTINE_SCHEDULER_NUMBER] = {0};
#else
#error "COROUTINE_SCHEDULER_NUMBER must be greater than 0"
#endif
#if (COROUTINE_STATIC_TASK_MAX_NUMBER > 0)
/**< Coroutine task static array */
struct CoTask sCoTasks[COROUTINE_STATIC_TASK_MAX_NUMBER] = {0};
#endif
#if (COROUTINE_STATIC_TIMER_MAX_NUMBER > 0)
/**< Coroutine timer static array */
struct CoTimer sCoTimers[COROUTINE_STATIC_TIMER_MAX_NUMBER] = {0};
#endif
#if (COROUTINE_STATIC_STACK_MAX_NUMBER > 0)
/**< Coroutine stack static array */
struct CoStack sCoStacks[COROUTINE_STATIC_STACK_MAX_NUMBER] = {0};
#endif
/**
* \brief: Coroutine scheduler lock
* \note: Coroutine scheduler lock is used to lock the coroutine scheduler.
* The coroutine scheduler lock is used to prevent the coroutine scheduler from being accessed by multiple threads.
*/
#if (COROUTINE_SCHEDULER_NUMBER > 1)
static CoLock_t sCoSchedulerLock = NULL;
#else
#define sCoSchedulerLock()
#endif
/**
* \brief: Coroutine scheduler unlock
* \note: Coroutine scheduler unlock is used to unlock the coroutine scheduler.
* The coroutine scheduler unlock is used to release the lock of the coroutine scheduler.
*/
#if (COROUTINE_SCHEDULER_NUMBER > 1)
static CoUnlock_t sCoSchedulerUnlock = NULL;
#else
#define sCoSchedulerUnlock()
#endif
/**
* \brief: Coroutine scheduler malloc
* \note: Coroutine scheduler malloc is used to malloc the coroutine scheduler.
* The coroutine scheduler malloc is used to malloc the memory for the coroutine scheduler.
*/
static CoMalloc_t sCoSchedulerMalloc = NULL;
/**
* \brief: Coroutine scheduler free
* \note: Coroutine scheduler free is used to free the coroutine scheduler.
* The coroutine scheduler free is used to free the memory for the coroutine scheduler.
*/
static CoFree_t sCoSchedulerFree = NULL;
/**
* \brief: Coroutine scheduler lock
* \note: Coroutine scheduler lock is used to lock the coroutine scheduler.
* Only for a single scheduler.
*/
#if (COROUTINE_SCHEDULER_NUMBER > 1)
static void CoScheduler_Lock(void) { }
#endif
/**
* \brief: Coroutine scheduler unlock
* \note: Coroutine scheduler unlock is used to unlock the coroutine scheduler.
* Only for a single scheduler.
*/
#if (COROUTINE_SCHEDULER_NUMBER > 1)
static void CoScheduler_Unlock(void) { }
#endif
static void EndlessLoop(void)
{
while (1)
{
/* Do nothing */
}
}
static void StackOverflowCheck(uint8_t *pStack)
{
if (*(uint32_t *)pStack != COTASK_STACK_MARK)
{
EndlessLoop();
}
}
static void Suspend(CoScheduler *pScheduler)
{
/* Block the coroutine task if the task stack be brokend */
StackOverflowCheck(pScheduler->CoTaskCurrent->stackBase);
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
/* Calculate the loading time */
pScheduler->cTick = pScheduler->tick();
/* Update the loading time */
if (pScheduler->pTick == 0)
{
pScheduler->pTick = pScheduler->cTick;
}
/* Update the running time */
if (pScheduler->cTick > pScheduler->pTick)
{
/* Add the running time */
pScheduler->rTick += (pScheduler->cTick - pScheduler->pTick);
}
#endif
/* Yield to the next coroutine task */
COSCHEDULER_SET_STATE(pScheduler, COSCHEDULER_STATE_SCHEDULE);
}
static void Resume(CoScheduler *pScheduler)
{
/* Block the coroutine task if the task stack be brokend */
StackOverflowCheck(pScheduler->CoTaskCurrent->stackBase);
pScheduler->CoTaskCurrent->state = COTASK_STATE_RUNNING;
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
/* Record the task cut time */
pScheduler->pTick = pScheduler->tick();
#endif
/* Jump to the coroutine */
longjmp(pScheduler->CoTaskCurrent->env, COSCHEDULER_STATE_RUNNING);
}
static CoScheduler *GetCurCoScheduler(void)
{
void *sp = NULL;
CoScheduler *pScheduler = NULL;
sCoSchedulerLock();
for (int i = 0; i < COROUTINE_SCHEDULER_NUMBER; i++)
{
pScheduler = &sCoScheduler[i];
if (pScheduler->tick == NULL) continue;
/* Check sp wether in the current scheduler stack */
COROUTINE_GET_SP(sp);
if (pScheduler->CoTaskCurrent && pScheduler->CoTaskCurrent->stackBase <= sp && sp < (void *)((char *)pScheduler->CoTaskCurrent->stackBase + pScheduler->CoTaskCurrent->stackSize))
{
break;
}
}
sCoSchedulerUnlock();
return pScheduler;
}
static CoScheduler *DistributeCoScheduler(void)
{
CoScheduler *pScheduler = NULL;
CoScheduler *minCoScheduler = NULL;
sCoSchedulerLock();
for (int i = 0; i < COROUTINE_SCHEDULER_NUMBER; i++)
{
pScheduler = &sCoScheduler[i];
if (pScheduler->tick == NULL) continue;
if (minCoScheduler == NULL)
{
minCoScheduler = pScheduler;
}
else if (pScheduler->CoTaskListSize < minCoScheduler->CoTaskListSize)
{
minCoScheduler = pScheduler;
}
}
sCoSchedulerUnlock();
return minCoScheduler;
}
static CoScheduler *DistributeCoSchedulerForTimer(void)
{
CoScheduler *pScheduler = NULL;
CoScheduler *minCoScheduler = NULL;
sCoSchedulerLock();
for (int i = 0; i < COROUTINE_SCHEDULER_NUMBER; i++)
{
pScheduler = &sCoScheduler[i];
if (pScheduler->tick == NULL) continue;
if (minCoScheduler == NULL)
{
minCoScheduler = pScheduler;
}
else if (pScheduler->CoTimerListSize < minCoScheduler->CoTimerListSize)
{
minCoScheduler = pScheduler;
}
}
sCoSchedulerUnlock();
return minCoScheduler;
}
#if (COROUTINE_STATIC_STACK_MAX_NUMBER > 0)
static void* CreateCoStack()
{
for (int i = 0; i < COROUTINE_STATIC_STACK_MAX_NUMBER; i++)
{
if (*(uint32_t *)sCoStacks[i].base == 0)
{
*(uint32_t *)sCoStacks[i].base = COTASK_STACK_MARK;
return (void *)sCoStacks[i].base;
}
}
return NULL;
}
#endif
#if (COROUTINE_STATIC_STACK_MAX_NUMBER > 0)
static void DeleteCoStack(uint8_t *pStack)
{
if ((uint8_t *)(&sCoStacks[0]) <= pStack && pStack <= ((uint8_t *)(&sCoStacks[COROUTINE_STATIC_STACK_MAX_NUMBER - 1])))
{
*(uint32_t *)pStack = 0;
}
}
#endif
static CoTask_t CreateCoTask(CoScheduler *pScheduler)
{
#if (COROUTINE_STATIC_TASK_MAX_NUMBER > 0)
/* Find the first unallocated coroutine task */
for (int i = 0; i < COROUTINE_STATIC_TASK_MAX_NUMBER; i++)
{
/* Check the coroutine task is allocated */
if (!(sCoTasks[i].flag & COTASK_FLAG_ALLOCED))
{
/* Clear the flag */
sCoTasks[i].flag = 0;
/* Set the flag as allocated */
sCoTasks[i].flag |= COTASK_FLAG_ALLOCED;
return &sCoTasks[i];
}
}
#endif
/* Allocate the coroutine task dynamically */
if (sCoSchedulerMalloc)
{
CoTask_t pCoroutine = (CoTask_t)sCoSchedulerMalloc(sizeof(struct CoTask));
if (pCoroutine)
{
/* Set the flag as allocated */
pCoroutine->flag = COTASK_FLAG_ALLOCED | COTASK_FLAG_DYNC_TCB;
return pCoroutine;
}
}
return NULL;
}
static void DeleteCoTask(CoScheduler *pScheduler, CoTask_t CoTaskCurrent)
{
#if (COROUTINE_STATIC_TASK_MAX_NUMBER > 0)
/* Check the coroutine task is statically allocated */
if (&sCoTasks[0] <= CoTaskCurrent && CoTaskCurrent <= &sCoTasks[COROUTINE_STATIC_TASK_MAX_NUMBER - 1])
{
/* Clear the flag */
CoTaskCurrent->flag &= ~COTASK_FLAG_ALLOCED;
return;
}
else
#endif
/* Check the coroutine task is dynamically allocated */
{
#if (COROUTINE_STATIC_STACK_MAX_NUMBER > 0)
DeleteCoStack(CoTaskCurrent->stackBase);
#endif
/* Check the stack is dynamically allocated */
if (CoTaskCurrent->flag & COTASK_FLAG_DYNC_STACK)
{
/* Free the stack */
sCoSchedulerFree(CoTaskCurrent->stackBase);
}
/* Check the TCB is dynamically allocated */
if (CoTaskCurrent->flag & COTASK_FLAG_DYNC_TCB)
{
/* Free the TCB */
sCoSchedulerFree(CoTaskCurrent);
}
}
}
static void InsertCoTaskList(CoScheduler *pScheduler, CoTask_t Task)
{
/* Check the coroutine task list is empty */
if (pScheduler->CoTaskList == NULL)
{
/* Set the coroutine task as the head and tail */
pScheduler->CoTaskList = Task;
Task->next = Task;
Task->prev = Task;
}
/* Insert the coroutine task to the tail of the list */
else
{
/* Get the head and tail of the list */
CoTask_t Head = pScheduler->CoTaskList;
CoTask_t Tail = Head->prev;
/* Insert the coroutine task to the tail */
Tail->next = Task;
Task->prev = Tail;
Head->prev = Task;
Task->next = Head;
}
pScheduler->CoTaskListSize++;
}
static void EraseCoTaskList(CoScheduler *pScheduler, CoTask_t CoTaskCurrent)
{
/* Check the coroutine task is the head of the list */
if (CoTaskCurrent == pScheduler->CoTaskList)
{
/* Set the next coroutine task as the head */
pScheduler->CoTaskList = CoTaskCurrent->next;
}
/* Erase the coroutine task from the list */
CoTaskCurrent->prev->next = CoTaskCurrent->next;
CoTaskCurrent->next->prev = CoTaskCurrent->prev;
/* Decrease the number of coroutine tasks */
pScheduler->CoTaskListSize--;
/* Check the coroutine task list is empty */
if (pScheduler->CoTaskListSize == 0)
{
pScheduler->CoTaskList = NULL;
}
}
static void CoTaskRun(CoScheduler *pScheduler)
{
/* Set the coroutine task state as running */
pScheduler->CoTaskCurrent->state = COTASK_STATE_RUNNING;
/* Run the coroutine task */
pScheduler->CoTaskCurrent->entry(pScheduler->CoTaskCurrent->arg);
/* Block the coroutine task if the task stack be brokend */
StackOverflowCheck(pScheduler->CoTaskCurrent->stackBase);
/* Set the coroutine task state as deleted */
pScheduler->CoTaskCurrent->state = COTASK_STATE_DELETED;
/* Return control to the scheduler so that it can delete the coroutine task */
Suspend(pScheduler);
}
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
static void CoTask_StackInit(uint32_t *pStack, size_t u32Count)
{
/* Initialize the stack with the mark */
for (size_t i = 0; i < u32Count; i++)
{
pStack[i] = COTASK_STACK_MARK;
}
}
#endif
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
static uint32_t CoTask_StackUsed(uint32_t *pStack, size_t u32Count)
{
uint32_t indexLeft = 0;
uint32_t indexRight = u32Count;
uint32_t indexCenter = u32Count / 2;
/* Binary search the mark in the stack */
while (indexLeft < indexRight)
{
/* Check the mark is in the left half */
if (pStack[indexCenter] == COTASK_STACK_MARK)
{
indexLeft = indexCenter + 1;
}
/* Check the mark is in the right half */
else
{
indexRight = indexCenter;
}
/* Update the center index */
indexCenter = (indexLeft + indexRight) / 2;
}
/* Calculate the stack used size */
return (u32Count - indexLeft) * sizeof(uint32_t);
}
#endif
CoTask_t CoTask_CreateP(CoTaskEntry_t entry, CoTaskCreatePara *pPara)
{
CoScheduler *pScheduler = NULL;
CoTask_t Task = NULL;
void *stackBase = NULL;
size_t stackSize = 0;
uint32_t flag = 0;
CoTaskCreatePara para = {.pStack=NULL,.stackSize=0,.arg=NULL,.schedulerId=-1};
/* Check the entry function is valid */
if (entry == NULL)
{
return NULL;
}
/* Check the create parameter is valid */
if (pPara == NULL)
{
pPara = &para;
}
/* Auto distribute the scheduler if the scheduler id is negative */
if (pPara->schedulerId < 0)
{
pScheduler = DistributeCoScheduler();
}
/* Manual distribute the scheduler if the scheduler id is valid */
else
{
/* Check the scheduler id is valid */
if (pPara->schedulerId >= COROUTINE_SCHEDULER_NUMBER)
{
return NULL;
}
/* Get the scheduler by id */
pScheduler = &sCoScheduler[pPara->schedulerId];
}
/* Check the scheduler is valid */
if (pScheduler->tick == NULL)
{
return NULL;
}
stackBase = pPara->pStack;
stackSize = pPara->stackSize;
/* If stackBase is empty, an automatic allocation of stackBase is attempted */
if (stackBase == NULL)
{
if (stackSize == 0)
{
/* Check the stack size is valid, or use the defualt stack size */
stackSize = COROUTINE_STACK_DEFAULT_SIZE;
#if (COROUTINE_STATIC_STACK_MAX_NUMBER > 0)
/* Allocate the stack memory */
stackBase = CreateCoStack();
#endif
}
if (stackBase == NULL)
{
/* Check the malloc function is valid */
if (sCoSchedulerMalloc)
{
/* Allocate the stack memory */
stackBase = sCoSchedulerMalloc(stackSize);
if (stackBase == NULL)
{
return NULL;
}
/* Set the flag as dynamic stack */
flag |= COTASK_FLAG_DYNC_STACK;
}
else
{
return NULL;
}
}
}
else
{
/* Check the stack size is valid */
if (stackSize == 0)
{
return NULL;
}
}
/* Create the coroutine task */
Task = CreateCoTask(pScheduler);
if (Task == NULL)
{
#if (COROUTINE_STATIC_STACK_MAX_NUMBER > 0)
/* Free the stack memory */
DeleteCoStack(stackBase);
#endif
/* Free the dynamic stack memory if it is allocated */
if (flag & COTASK_FLAG_DYNC_STACK)
{
sCoSchedulerFree(stackBase);
}
return NULL;
}
/* Set the coroutine task parameters */
Task->entry = entry;
Task->arg = pPara->arg;
Task->stackBase = stackBase;
Task->stackSize = stackSize;
Task->next = NULL;
Task->prev = NULL;
Task->state = COTASK_STATE_INIT;
Task->pEvent = NULL;
Task->nextRunTick = 0;
Task->flag |= flag;
Task->pScheduler = pScheduler;
/* Insert the coroutine task into the scheduler */
InsertCoTaskList(pScheduler, Task);
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
/* Initialize the stack */
Task->stackMaxUsed = 0;
CoTask_StackInit((uint32_t *)stackBase, stackSize / sizeof(uint32_t));
#endif
return Task;
}
int CoTask_Delete(CoTask_t CoTask)
{
/* Check the coroutine task is valid */
if (CoTask == NULL)
{
return COROUTINE_E_INVALID_PARAMETER;
}
/* Check the coroutine task is running */
CoScheduler *pScheduler = CoTask->pScheduler;
/* Set the coroutine task state as deleted */
CoTask->state = COTASK_STATE_DELETED;
/* Return control to the scheduler so that it can clean up the task */
if (COSCHEDULER_STATE_RUNNING != setjmp(pScheduler->CoTaskCurrent->env))
{
Suspend(pScheduler);
}
return 0;
}
CoTask_t CoTask_Self(void)
{
/* Get the current coroutine scheduler */
CoScheduler *pScheduler = GetCurCoScheduler();
/* Return the current coroutine task */
return pScheduler->CoTaskCurrent;
}
int CoTask_SchedulerId(void)
{
/* Get the current coroutine scheduler */
CoScheduler *pScheduler = GetCurCoScheduler();
/* Return the current coroutine scheduler id */
return (int)(pScheduler - sCoScheduler);
}
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
size_t CoTask_StackMaxUsed(CoTask_t CoTask)
{
/* Check the coroutine task is valid */
if (CoTask == NULL)
{
return 0;
}
/* Check the coroutine task is initialized or deleted */
if (CoTask->state == COTASK_STATE_INIT || CoTask->state == COTASK_STATE_DELETED)
{
return 0;
}
/* Calculate the stack max used */
CoTask->stackMaxUsed = CoTask_StackUsed((uint32_t *)CoTask->stackBase, CoTask->stackSize / sizeof(uint32_t));
return CoTask->stackMaxUsed;
}
#endif
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
size_t CoTask_StackCurUsed(CoTask_t CoTask)
{
char *sp = NULL;
/* Get the current stack pointer */
COROUTINE_GET_SP(sp);
/* Calculate the current stack used */
return (size_t)((char *)CoTask->stackBase + CoTask->stackSize - sp);
}
#endif
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
uint16_t CoScheduler_CurLoad(uint32_t CoSchedulerId)
{
CoScheduler *pScheduler = NULL;
/* Check the coroutine scheduler is valid */
if (CoSchedulerId >= COROUTINE_SCHEDULER_NUMBER)
{
return 0;
}
/* Get the coroutine scheduler */
pScheduler = &sCoScheduler[CoSchedulerId];
if (pScheduler->tick == NULL)
{
return 0;
}
/* Return the current loading */
return pScheduler->curLoad;
}
#endif
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
uint16_t CoScheduler_MaxLoad(uint32_t CoSchedulerId)
{
CoScheduler *pScheduler = NULL;
/* Check the coroutine scheduler is valid */
if (CoSchedulerId >= COROUTINE_SCHEDULER_NUMBER)
{
return 0;
}
/* Get the coroutine scheduler */
pScheduler = &sCoScheduler[CoSchedulerId];
if (pScheduler->tick == NULL)
{
return 0;
}
/* Return the maximum loading */
return pScheduler->maxLoad;
}
#endif
void CoEvent_Init(CoEvent *pEvent)
{
/* Check the coroutine event is valid */
if (pEvent == NULL)
{
return;
}
/* Initialize the event flag */
pEvent->flag = 0;
}
uint32_t CoTask_WaitP(CoTaskWaitPara *pPara)
{
CoScheduler *pScheduler = NULL;
uint32_t evs = 0;
CoTaskWaitPara para = {.pEvent=NULL,.ms=0,.tick=0};
uint64_t tick = 0;
/* Check the wait parameter */
if (pPara == NULL)
{
/* Use the default wait parameter, it will waiting nothing */
pPara = &para;
}
/* Get the current coroutine scheduler */
pScheduler = GetCurCoScheduler();
/* Check the wait parameter */
/* Default wait time is 0, means no wait */
if (pPara->ms != 0 || pPara->tick != 0)
{
/* Check the wait time ms */
if (pPara->ms != 0)
{
tick = pPara->ms * 1000000 / pScheduler->tickInterval;
}
/* Check the wait time tick */
if (pPara->tick != 0)
{
/* Select the smaller one */
if (tick == 0 || tick > pPara->tick)
{
tick = pPara->tick;
}
}
}
/* Set the next run tick */
if (tick == 0 && pPara->pEvent != NULL)
{
pScheduler->CoTaskCurrent->nextRunTick = 0;
}
else
{
pScheduler->CoTaskCurrent->nextRunTick = pScheduler->tick() + tick;
}
/* Set the event */
pScheduler->CoTaskCurrent->pEvent = pPara->pEvent;
/* Set the coroutine task state as suspend */
pScheduler->CoTaskCurrent->state = COTASK_STATE_SUSPEND;
/* Return control to the scheduler so that it can schedule other tasks */
if (COSCHEDULER_STATE_RUNNING != setjmp(pScheduler->CoTaskCurrent->env))
{
Suspend(pScheduler);
}
if (pPara->pEvent != NULL)
{
/* Get the event flag */
evs = pPara->pEvent->flag;
/* Clear the event flag */
pPara->pEvent->flag = 0;
}
/* Return the event flag */
return evs;
}
void CoEvent_Notify(CoEvent *pEvent, uint32_t evs)
{
/* Check the coroutine event is valid */
if (pEvent == NULL)
{
return;
}
/* Check the event flag */
if (evs == 0)
{
return;
}
sCoSchedulerLock();
/* Set the event flag */
pEvent->flag |= evs;
sCoSchedulerUnlock();
}
static CoTimer_t CreateCoTimer(CoScheduler *pScheduler)
{
#if (COROUTINE_STATIC_TIMER_MAX_NUMBER > 0)
/* Search the free coroutine timer */
for (int i = 0; i < COROUTINE_STATIC_TIMER_MAX_NUMBER; i++)
{
/* Check the coroutine timer is free */
if (!(sCoTimers[i].flag & COTASK_FLAG_ALLOCED))
{
/* Allocate the coroutine timer */
sCoTimers[i].flag |= COTASK_FLAG_ALLOCED;
return &sCoTimers[i];
}
}
#endif
/* Check the dynamic coroutine timer malloc function */
if (sCoSchedulerMalloc)
{
/* Allocate the coroutine timer */
CoTimer_t pCoroutineTimer = (CoTimer_t)sCoSchedulerMalloc(sizeof(struct CoTimer));
if (pCoroutineTimer)
{
/* Initialize the coroutine timer */
pCoroutineTimer->flag = COTASK_FLAG_ALLOCED | COTASK_FLAG_DYNC_TCB;
return pCoroutineTimer;
}
}
return NULL;
}
static void DeleteCoTimer(CoScheduler *pScheduler, CoTimer_t CoTimerCurrent)
{
#if (COROUTINE_STATIC_TIMER_MAX_NUMBER > 0)
/* Check the coroutine timer is static */
if (&sCoTimers[0] <= CoTimerCurrent && CoTimerCurrent <= &sCoTimers[COROUTINE_STATIC_TIMER_MAX_NUMBER - 1])
{
/* Free the coroutine timer */
CoTimerCurrent->flag &= ~COTASK_FLAG_ALLOCED;
return;
}
else
#endif
/* Check the coroutine timer is dynamic */
{
/* Check the coroutine timer is dynamic */
if (CoTimerCurrent->flag & COTASK_FLAG_DYNC_TCB)
{
/* Free the coroutine timer */
sCoSchedulerFree(CoTimerCurrent);
}
}
}
static void InsertCoTimerList(CoScheduler *pScheduler, CoTimer_t NewCoTimer)
{
/* Check the coroutine timer list is empty */
if (pScheduler->CoTimerList == NULL)
{
pScheduler->CoTimerList = NewCoTimer;
/* Initialize the coroutine timer list */
NewCoTimer->next = NewCoTimer;
NewCoTimer->prev = NewCoTimer;
}
else
{
/* Insert the coroutine timer to the end of the list */
CoTimer_t Head = pScheduler->CoTimerList;
CoTimer_t Tail = Head->prev;
/* Insert the coroutine timer to the end of the list */
Tail->next = NewCoTimer;
NewCoTimer->prev = Tail;
Head->prev = NewCoTimer;
NewCoTimer->next = Head;
}
/* Update the coroutine timer list size */
pScheduler->CoTimerListSize++;
}
static void EraseCoTimerList(CoScheduler *pScheduler, CoTimer_t CoTimerCurrent)
{
/* Check the coroutine timer is the head of the list */
if (CoTimerCurrent == pScheduler->CoTimerList)
{
/* Update the coroutine timer list head */
pScheduler->CoTimerList = CoTimerCurrent->next;
}
/* Erase the coroutine timer from the list */
CoTimerCurrent->prev->next = CoTimerCurrent->next;
CoTimerCurrent->next->prev = CoTimerCurrent->prev;
/* Update the coroutine timer list size */
pScheduler->CoTimerListSize--;
/* Check the coroutine timer list is empty */
if (pScheduler->CoTimerListSize == 0)
{
pScheduler->CoTimerList = NULL;
}
}
CoTimer_t CoTimer_CreateP(CoTimerEntry_t entry, CoTimerCreatePara *pPara)
{
CoScheduler *pScheduler = NULL;
CoTimer_t Timer = NULL;
uint64_t tick = 0;
/* Check the coroutine timer entry is valid */
if (entry == NULL)
{
return NULL;
}
/* Check the coroutine timer parameter is valid */
if (pPara == NULL)
{
return NULL;
}
/* Check the coroutine timer period is valid */
if (pPara->ms == 0 && pPara->tick == 0)
{
return NULL;
}
/* Auto distribute the scheduler if the scheduler id is negative */
if (pPara->schedulerId < 0)
{
pScheduler = DistributeCoSchedulerForTimer();
}
/* Manual distribute the scheduler if the scheduler id is valid */
else
{
/* Check the scheduler id is valid */
if (pPara->schedulerId >= COROUTINE_SCHEDULER_NUMBER)
{
return NULL;
}
/* Get the scheduler by id */
pScheduler = &sCoScheduler[pPara->schedulerId];
}
/* Check the scheduler is valid */
if (pScheduler->tick == NULL)
{
return NULL;
}
/* Check the wait time ms */
if (pPara->ms != 0)
{
tick = pPara->ms * 1000000 / pScheduler->tickInterval;
}
/* Check the wait time tick */
if (pPara->tick != 0)
{
/* Select the smaller one */
if (tick == 0 || tick > pPara->tick)
{
tick = pPara->tick;
}
}
/* Create the coroutine timer */
Timer = CreateCoTimer(pScheduler);
if (Timer == NULL)
{
return NULL;
}
/* Initialize the coroutine timer */
Timer->entry = entry;
Timer->periodTick = tick;
Timer->nextRunTick = pScheduler->tick() + tick;
Timer->next = NULL;
Timer->prev = NULL;
Timer->pScheduler = pScheduler;
/* Insert the coroutine timer to the list */
InsertCoTimerList(pScheduler, Timer);
return Timer;
}
void CoTimer_Delete(CoTimer_t Timer)
{
CoScheduler *pScheduler = NULL;
/* Check the coroutine timer is valid */
if (Timer == NULL)
{
return;
}
pScheduler = Timer->pScheduler;
/* Erase the coroutine timer from the list */
EraseCoTimerList(pScheduler, Timer);
/* Free the coroutine timer */
DeleteCoTimer(pScheduler, Timer);
}
CoTimer_t CoTimer_Self(void)
{
CoTimer_t Self = NULL;
CoScheduler *pScheduler = NULL;
CoScheduler *minCoScheduler = NULL;
void *sp = NULL;
size_t minSize = 0;
sCoSchedulerLock();
for (int i = 0; i < COROUTINE_SCHEDULER_NUMBER; i++)
{
pScheduler = &sCoScheduler[i];
if (pScheduler->tick == NULL) continue;
/* Get the current stack pointer */
COROUTINE_GET_SP(sp);
/* Check the coroutine scheduler stack top is above the current stack pointer */
if (pScheduler->stackTop - sp > 0)
{
/* Check the coroutine scheduler stack top is above the current stack pointer */
if (minSize == 0)
{
minSize = pScheduler->stackTop - sp;
minCoScheduler = pScheduler;
}
/* Check the coroutine scheduler stack top is above the current stack pointer */
else
{
size_t size = pScheduler->stackTop - sp;
if (pScheduler->stackTop - sp < minSize)
{
minSize = size;
minCoScheduler = pScheduler;
}
}
}
/* Get the coroutine timer current of the closest scheduler */
Self = minCoScheduler->CoTimerCurrent;
}
sCoSchedulerUnlock();
return Self;
}
int CoTimer_SchedulerId(void)
{
CoTimer_t Timer = NULL;
CoScheduler *pScheduler = NULL;
/* Get the current coroutine timer */
Timer = CoTimer_Self();
if (Timer == NULL)
{
return -1;
}
/* Get the current coroutine scheduler */
pScheduler = Timer->pScheduler;
/* Return the current coroutine scheduler id */
return (int)(pScheduler - sCoScheduler);
}
int CoScheduler_InitP(uint32_t CoSchedulerId, CoTick_t tick, uint32_t tickInterval, CoSchedulerInitPara *pPara)
{
CoSchedulerInitPara para = {.lock=NULL,.unlock=NULL,.malloc=NULL,.free=NULL};
/* Check the coroutine scheduler is valid */
if (CoSchedulerId >= COROUTINE_SCHEDULER_NUMBER)
{
return COROUTINE_E_INVALID_PARAMETER;
}
/* Check the coroutine scheduler tick function is valid */
if (tick == NULL)
{
return COROUTINE_E_INVALID_TICK;
}
/* Check the coroutine scheduler tick interval is valid */
if (tickInterval == 0)
{
return COROUTINE_E_INVALID_TICK_INTERVAL;
}
/* Check the coroutine scheduler init parameter is valid */
if (pPara == NULL)
{
pPara = &para;
}
#if (COROUTINE_SCHEDULER_NUMBER > 1)
/* Check the coroutine scheduler lock function is valid */
if (sCoSchedulerLock && sCoSchedulerUnlock)
{
/* Ensure that different schedulers use the same lock */
if (pPara->lock != sCoSchedulerLock || pPara->unlock != sCoSchedulerUnlock)
{
return COROUTINE_E_INVALID_LOCK;
}
}
else
{
/* Specifies the passed lock */
if (pPara->lock && pPara->unlock)
{
sCoSchedulerLock = pPara->lock;
sCoSchedulerUnlock = pPara->unlock;
}
/* Use the default lock */
else
{
sCoSchedulerLock = CoScheduler_Lock;
sCoSchedulerUnlock = CoScheduler_Unlock;
}
}
#endif
/* Check the coroutine scheduler malloc function is valid */
if (sCoSchedulerMalloc && sCoSchedulerFree)
{
/* Ensure that different schedulers use the same malloc */
if (pPara->malloc != sCoSchedulerMalloc || pPara->free != sCoSchedulerFree)
{
return COROUTINE_E_INVALID_MHOOK;
}
}
else
{
sCoSchedulerLock();
/* Specifies the passed malloc */
if (pPara->malloc && pPara->free)
{
sCoSchedulerMalloc = pPara->malloc;
sCoSchedulerFree = pPara->free;
}
/* Not use */
else
{
sCoSchedulerMalloc = NULL;
sCoSchedulerFree = NULL;
}
sCoSchedulerUnlock();
}
/* Get the coroutine scheduler */
CoScheduler *pScheduler = &sCoScheduler[CoSchedulerId];
/* Initialize the coroutine scheduler */
pScheduler->tick = tick;
pScheduler->tickInterval = tickInterval;
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
/* Initialize the coroutine scheduler loading calculate */
pScheduler->uTick = COROUTINE_LOADING_CALCULATE_PERIOD * 1000000 / pScheduler->tickInterval;
pScheduler->pTick = 0;
pScheduler->sTick = 0;
pScheduler->rTick = 0;
pScheduler->curLoad = 0;
pScheduler->maxLoad = 0;
#endif
pScheduler->CoTaskList = NULL;
pScheduler->CoTaskCurrent = NULL;
pScheduler->CoTaskListSize = 0;
pScheduler->CoTimerList = NULL;
pScheduler->CoTimerCurrent = NULL;
pScheduler->CoTimerListSize = 0;
pScheduler->stackTop = NULL;
pScheduler->CoTaskRun = CoTaskRun;
return COROUTINE_E_OK;
}
int CoScheduler_Start(uint32_t CoSchedulerId)
{
int state = COSCHEDULER_STATE_INTI;
CoScheduler *pScheduler = NULL;
if (CoSchedulerId >= COROUTINE_SCHEDULER_NUMBER)
{
return COROUTINE_E_INVALID_PARAMETER;
}
pScheduler = &sCoScheduler[CoSchedulerId];
state = COSCHEDULER_GET_STATE(pScheduler);
/* Initialize the coroutine scheduler stack pointer */
pScheduler->stackTop = &state;
if (COSCHEDULER_STATE_INTI == state)
{
if (pScheduler->tick != NULL)
{
/* Jump directly to the scheduling state */
COSCHEDULER_SET_STATE(pScheduler, COSCHEDULER_STATE_SCHEDULE);
}
else
{
/* Jump directly to the exit state */
COSCHEDULER_SET_STATE(pScheduler, COSCHEDULER_STATE_EXIT);
}
}
else if (COSCHEDULER_STATE_START == state)
{
/* Calculate the coroutine scheduler stack pointer */
/* Align with 16 bytes */
void *stackTop = (void *)((char *)pScheduler->CoTaskCurrent->stackBase + ((pScheduler->CoTaskCurrent->stackSize + 15) & ~15) - 16);
/* Set the coroutine scheduler stack pointer */
COROUTINE_SET_SP(stackTop);
/* Run the coroutine scheduler */
pScheduler->CoTaskRun(pScheduler);
}
else if (COSCHEDULER_STATE_EXIT == state)
{
/* Delete all coroutines in the list */
while (pScheduler->CoTaskList)
{
CoTask_t Task = pScheduler->CoTaskList;
/* Remove the coroutine from the list */
EraseCoTaskList(pScheduler, Task);
/* Delete the coroutine */
DeleteCoTask(pScheduler, Task);
}
/* Delete all coroutine timers in the list */
while (pScheduler->CoTimerList)
{
CoTimer_t Timer = pScheduler->CoTimerList;
/* Erase the coroutine timer from the list */
EraseCoTimerList(pScheduler, Timer);
/* Free the coroutine timer */
DeleteCoTimer(pScheduler, Timer);
}
/* De-initialize the coroutine scheduler */
pScheduler->tick = NULL;
pScheduler->tickInterval = 0;
pScheduler->CoTaskList = NULL;
pScheduler->CoTaskCurrent = NULL;
pScheduler->CoTaskListSize = 0;
pScheduler->CoTimerList = NULL;
pScheduler->CoTimerCurrent = NULL;
pScheduler->CoTimerListSize = 0;
pScheduler->stackTop = NULL;
pScheduler->CoTaskRun = NULL;
}
else if (COSCHEDULER_STATE_SCHEDULE == state)
{
/* Infinite loop to run the coroutine scheduler */
while (1)
{
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
/* Calculate the coroutine scheduler tick */
if (pScheduler->sTick == 0)
{
pScheduler->sTick = pScheduler->tick();
}
else
{
pScheduler->cTick = pScheduler->tick();
/* Calculate the time difference between task cut-in and cut-out */
uint64_t diffTick = pScheduler->cTick - pScheduler->sTick;
/* Check the time difference is greater than or equal to the tick interval */
if (diffTick >= pScheduler->uTick)
{
/* Calculate the loading percentage */
uint16_t tload = pScheduler->rTick * 10000 / diffTick;
/* Add the loading percentage to the loading queue */
pScheduler->loadQueueBase[pScheduler->loadQueueTail] = tload;
pScheduler->loadQueueTail = (pScheduler->loadQueueTail + 1) % COROUTINE_LOADING_CALCULATE_QSIZE;
/* Update the loading queue size */
/* If the loading queue size is less than the queue size, increment the size */
if (pScheduler->loadQueueSize < COROUTINE_LOADING_CALCULATE_QSIZE)
{
pScheduler->loadQueueSize++;
}
/* If the loading queue size is equal to the queue size, calculate the average loading percentage */
else
{
uint32_t totalLoad = 0;
/* Calculate the total loading percentage */
for (int i = 0; i < COROUTINE_LOADING_CALCULATE_QSIZE; i++)
{
totalLoad += pScheduler->loadQueueBase[i];
}
/* Calculate the average loading percentage */
pScheduler->curLoad = (uint16_t)(totalLoad / COROUTINE_LOADING_CALCULATE_QSIZE);
/* Update the maximum loading percentage */
if (pScheduler->curLoad > pScheduler->maxLoad)
{
pScheduler->maxLoad = pScheduler->curLoad;
}
}
/* Update the scheduler tick */
/* Set the scheduler tick to the current tick */
pScheduler->sTick = pScheduler->cTick;
/* Reset the running tick */
pScheduler->rTick = 0;
}
}
#endif
/* Check the coroutine scheduler has coroutine */
if (pScheduler->CoTaskList != NULL)
{
/* Check the current coroutine is NULL */
if (pScheduler->CoTaskCurrent == NULL)
{
/* Set the current coroutine to the first coroutine in the list */
pScheduler->CoTaskCurrent = pScheduler->CoTaskList;
}
/* Get the next coroutine */
CoTask_t tCoroutine = pScheduler->CoTaskCurrent->next;
/* Loop through the coroutine list */
do {
/* Check the coroutine is initialized */
if (tCoroutine->state == COTASK_STATE_INIT)
{
/* Set the current coroutine to the initialized coroutine */
pScheduler->CoTaskCurrent = tCoroutine;
/* Jump to the coroutine */
COSCHEDULER_SET_STATE(pScheduler, COSCHEDULER_STATE_START);
}
/* Check the coroutine is ready */
else if (tCoroutine->state == COTASK_STATE_READY)
{
/* Set the current coroutine to the ready coroutine */
pScheduler->CoTaskCurrent = tCoroutine;
/* Resume the coroutine */
Resume(pScheduler);
}
/* Check the coroutine is running */
else if (tCoroutine->state == COTASK_STATE_RUNNING)
{
tCoroutine->state = COTASK_STATE_INIT;
continue;
}
/* Check the coroutine is suspend */
else if (tCoroutine->state == COTASK_STATE_SUSPEND)
{
/* Check the next run time is reached */
if (((tCoroutine->nextRunTick != 0 && pScheduler->tick() > tCoroutine->nextRunTick) ||
/* Check the event is set */
(tCoroutine->pEvent != NULL && tCoroutine->pEvent->flag != 0)))
{
/* Set the coroutine to ready */
tCoroutine->state = COTASK_STATE_READY;
continue;
}
}
/* Check the coroutine is deleted */
else if (tCoroutine->state == COTASK_STATE_DELETED)
{
/* Remove the coroutine from the list */
EraseCoTaskList(pScheduler, tCoroutine);
/* Delete the coroutine */
DeleteCoTask(pScheduler, tCoroutine);
}
/* Check the coroutine is unknown */
else
{
/* Set the coroutine to initialized */
tCoroutine->state = COTASK_STATE_INIT;
continue;
}
/* Check the next coroutine */
tCoroutine = tCoroutine->next;
} while (tCoroutine != pScheduler->CoTaskCurrent->next);
}
/* Check the timer scheduler has timer */
if (pScheduler->CoTimerList != NULL)
{
/* Get the first timer */
CoTimer_t tTimer = pScheduler->CoTimerList;
/* Loop through the timer list */
do {
/* Check the timer is expired */
if (pScheduler->tick() >= tTimer->nextRunTick)
{
/* Set the current timer to the expired timer */
pScheduler->CoTimerCurrent = tTimer;
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
/* Record the timer cut-in time */
uint64_t pTick = pScheduler->tick();
#endif
tTimer->entry();
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
/* Record the timer cut-out time */
uint64_t cTick = pScheduler->tick();
#endif
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
/* Record the timer loading time */
pScheduler->rTick += cTick - pTick;
#endif
/* Set the timer to next run time */
pScheduler->CoTimerCurrent = NULL;
tTimer->nextRunTick = pScheduler->tick() + tTimer->periodTick;
}
/* Check the next timer */
tTimer = tTimer->next;
} while (tTimer != pScheduler->CoTimerList);
}
}
}
else
{
COSCHEDULER_SET_STATE(pScheduler, COSCHEDULER_STATE_EXIT);
}
return 0;
}
int CoScheduler_Exit(uint32_t CoSchedulerId)
{
CoScheduler *pScheduler = NULL;
/* Check the coroutine scheduler is valid */
if (CoSchedulerId >= COROUTINE_SCHEDULER_NUMBER)
{
return COROUTINE_E_INVALID_PARAMETER;
}
/* Get the coroutine scheduler */
pScheduler = &sCoScheduler[CoSchedulerId];
if (pScheduler->tick == NULL)
{
return COROUTINE_E_INVALID_TICK;
}
/* Yield to the next coroutine task */
COSCHEDULER_SET_STATE(pScheduler, COSCHEDULER_STATE_EXIT);
return COROUTINE_E_OK;
}
int CoScheduler_TaskCount(uint32_t CoSchedulerId)
{
CoScheduler *pScheduler = NULL;
/* Check the coroutine scheduler is valid */
if (CoSchedulerId >= COROUTINE_SCHEDULER_NUMBER)
{
return COROUTINE_E_INVALID_PARAMETER;
}
/* Get the coroutine scheduler */
pScheduler = &sCoScheduler[CoSchedulerId];
if (pScheduler->tick == NULL)
{
return COROUTINE_E_INVALID_TICK;
}
return (int)pScheduler->CoTaskListSize;
}
int CoScheduler_TimerCount(uint32_t CoSchedulerId)
{
CoScheduler *pScheduler = NULL;
/* Check the coroutine scheduler is valid */
if (CoSchedulerId >= COROUTINE_SCHEDULER_NUMBER)
{
return COROUTINE_E_INVALID_PARAMETER;
}
/* Get the coroutine scheduler */
pScheduler = &sCoScheduler[CoSchedulerId];
if (pScheduler->tick == NULL)
{
return COROUTINE_E_INVALID_TICK;
}
return (int)pScheduler->CoTimerListSize;
}