mirror of
https://gitee.com/Lamdonn/varch.git
synced 2025-12-06 08:46:42 +08:00
Add the initial version coroutine module
TODO: 1. Add feature to bind CoTask and CoTimer to the specified CoScheduler 2. Compatibility with coroutine API highly intensive IO, sockets, etc 3. Improve functional safety
This commit is contained in:
parent
70d1741bab
commit
49025692ca
@ -77,6 +77,7 @@ It has the characteristics of **simplicity, universality, and efficiency**, with
|
||||
| intl | 01.01.00 | [link](/doc/intl.en.md) | [path](./source/07_math) | Large integer arithmetic module
|
||||
| floatl | 01.01.01 | [link](/doc/floatl.en.md) | [path](./source/07_math) | Large floating-point arithmetic module
|
||||
| flmath | 01.00.00 | [link](/doc/flmath.en.md) | [path](./source/07_math) | Large floating-point arithmetic math module
|
||||
| coroutine | 00.01.00 | [link](/doc/coroutine.en.md) | [path](./source/08_coroutine) | Coroutine module
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@ -77,6 +77,7 @@ varch(we-architecture,意为我们的框架库)是嵌入式C语言常用
|
||||
| intl | 01.01.00 | [link](/doc/intl.md) | [path](./source/07_math) | 大型整数运算模块
|
||||
| floatl | 01.01.01 | [link](/doc/floatl.md) | [path](./source/07_math) | 大型浮点数运算模块
|
||||
| flmath | 01.00.00 | [link](/doc/flmath.md) | [path](./source/07_math) | 大型浮点数数学运算模块
|
||||
| coroutine | 00.01.00 | [link](/doc/coroutine.md) | [path](./source/08_coroutine) | 协程模块
|
||||
|
||||
## 使用说明
|
||||
|
||||
|
||||
0
doc/coroutine.en.md
Normal file
0
doc/coroutine.en.md
Normal file
0
doc/coroutine.md
Normal file
0
doc/coroutine.md
Normal file
3
makefile
3
makefile
@ -21,6 +21,7 @@ ALGORITHM_PATH = $(WORKSPACE)/04_algorithm
|
||||
PARSER_PATH = $(WORKSPACE)/05_parser
|
||||
PERFORMANCE_PATH = $(WORKSPACE)/06_performance
|
||||
MATH_PATH = $(WORKSPACE)/07_math
|
||||
COROUTINE_PATH = $(WORKSPACE)/08_coroutine
|
||||
|
||||
##################################################################################
|
||||
### sources, libaries and head path
|
||||
@ -33,6 +34,7 @@ INCLUDE += $(ALGORITHM_PATH)
|
||||
INCLUDE += $(PARSER_PATH)
|
||||
INCLUDE += $(PERFORMANCE_PATH)
|
||||
INCLUDE += $(MATH_PATH)
|
||||
INCLUDE += $(COROUTINE_PATH)
|
||||
|
||||
LIBSRCS += $(APPLICATION_PATH)/init.c
|
||||
LIBSRCS += $(wildcard $(APPLICATION_PATH)/console/*.c)
|
||||
@ -43,6 +45,7 @@ LIBSRCS += $(wildcard $(ALGORITHM_PATH)/*.c)
|
||||
LIBSRCS += $(wildcard $(PARSER_PATH)/*.c)
|
||||
LIBSRCS += $(wildcard $(PERFORMANCE_PATH)/*.c)
|
||||
LIBSRCS += $(wildcard $(MATH_PATH)/*.c)
|
||||
LIBSRCS += $(wildcard $(COROUTINE_PATH)/*.c)
|
||||
|
||||
LIBLIST += m
|
||||
LIBLIST += pthread
|
||||
|
||||
1234
source/08_coroutine/coroutine.c
Normal file
1234
source/08_coroutine/coroutine.c
Normal file
File diff suppressed because it is too large
Load Diff
588
source/08_coroutine/coroutine.h
Normal file
588
source/08_coroutine/coroutine.h
Normal file
@ -0,0 +1,588 @@
|
||||
/*********************************************************************************************************
|
||||
* ------------------------------------------------------------------------------------------------------
|
||||
* file description
|
||||
* ------------------------------------------------------------------------------------------------------
|
||||
* \file coroutine.h
|
||||
* \unit coroutine
|
||||
* \brief This is a C language coroutine library
|
||||
* \author Lamdonn
|
||||
* \version v0.1.0
|
||||
* \license GPL-2.0
|
||||
* \copyright Copyright (C) 2025 Lamdonn.
|
||||
********************************************************************************************************/
|
||||
#ifndef __coroutine_H
|
||||
#define __coroutine_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Version infomation */
|
||||
#define COROUTINE_V_MAJOR 0
|
||||
#define COROUTINE_V_MINOR 1
|
||||
#define COROUTINE_V_PATCH 0
|
||||
|
||||
#include "coroutine_cfg.h"
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
/**
|
||||
* \brief: Coroutine scheduler
|
||||
*/
|
||||
typedef struct CoScheduler CoScheduler;
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task
|
||||
*/
|
||||
typedef struct CoTask* CoTask_t;
|
||||
|
||||
/**
|
||||
* \brief: Coroutine timer
|
||||
*/
|
||||
typedef struct CoTimer* CoTimer_t;
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task entry function
|
||||
* \param arg: Task argument address
|
||||
* \return: Task return value address
|
||||
*/
|
||||
typedef void *(*CoTaskEntry_t)(void *arg);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine timer entry function
|
||||
*/
|
||||
typedef void (*CoTimerEntry_t)(void);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task run function
|
||||
* \param pScheduler: Coroutine scheduler
|
||||
* \param mark: Coroutine mark
|
||||
* \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, uint32_t mark);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine lock function
|
||||
* \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, lock
|
||||
*/
|
||||
typedef void (*CoLock_t)(void);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine unlock function
|
||||
* \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, unlock
|
||||
*/
|
||||
typedef void (*CoUnlock_t)(void);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine malloc function
|
||||
* \param size: Allocate memory size
|
||||
* \return: Allocate memory address
|
||||
* \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, malloc
|
||||
*/
|
||||
typedef void *(*CoMalloc_t)(size_t size);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine free function
|
||||
* \param block: Free memory address
|
||||
* \note: Use coroutines for multi-thread, protect the security of multi-thread shared resources, free
|
||||
*/
|
||||
typedef void (*CoFree_t)(void *block);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine tick function
|
||||
* \return: Current tick value
|
||||
* \note: The time base on which the coroutine schedule depends, such as system tick.
|
||||
*/
|
||||
typedef uint64_t (*CoTick_t)(void);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine event
|
||||
* \note: Used for synchronization between coroutines
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint32_t flag; /**< Event flag, 32 bit storage, each bit can individually represent an event */
|
||||
} CoEvent;
|
||||
|
||||
/**
|
||||
* \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
|
||||
{
|
||||
CoScheduler *next; /**< Single-link list, next coroutine scheduler */
|
||||
|
||||
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 */
|
||||
|
||||
#if (COROUTINE_STATIC_TASK_MAX_NUMBER > 0)
|
||||
struct CoTask sCoTasks[COROUTINE_STATIC_TASK_MAX_NUMBER]; /**< Coroutine task static array */
|
||||
#endif
|
||||
#if (COROUTINE_STATIC_TIMER_MAX_NUMBER > 0)
|
||||
struct CoTimer sCoTimers[COROUTINE_STATIC_TIMER_MAX_NUMBER]; /**< Coroutine timer static array */
|
||||
#endif
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief: Coroutine scheduler initialize parameter
|
||||
* \note: Coroutine scheduler initialize parameter is used to initialize the coroutine scheduler.
|
||||
*/
|
||||
typedef struct CoSchedulerInitPara
|
||||
{
|
||||
CoTick_t tick; /**< Coroutine scheduler tick function */
|
||||
uint32_t tickInterval; /**< Coroutine scheduler tick interval, indicates how many ns each tick is */
|
||||
CoLock_t lock; /**< Coroutine scheduler lock function */
|
||||
CoUnlock_t unlock; /**< Coroutine scheduler unlock function */
|
||||
CoMalloc_t malloc; /**< Coroutine scheduler malloc function */
|
||||
CoFree_t free; /**< Coroutine scheduler free function */
|
||||
} CoSchedulerInitPara;
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task create parameter
|
||||
* \note: Coroutine task create parameter is used to create a coroutine task.
|
||||
*/
|
||||
typedef struct CoTaskCreatePara
|
||||
{
|
||||
CoTaskEntry_t entry; /**< Coroutine task entry function */
|
||||
void *pStack; /**< Coroutine task stack address */
|
||||
size_t stackSize; /**< Coroutine task stack size */
|
||||
void *arg; /**< Coroutine task argument */
|
||||
} CoTaskCreatePara;
|
||||
|
||||
typedef struct CoTaskWaitPara
|
||||
{
|
||||
CoEvent *pEvent;
|
||||
uint64_t ms;
|
||||
uint64_t tick;
|
||||
} CoTaskWaitPara;
|
||||
|
||||
/**
|
||||
* \brief: Coroutine scheduler default initialize parameter
|
||||
* \note: Coroutine scheduler default initialize parameter is used to initialize the coroutine scheduler.
|
||||
*/
|
||||
#define COSCHEDULER_INIT_PARA(...) (CoSchedulerInitPara[1]){[0]={.tick=NULL,.tickInterval=0,.lock=NULL,.unlock=NULL,.malloc=NULL,.free=NULL},[0]={__VA_ARGS__}}
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task default create parameter
|
||||
* \note: Coroutine task default create parameter is used to create a coroutine task.
|
||||
*/
|
||||
#define COTASK_CREATE_PARA(...) (CoTaskCreatePara[1]){[0]={.entry=NULL,.pStack=NULL,.stackSize=0,.arg=NULL},[0]={__VA_ARGS__}}
|
||||
|
||||
/**
|
||||
* \brief: Coroutine wait default wait parameter
|
||||
* \note: Coroutine wait default wait parameter is used to blocking task waiting for event or timeout.
|
||||
*/
|
||||
#define COTASK_WAIT_PARA(...) (CoTaskWaitPara[1]){[0]={.pEvent=NULL,.ms=0,.tick=0},[0]={__VA_ARGS__}}
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task create parameter default value
|
||||
* \note: Coroutine task create parameter default value is used to create a coroutine task.
|
||||
*/
|
||||
#define COROUTINE_E_OK (0) /**< Coroutine task create parameter default value, indicates success */
|
||||
#define COROUTINE_E_INVALID_PARAMETER (-1) /**< Coroutine task create parameter default value, indicates invalid parameter */
|
||||
#define COROUTINE_E_INVALID_TICK (-2) /**< Coroutine task create parameter default value, indicates invalid tick function */
|
||||
#define COROUTINE_E_INVALID_TICK_INTERVAL (-3) /**< Coroutine task create parameter default value, indicates invalid tick interval */
|
||||
#define COROUTINE_E_INVALID_LOCK (-4) /**< Coroutine task create parameter default value, indicates invalid lock function */
|
||||
#define COROUTINE_E_INVALID_MHOOK (-5) /**< Coroutine task create parameter default value, indicates invalid memory hook function */
|
||||
#define COROUTINE_E_TCB_MEM_STACK_FAIL (-6) /**< Coroutine task create parameter default value, indicates memory stack fail */
|
||||
|
||||
/**
|
||||
* \brief: Coroutine event default value
|
||||
* \note: Coroutine event default value is used to initialize the coroutine event.
|
||||
*/
|
||||
#define COEVENT_STATIC_VALUE ((CoEvent){.flag=0})
|
||||
|
||||
/**
|
||||
* \brief: Coroutine scheduler initialize function
|
||||
* \param pScheduler: [Non-default parameter] Coroutine scheduler pointer
|
||||
* \param tick: [Default parameter] Coroutine scheduler tick function, must be specified
|
||||
* \param tickInterval: [Default parameter] Coroutine scheduler tick interval, indicates how many us each tick is, must be specified
|
||||
* \param lock: [Default parameter] Coroutine scheduler lock function,
|
||||
* for use on multiple threads, you must specify as follows .lock=thread_lock
|
||||
* \param unlock: [Default parameter] Coroutine scheduler unlock function,
|
||||
* for use on multiple threads, you must specify as follows .unlock=thread_unlock
|
||||
* \param malloc: [Default parameter] Coroutine scheduler malloc function,
|
||||
* if you need to allocate memory dynamically (CoTask, CoTimer, stack, etc),
|
||||
* you must specify as follows .malloc=mallloc
|
||||
* \param free: [Default parameter] Coroutine scheduler free function,
|
||||
* if you need to free memory dynamically (CoTask, CoTimer, stack, etc),
|
||||
* you must specify as follows .free=free
|
||||
* \return: Coroutine task create parameter default value, indicates success
|
||||
* \note: Coroutine scheduler initialize function is used to initialize the coroutine scheduler.
|
||||
* It must be called once before any other coroutine scheduler function.
|
||||
* Only one initialization is allowed within a single thread
|
||||
* It will initialize the coroutine scheduler with the given parameter.
|
||||
* The coroutine scheduler will use the given tick function to measure the load of each task.
|
||||
* The tick function must be called periodically with the given tick interval.
|
||||
* The coroutine scheduler will use the given lock function to protect the shared resources.
|
||||
* The coroutine scheduler will use the given malloc function to allocate memory for the tasks.
|
||||
* The coroutine scheduler will use the given free function to free memory of the tasks.
|
||||
* \warning: The coroutine scheduler will not check the validity of the given parameter.
|
||||
* You must ensure that the given parameter is valid.
|
||||
* \example:
|
||||
* CoScheduler scheduler;
|
||||
* CoScheduler_Init(&scheduler, GetTimerMs, 1000000);
|
||||
* \example:
|
||||
* CoScheduler scheduler;
|
||||
* CoScheduler_Init(&scheduler, GetTimerUsec, 1000);
|
||||
* \example:
|
||||
* CoScheduler scheduler;
|
||||
* CoScheduler_Init(&scheduler, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock);
|
||||
* \example:
|
||||
* CoScheduler scheduler;
|
||||
* CoScheduler_Init(&scheduler, GetTimerUsec, 1000, .malloc=mallloc, .free=free);
|
||||
*/
|
||||
#define CoScheduler_Init(pScheduler, ...) CoScheduler_InitP(pScheduler, COSCHEDULER_INIT_PARA(__VA_ARGS__))
|
||||
|
||||
/**
|
||||
* \brief: Coroutine scheduler initialize function
|
||||
* \param pScheduler: Coroutine scheduler pointer
|
||||
* \param pPara: Coroutine scheduler initialize parameter pointer
|
||||
* \return: Coroutine task create parameter default value, indicates success
|
||||
* \note: Coroutine scheduler initialize function is used to initialize the coroutine scheduler.
|
||||
* It must be called once before any other coroutine scheduler function.
|
||||
* Only one initialization is allowed within a single thread
|
||||
* It will initialize the coroutine scheduler with the given parameter.
|
||||
* The coroutine scheduler will use the given tick function to measure the load of each task.
|
||||
* The tick function must be called periodically with the given tick interval.
|
||||
* The coroutine scheduler will use the given lock function to protect the shared resources.
|
||||
* The coroutine scheduler will use the given malloc function to allocate memory for the tasks.
|
||||
* The coroutine scheduler will use the given free function to free memory of the tasks.
|
||||
*/
|
||||
int CoScheduler_InitP(CoScheduler *pScheduler, CoSchedulerInitPara *pPara);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine scheduler start function
|
||||
* \param pScheduler: Coroutine scheduler pointer
|
||||
* \return: Coroutine task create parameter default value, indicates success
|
||||
* \note: Coroutine scheduler start function is used to start the coroutine scheduler.
|
||||
* It must be called once after the coroutine scheduler is initialized.
|
||||
* It will start the coroutine scheduler and run the tasks.
|
||||
* \example:
|
||||
* CoScheduler scheduler;
|
||||
* CoScheduler_Init(&scheduler, GetTimerUsec, 1000);
|
||||
* testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest), 0);
|
||||
* CoScheduler_Start(&scheduler);
|
||||
*/
|
||||
int CoScheduler_Start(CoScheduler *pScheduler);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine scheduler current load function
|
||||
* \param pScheduler: Coroutine scheduler pointer
|
||||
* \return: Coroutine scheduler current load, indicates the load of the coroutine scheduler
|
||||
* \note: Coroutine scheduler current load function is used to get the current load of the coroutine scheduler.
|
||||
* The load is the number of ticks that the coroutine scheduler has run.
|
||||
* The load is updated periodically with the given tick interval.
|
||||
*/
|
||||
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
|
||||
uint16_t CoScheduler_CurLoad(CoScheduler *pScheduler);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief: Coroutine scheduler maximum load function
|
||||
* \param pScheduler: Coroutine scheduler pointer
|
||||
* \return: Coroutine scheduler maximum load, indicates the maximum load of the coroutine scheduler
|
||||
* \note: Coroutine scheduler maximum load function is used to get the maximum load of the coroutine scheduler.
|
||||
* The load is the number of ticks that the coroutine scheduler has run.
|
||||
* The load is updated periodically with the given tick interval.
|
||||
*/
|
||||
#if (COROUTINE_ENABLE_LOADING_CALCULATE > 0)
|
||||
uint16_t CoScheduler_MaxLoad(CoScheduler *pScheduler);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task create function
|
||||
* \param entry: [Default parameter] Coroutine task entry function, indicates the entry function of the coroutine task, must be specified
|
||||
* \param stack: [Default parameter] Coroutine task stack, indicates the stack of the coroutine task.
|
||||
* If not specified, it is allocated via the scheduler's malloc function.
|
||||
* \param stackSize: [Default parameter] Coroutine task stack size, indicates the size of the coroutine task stack
|
||||
* If not specified, it is the default stack size `COROUTINE_STACK_DEFAULT_SIZE`.
|
||||
* \param arg: [Default parameter] Coroutine task argument, indicates the argument of the coroutine task
|
||||
* \return: Coroutine task handle, indicates the created coroutine task
|
||||
* \note: Coroutine task create function is used to create a coroutine task.
|
||||
* It will create a coroutine task with the given parameter.
|
||||
* The coroutine task will run in the coroutine scheduler.
|
||||
* \example:
|
||||
* CoTask_t testCoroutine = CoTask_Create(test);
|
||||
* \example:
|
||||
* CoTask_t testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
|
||||
* \example:
|
||||
* CoTask_t testCoroutine = CoTask_Create(test, .stackSize=4096);
|
||||
* \example:
|
||||
* int arg = 4096;
|
||||
* CoTask_t testCoroutine = CoTask_Create(test, .arg=&arg);
|
||||
*/
|
||||
#define CoTask_Create(...) CoTask_CreateP(COTASK_CREATE_PARA(__VA_ARGS__))
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task create function, it is recommended to use @ref `CoTask_Create` instead
|
||||
* \param pPara: Coroutine task create parameter pointer
|
||||
* \return: Coroutine task handle, indicates the created coroutine task
|
||||
* \note: Coroutine task create function is used to create a coroutine task.
|
||||
* It will create a coroutine task with the given parameter.
|
||||
* The coroutine task will run in the coroutine scheduler.
|
||||
*/
|
||||
CoTask_t CoTask_CreateP(CoTaskCreatePara *pPara);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task delete function
|
||||
* \param CoTask: Coroutine task handle, indicates the coroutine task to be deleted
|
||||
* \return: Coroutine task delete parameter default value, indicates success
|
||||
* \note: Coroutine task delete function is used to delete a coroutine task.
|
||||
* It will delete the given coroutine task.
|
||||
* The coroutine task must be in the deleted state.
|
||||
*/
|
||||
int CoTask_Delete(CoTask_t CoTask);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task self function
|
||||
* \return: Coroutine task handle, indicates the current coroutine task
|
||||
* \note: Coroutine task self function is used to get the current coroutine task.
|
||||
* It will return the handle of the current coroutine task.
|
||||
*/
|
||||
CoTask_t CoTask_Self(void);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task stack max used function
|
||||
* \param CoTask: Coroutine task handle, indicates the coroutine task to get the stack max used
|
||||
* \return: Coroutine task stack max used, indicates the maximum stack used of the coroutine task
|
||||
* \note: Coroutine task stack max used function is used to get the maximum stack used of the coroutine task.
|
||||
* The stack used is the maximum stack used of the coroutine task.
|
||||
* The stack used is updated periodically with the given tick interval.
|
||||
*/
|
||||
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
|
||||
size_t CoTask_StackMaxUsed(CoTask_t CoTask);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task stack current used function
|
||||
* \param CoTask: Coroutine task handle, indicates the coroutine task to get the stack current used
|
||||
* \return: Coroutine task stack current used, indicates the current stack used of the coroutine task
|
||||
* \note: Coroutine task stack current used function is used to get the current stack used of the coroutine task.
|
||||
* The stack used is the current stack used of the coroutine task.
|
||||
* The stack used is updated periodically with the given tick interval.
|
||||
*/
|
||||
#if (COROUTINE_ENABLE_STACK_CALCULATE > 0)
|
||||
size_t CoTask_StackCurUsed(CoTask_t CoTask);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task wait function, blocking task waiting for event or timeout.
|
||||
* \param pEvent: [Default parameter] Coroutine event pointer, indicates the event to wait
|
||||
* \param ms: [Default parameter] Coroutine task wait ms, indicates the ms to wait
|
||||
* \param tick: [Default parameter] Coroutine task wait tick, indicates the tick to wait
|
||||
* \return: Coroutine event value, indicates the event value that triggered the coroutine task
|
||||
* \note: Coroutine task wait event function is used to wait for the given event.
|
||||
* @ref `CoEvent_Init` must first be called to initialize the event.
|
||||
* @ref `CoEvent_Notify` notifies the occurrence of the event
|
||||
* It will block the current coroutine task.
|
||||
* The event value is a bit mask, each bit represents an event.
|
||||
* If multiple events are triggered, the corresponding bits will be set.
|
||||
* \note: Coroutine task wait ms function is used to wait for the given ms.
|
||||
* It will block the current coroutine task.
|
||||
* \note: Coroutine task wait tick function is used to wait for the given tick.
|
||||
* It will block the current coroutine task.
|
||||
* \example:
|
||||
* void *test(void *arg)
|
||||
* {
|
||||
* while (1)
|
||||
* {
|
||||
* // Give up access to the scheduler without waiting, similar to yeild
|
||||
* CoTask_Wait();
|
||||
*
|
||||
* // Wait for 1000 ms
|
||||
* CoTask_Wait(.ms=1000);
|
||||
*
|
||||
* // Wait for 1000000 tick
|
||||
* CoTask_Wait(.tick=1000000);
|
||||
*
|
||||
* // Wait for 1000 ms or event, if no any events occur within 1000 ms, and will timeout blocking
|
||||
* CoTask_Wait(.pEvent=&g_Event, .ms=1000);
|
||||
*
|
||||
* // Always wait for the event, and judge every events
|
||||
* uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
|
||||
* if (evs & 0x01)
|
||||
* {
|
||||
* printf("event 0x01 triggered\n");
|
||||
* }
|
||||
* if (evs & 0x02)
|
||||
* {
|
||||
* printf("event 0x02 triggered\n");
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
#define CoTask_Wait(...) CoTask_WaitP(COTASK_WAIT_PARA(__VA_ARGS__))
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task wait function, it is recommended to use @ref `CoTask_Wait` instead
|
||||
* \param pPara: Coroutine task wait parameter pointer
|
||||
* \return: Coroutine event value, indicates the event value that triggered the coroutine task
|
||||
* \note: Coroutine task wait function is used to wait for the given event or timeout.
|
||||
* It will block the current coroutine task.
|
||||
* The event value is a bit mask, each bit represents an event.
|
||||
* If multiple events are triggered, the corresponding bits will be set.
|
||||
*/
|
||||
uint32_t CoTask_WaitP(CoTaskWaitPara *pPara);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine task wait functions, the version of the macro definition used is often used
|
||||
* \param m: Coroutine task wait ms, indicates the ms to wait
|
||||
* \param t: Coroutine task wait tick, indicates the tick to wait
|
||||
* \param e: Coroutine event pointer, indicates the event to wait
|
||||
* \return: Coroutine event value, indicates the event value that triggered the coroutine task
|
||||
*/
|
||||
#define CoTask_WaitMs(m) CoTask_Wait(.ms=m)
|
||||
#define CoTask_WaitTick(t) CoTask_Wait(.tick=t)
|
||||
#define CoTask_WaitEvent(e) CoTask_Wait(.pEvent=e)
|
||||
#define CoTask_WaitEventMs(e, m) CoTask_Wait(.pEvent=e, .ms=m)
|
||||
#define CoTask_WaitEventTick(e, t) CoTask_Wait(.pEvent=e, .tick=t)
|
||||
|
||||
/**
|
||||
* \brief: Coroutine event initialize function
|
||||
* \param pEvent: Coroutine event pointer, indicates the event to initialize
|
||||
* \note: Coroutine event initialize function is used to initialize the given event.
|
||||
* It must be called before @ref `CoTask_WaitEvent`.
|
||||
* \example:
|
||||
* CoEvent_Init(&g_Event);
|
||||
*/
|
||||
void CoEvent_Init(CoEvent *pEvent);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine event notify function
|
||||
* \param pEvent: Coroutine event pointer, indicates the event to notify
|
||||
* \param evs: Coroutine event value, indicates the event value to notify
|
||||
* \note: Coroutine event notify function is used to notify the occurrence of the event.
|
||||
* It will set the corresponding bits in the event value.
|
||||
* \example:
|
||||
* CoEvent_Notify(&g_Event, 0x01); // notify event 0x01
|
||||
* \example:
|
||||
* CoEvent_Notify(&g_Event, 0x01 | 0x02); // notify event 0x01 and 0x02
|
||||
*/
|
||||
void CoEvent_Notify(CoEvent *pEvent, uint32_t evs);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine timer create tick function
|
||||
* \param entry: Coroutine timer entry, indicates the timer entry function
|
||||
* \param tick: Coroutine timer tick, indicates the tick interval
|
||||
* \return: Coroutine timer handle, indicates the created timer
|
||||
* \note: Coroutine timer create tick function is used to create a timer with the given tick interval.
|
||||
* The timer will call the given entry function periodically with the given tick interval.
|
||||
* \example:
|
||||
* CoTimer_t timer = CoTimer_CreateTick(timer_entry, 100); // create a timer with 100 tick interval
|
||||
*/
|
||||
CoTimer_t CoTimer_CreateTick(CoTimerEntry_t entry, uint64_t tick);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine timer create ms function
|
||||
* \param entry: Coroutine timer entry, indicates the timer entry function
|
||||
* \param ms: Coroutine timer ms, indicates the ms interval
|
||||
* \return: Coroutine timer handle, indicates the created timer
|
||||
* \note: Coroutine timer create ms function is used to create a timer with the given ms interval.
|
||||
* The timer will call the given entry function periodically with the given ms interval.
|
||||
* \example:
|
||||
* CoTimer_t timer = CoTimer_CreateMs(timer_entry, 100); // create a timer with 100ms interval
|
||||
*/
|
||||
CoTimer_t CoTimer_CreateMs(CoTimerEntry_t entry, uint32_t ms);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine timer delete function
|
||||
* \param Timer: Coroutine timer handle, indicates the timer to delete
|
||||
* \note: Coroutine timer delete function is used to delete the given timer.
|
||||
* It must be called after @ref `CoTimer_CreateTick` or @ref `CoTimer_CreateMs`.
|
||||
* \example:
|
||||
* CoTimer_Delete(timer); // delete the timer
|
||||
*/
|
||||
void CoTimer_Delete(CoTimer_t Timer);
|
||||
|
||||
/**
|
||||
* \brief: Coroutine timer self function
|
||||
* \return: Coroutine timer handle, indicates the current timer
|
||||
* \note: Coroutine timer self function is used to get the handle of the current timer.
|
||||
* \example:
|
||||
* CoTimer_t timer = CoTimer_Self(); // get the current timer
|
||||
*/
|
||||
CoTimer_t CoTimer_Self(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // !__coroutine_H
|
||||
160
source/08_coroutine/coroutine_cfg.h
Normal file
160
source/08_coroutine/coroutine_cfg.h
Normal file
@ -0,0 +1,160 @@
|
||||
/*********************************************************************************************************
|
||||
* ------------------------------------------------------------------------------------------------------
|
||||
* file description
|
||||
* ------------------------------------------------------------------------------------------------------
|
||||
* \file coroutine_cfg.h
|
||||
* \unit coroutine
|
||||
* \brief This is a C language coroutine library
|
||||
* \author Lamdonn
|
||||
* \version v0.1.0
|
||||
* \license GPL-2.0
|
||||
* \copyright Copyright (C) 2025 Lamdonn.
|
||||
********************************************************************************************************/
|
||||
#ifndef __coroutine_cfg_H
|
||||
#define __coroutine_cfg_H
|
||||
|
||||
/**
|
||||
* \brief Coroutine static task max number
|
||||
* \note This macro defines the maximum number of coroutine tasks that can be created statically
|
||||
*/
|
||||
#define COROUTINE_STATIC_TASK_MAX_NUMBER 32
|
||||
|
||||
/**
|
||||
* \brief Coroutine static timer max number
|
||||
* \note This macro defines the maximum number of coroutine timers that can be created statically
|
||||
*/
|
||||
#define COROUTINE_STATIC_TIMER_MAX_NUMBER 32
|
||||
|
||||
/**
|
||||
* \brief Coroutine stack default size
|
||||
* \note This macro defines the default size of the coroutine stack
|
||||
* \note If the coroutine stack size is not specified, this value will be used
|
||||
*/
|
||||
#define COROUTINE_STACK_DEFAULT_SIZE 4096 // 10240 //
|
||||
|
||||
/**
|
||||
* \brief Coroutine enable stack calculate
|
||||
* \note This macro defines whether to enable the stack calculate feature
|
||||
* \note If enabled, the coroutine stack using will be calculated automatically
|
||||
*/
|
||||
#define COROUTINE_ENABLE_STACK_CALCULATE 1
|
||||
|
||||
/**
|
||||
* \brief Coroutine enable loading calculate
|
||||
* \note This macro defines whether to enable the loading calculate feature
|
||||
* \note If enabled, the coroutine loading will be calculated automatically
|
||||
*/
|
||||
#define COROUTINE_ENABLE_LOADING_CALCULATE 1
|
||||
|
||||
/**
|
||||
* \brief Coroutine loading queue size
|
||||
* \note This macro defines the size of the coroutine loading queue
|
||||
*/
|
||||
#define COROUTINE_LOADING_CALCULATE_QSIZE 10
|
||||
|
||||
/**
|
||||
* \brief Coroutine loading calculate period
|
||||
* \note This macro defines the period of the coroutine loading calculate
|
||||
* \note The unit is ms
|
||||
*/
|
||||
#define COROUTINE_LOADING_CALCULATE_PERIOD 100
|
||||
|
||||
/**
|
||||
* \brief Stack get and set Pointers depending on the platform definition
|
||||
* \param p Pointer to the stack
|
||||
* \return None
|
||||
* \note This macro is used to get and set the stack pointer depending on the platform definition
|
||||
* \note For x86_32, the stack pointer is esp; for x86_64, the stack pointer is rsp; for arm, the stack pointer is sp
|
||||
* \note Common platforms have been implemented, not implemented platforms need to be implemented by users
|
||||
*/
|
||||
#if defined(__i386__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %%esp, %0" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov %0, %%esp" : : "r" (p) : "memory")
|
||||
#elif defined(__x86_64__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %%rsp, %0" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov %0, %%rsp" : : "r" (p) : "memory")
|
||||
#elif defined(__arm__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__aarch64__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__mips__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile(".set noreorder\n\t" "move %0, $sp\n\t" ".set reorder" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile(".set noreorder\n\t" "move $sp, %0\n\t" ".set reorder" : : "r" (p) : "memory")
|
||||
#elif defined(__riscv__)
|
||||
#if __riscv_xlen == 32
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mv %0, sp" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mv sp, %0" : : "r" (p) : "memory")
|
||||
#else
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mv %0, sp" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mv sp, %0" : : "r" (p) : "memory")
|
||||
#endif
|
||||
#elif defined(__powerpc__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__powerpc64__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__s390__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mr %0, r15" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mr r15, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__s390x__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mr %0, r15" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mr r15, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__sparc__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %%sp, %0" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov %0, %%sp" : : "r" (p) : "memory")
|
||||
#elif defined(__sparcv9__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %%sp, %0" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov %0, %%sp" : : "r" (p) : "memory")
|
||||
#elif defined(__tile__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %%sp, %0" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov %0, %%sp" : : "r" (p) : "memory")
|
||||
#elif defined(__tilegx__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %%sp, %0" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov %0, %%sp" : : "r" (p) : "memory")
|
||||
#elif defined(__hppa__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__hppa64__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__ia64__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %0=sp" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov sp=%0" : : "r" (p) : "memory")
|
||||
#elif defined(__loongarch__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__m68k__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__mips64__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__parisc__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__parisc64__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mr %0, r1" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mr r1, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__s390__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mr %0, r15" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mr r15, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__s390x__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mr %0, r15" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mr r15, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__sh__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__sh64__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("mov %0, sp" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("mov sp, %0" : : "r" (p) : "memory")
|
||||
#elif defined(__xtensa__)
|
||||
#define COROUTINE_GET_STACK(p) __asm__ volatile("rsr.a1 %0" : "=r" (p) : : "memory")
|
||||
#define COROUTINE_SET_STACK(p) __asm__ volatile("wsr.a1 %0" : : "r" (p) : "memory")
|
||||
#else
|
||||
#error "Unsupported platform, please implement `COROUTINE_GET_STACK` and `COROUTINE_SET_STACK`"
|
||||
#endif
|
||||
|
||||
#endif // !__coroutine_cfg_H
|
||||
@ -47,3 +47,4 @@ TEST_LIST += slup
|
||||
# TEST_LIST += cpul
|
||||
TEST_LIST += date
|
||||
TEST_LIST += unitt
|
||||
TEST_LIST += coroutine
|
||||
|
||||
321
test/test_coroutine.c
Normal file
321
test/test_coroutine.c
Normal file
@ -0,0 +1,321 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#if defined(TEST_TARGET_coroutine)
|
||||
#include <varch/command.h>
|
||||
#include <varch/unitt.h>
|
||||
#include <varch/coroutine.h>
|
||||
#else
|
||||
#include "init.h"
|
||||
#include "command.h"
|
||||
#include "unitt.h"
|
||||
#include "kern.h"
|
||||
#include "coroutine.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
uint64_t GetTimerUsec(void)
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
LARGE_INTEGER frequency;
|
||||
QueryPerformanceFrequency(&frequency);
|
||||
QueryPerformanceCounter(&li);
|
||||
return (uint64_t)li.QuadPart * 1000000 / frequency.QuadPart;
|
||||
}
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
uint64_t GetTimerUsec(void)
|
||||
{
|
||||
struct timeval mstime;
|
||||
uint64_t us = 0;
|
||||
gettimeofday(&mstime, NULL);
|
||||
us = mstime.tv_sec * 1000000 + mstime.tv_usec;
|
||||
return us;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#define MUTI_THREAD_NUM 2
|
||||
static pthread_t threads[MUTI_THREAD_NUM];
|
||||
static pthread_t testThread;
|
||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static CoScheduler gMutiScheduler[MUTI_THREAD_NUM];
|
||||
|
||||
static CoScheduler gSingleScheduler;
|
||||
static CoEvent gEvent = COEVENT_STATIC_VALUE;
|
||||
static CoTask_t gTickCoroutine = NULL;
|
||||
static CoTask_t gTockCoroutine = NULL;
|
||||
static uint8_t gStackTick[1024 * 10];
|
||||
static uint8_t gStackTock[1024 * 10];
|
||||
static CoTimer_t gTimer0Handle = NULL;
|
||||
static CoTimer_t gTimer1Handle = NULL;
|
||||
|
||||
static void thread_lock(void)
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
}
|
||||
|
||||
static void thread_unlock(void)
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
static void *tick(void *arg)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
CoTask_Wait(.ms=1000);
|
||||
printf("tick-running...\n");
|
||||
CoEvent_Notify(&gEvent, 0x02);
|
||||
}
|
||||
}
|
||||
|
||||
static void *tock(void *arg)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
uint32_t evs = CoTask_Wait(.pEvent=&gEvent);
|
||||
if (evs & 0x01)
|
||||
{
|
||||
printf("event 0x01 triggered...\n");
|
||||
}
|
||||
if (evs & 0x02)
|
||||
{
|
||||
printf("event 0x02 triggered...\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void timer0(void)
|
||||
{
|
||||
printf("timer0-running...\n");
|
||||
}
|
||||
|
||||
static void timer1(void)
|
||||
{
|
||||
printf("timer1-running... %d\n", CoScheduler_CurLoad(CoTimer_Self()->pScheduler));
|
||||
// CoEvent_Notify(&gEvent, 0x01 | 0x02);
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
/************************************* Unit Test ************************************/
|
||||
/************************************************************************************/
|
||||
|
||||
// #define EXIT_TEST
|
||||
extern uint64_t unitt_clock(void);
|
||||
|
||||
static int test_0(void)
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
if (0)
|
||||
{
|
||||
|
||||
#if defined (EXIT_TEST)
|
||||
exit(0);
|
||||
#endif
|
||||
return UNITT_E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return UNITT_E_OK;
|
||||
}
|
||||
|
||||
static void unitt_task(void)
|
||||
{
|
||||
static UNITT_TCASE rand_tests[] = {
|
||||
UNITT_TCASE(test_0),
|
||||
// UNITT_TCASE(test_1),
|
||||
// UNITT_TCASE(test_2),
|
||||
};
|
||||
|
||||
static UNITT suites[] = {
|
||||
{ "coroutine suite", rand_tests, sizeof(rand_tests) / sizeof(rand_tests[0]) , unitt_clock },
|
||||
};
|
||||
|
||||
UNITT_EXE(suites);
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
/************************************* Base Test ************************************/
|
||||
/************************************************************************************/
|
||||
|
||||
static void test_base(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void test_singleThread(void)
|
||||
{
|
||||
CoScheduler_Init(&gSingleScheduler, GetTimerUsec, 1000);
|
||||
gTickCoroutine = CoTask_Create(tick, gStackTick, sizeof(gStackTick));
|
||||
gTockCoroutine = CoTask_Create(tock, gStackTock, sizeof(gStackTock));
|
||||
gTimer0Handle = CoTimer_CreateMs(timer0, 1000);
|
||||
gTimer1Handle = CoTimer_CreateMs(timer1, 1000);
|
||||
CoScheduler_Start(&gSingleScheduler);
|
||||
}
|
||||
|
||||
static void *muti_thread_entry(void *arg)
|
||||
{
|
||||
int threadId = *(int *)arg;
|
||||
CoScheduler_Init(&gMutiScheduler[threadId], GetTimerUsec, 1000, thread_lock, thread_unlock);
|
||||
if (threadId == 0)
|
||||
{
|
||||
gTickCoroutine = CoTask_Create(tick, gStackTick, sizeof(gStackTick));
|
||||
}
|
||||
else
|
||||
{
|
||||
gTockCoroutine = CoTask_Create(tock, gStackTock, sizeof(gStackTock));
|
||||
}
|
||||
CoScheduler_Start(&gMutiScheduler[threadId]);
|
||||
}
|
||||
|
||||
void test_mutiThread(void)
|
||||
{
|
||||
int threadId[MUTI_THREAD_NUM];
|
||||
for (int i = 0; i < MUTI_THREAD_NUM; i++)
|
||||
{
|
||||
threadId[i] = i;
|
||||
pthread_create(&threads[i], NULL, muti_thread_entry, &threadId[i]);
|
||||
|
||||
/* You can bind thread to specific core */
|
||||
/* So that each CoScheduler can run on different core */
|
||||
}
|
||||
for (int i = 0; i < MUTI_THREAD_NUM; i++)
|
||||
{
|
||||
pthread_join(threads[i], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************************/
|
||||
/************************************* Command ************************************/
|
||||
/************************************************************************************/
|
||||
|
||||
static void usage(void)
|
||||
{
|
||||
printf(
|
||||
"Usage: coroutine [opt] [arg] ...\n"
|
||||
"\n"
|
||||
"options:\n"
|
||||
" -e <execute> Specifies the function to execute, the default is the <base> test\n"
|
||||
" <base> Test base function\n"
|
||||
" <ut> Unit test\n"
|
||||
" <single> Test single thread\n"
|
||||
" <muti> Test muti thread\n"
|
||||
" ...\n"
|
||||
" -h Print help\n"
|
||||
" -v Print version\n"
|
||||
" -u [<period>] Unit test period, unit ms, the default is 1000ms\n"
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
static int test(int argc, char *argv[])
|
||||
{
|
||||
char *execute = NULL;
|
||||
int ut_period = 1000;
|
||||
|
||||
/* reset getopt */
|
||||
command_opt_init();
|
||||
|
||||
while (1)
|
||||
{
|
||||
int opt = command_getopt(argc, argv, "e:hvu::");
|
||||
if (opt == -1) break;
|
||||
|
||||
switch (opt)
|
||||
{
|
||||
// Add others opt here
|
||||
case 'u' :
|
||||
if (command_optarg) ut_period = atoi(command_optarg);
|
||||
break;
|
||||
case 'e' :
|
||||
execute = command_optarg;
|
||||
break;
|
||||
case 'v' :
|
||||
printf("coroutine version %d.%d.%d\r\n", COROUTINE_V_MAJOR, COROUTINE_V_MINOR, COROUTINE_V_PATCH);
|
||||
return 0;
|
||||
case '?':
|
||||
printf("Unknown option `%c`\r\n", command_optopt);
|
||||
return -1;
|
||||
case 'h' :
|
||||
default:
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (execute)
|
||||
{
|
||||
if (!strcmp(execute, "base"))
|
||||
{
|
||||
test_base();
|
||||
}
|
||||
else if (!strcmp(execute, "ut"))
|
||||
{
|
||||
#if defined(TEST_TARGET_coroutine)
|
||||
while (1)
|
||||
{
|
||||
unitt_task();
|
||||
usleep(1000 * ut_period);
|
||||
}
|
||||
#else
|
||||
printf("create task %d\r\n", task_create(ut_period, unitt_task));
|
||||
#endif
|
||||
}
|
||||
else if (!strcmp(execute, "single"))
|
||||
{
|
||||
test_singleThread();
|
||||
}
|
||||
else if (!strcmp(execute, "muti"))
|
||||
{
|
||||
test_mutiThread();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
test_base();
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (1 == command_optind) // no opt
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
for (int index = command_optind; index < argc; index++)
|
||||
{
|
||||
if (!strcmp(argv[index], "base"))
|
||||
{
|
||||
test_base();
|
||||
}
|
||||
}
|
||||
|
||||
if (1 == argc)
|
||||
{
|
||||
test_base();
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
/************************************ Test entry ************************************/
|
||||
/************************************************************************************/
|
||||
|
||||
#if defined(TEST_TARGET_coroutine)
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
return test(argc, argv);
|
||||
}
|
||||
#else
|
||||
void test_coroutine(void)
|
||||
{
|
||||
command_export("coroutine", test);
|
||||
}
|
||||
init_export_app(test_coroutine);
|
||||
#endif
|
||||
Loading…
x
Reference in New Issue
Block a user