varch/doc/coroutine.md
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

18 KiB
Raw Blame History

目录

概述

协程模块是一种轻量级的用户态线程管理机制在程序开发中扮演着重要角色主要用于资源受限的嵌入式设备、网络编程、异步IO、实时数据处理等需要高效并发的场景。 相比传统线程具有更低的资源消耗(无需内核态切换)和更快的上下文切换速度,通过协程模块,开发者能够实现高效的任务切换和并发执行,从而提升程序的性能和响应速度。

coroutine 是一个高性能、轻量级的C语言协程库采用非对称有栈协程设计。该库提供了完整的协程生命周期管理支持多任务调度、定时器管理和事件同步机制提供简洁的创建、调度与同步API接口可无缝集成到嵌入式系统、服务器程序等多种环境中。并且移植起来也比较简单只需要在目标平台上编译并链接该模块即可已支持主流的芯片内核架构

设计理念

  • 简单易用提供直观的API接口
  • 移植方便:移植所需资源依赖少
  • 高效性能:低开销的上下文切换
  • 稳定可靠:错误处理和资源管理

核心特性

1. 架构特性

  • 非对称有栈协程:每个协程拥有独立的栈空间
  • 多调度器支持:可配置多个独立的协程调度器
  • 混合内存管理:支持静态预分配和动态内存管理
  • 跨平台兼容支持x86、x64、ARM、MIPS等多种CPU架构尚未完全测试完

2. 协程状态管理

协程调度器状态定义

#define COSCHEDULER_STATE_INTI      (0)  // 初始化状态
#define COSCHEDULER_STATE_EXIT      (1)  // 退出状态
#define COSCHEDULER_STATE_START     (2)  // 启动状态
#define COSCHEDULER_STATE_RUNNING   (3)  // 运行状态
#define COSCHEDULER_STATE_SCHEDULE  (4)  // 调度状态

协程任务状态定义

#define COTASK_STATE_INTI          (0)  // 初始化状态
#define COTASK_STATE_READY         (1)  // 就绪状态
#define COTASK_STATE_RUNNING       (2)  // 运行状态
#define COTASK_STATE_SUSPEND       (3)  // 挂起状态
#define COTASK_STATE_DELETED       (4)  // 删除状态

3. 内存管理配置

静态资源限制配置

#define COROUTINE_SCHEDULER_NUMBER             1   // 调度器数量
#define COROUTINE_STATIC_TASK_MAX_NUMBER       8   // 静态任务最大数量
#define COROUTINE_STATIC_TIMER_MAX_NUMBER      8   // 静态定时器最大数量
#define COROUTINE_STATIC_STACK_MAX_NUMBER      8   // 静态栈最大数量
#define COROUTINE_STACK_DEFAULT_SIZE           10240 // 默认栈大小

架构设计

1. 整体架构

+----------------------------------------------------+
| Architecture                                       |
+------------------------------------+---------------+
|                                    |  +---------+  |
| +--------+         +--------+      |  | CoTimer |  |
| |        |         |        |      |  +---------+  |
| |        | CoEvent |        | CoEvent +---------+  |
| | CoTask |<=======>| CoTask |<=======>| CoTimer |  |
| |        |         |        |      |  +---------+  |
| |        |         |        |      |  +---------+  |
| +--------+         +--------+      |  | CoTimer |  |
+------------------------------------+  +---------+  |
|                   CoScheduler                      |
+----------------------------------------------------+

2. 协程上下文切换机制

  • 使用setjmp/longjmp实现快速上下文保存和恢复
  • 每个协程维护独立的栈空间和环境
  • 支持协程挂起、恢复和删除操作

API接口详解

1. 调度器管理接口

CoScheduler_Init - 初始化协程调度器

函数原型:

#define CoScheduler_Init(CoSchedulerId, tick, tickInterval, ...)

函数说明: 初始化指定的协程调度器,设置时钟函数和时钟间隔。此函数必须在调用任何其他协程函数之前执行。 套用 int CoScheduler_InitP(uint32_t CoSchedulerId, CoTick_t tick, uint32_t tickInterval, CoSchedulerInitPara *pPara); 函数,提供更多的初始化参数配置。

参数详解:

  • CoSchedulerId [必需]调度器ID范围0到(COROUTINE_SCHEDULER_NUMBER-1)
  • tick [必需]:时钟函数指针,用于获取当前时间戳
  • tickInterval [必需]时钟间隔表示每个tick对应的纳秒数
  • lock [可选]:锁函数指针,用于多线程环境下的资源保护
  • unlock [可选]:解锁函数指针,用于多线程环境下的资源释放
  • malloc [可选]:内存分配函数指针
  • free [可选]:内存释放函数指针

返回值:

  • COROUTINE_E_OK (0):初始化成功
  • COROUTINE_E_INVALID_PARAMETER (-1):无效参数
  • COROUTINE_E_INVALID_TICK (-2):无效时钟函数
  • COROUTINE_E_INVALID_TICK_INTERVAL (-3):无效时钟间隔
  • COROUTINE_E_INVALID_LOCK (-4):无效锁函数
  • COROUTINE_E_INVALID_MHOOK (-5):无效内存钩子函数

使用示例:

// 基本使用使用微秒时钟1000us间隔
CoScheduler_Init(0, GetTimerUsec, 1000);

// 多线程支持:带锁函数
CoScheduler_Init(0, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock);

// 完整配置:带内存管理函数
CoScheduler_Init(0, GetTimerUsec, 1000, 
    .lock=thread_lock, 
    .unlock=thread_unlock,
    .malloc=malloc, 
    .free=free
);

CoScheduler_Start - 启动协程调度器

函数原型:

int CoScheduler_Start(uint32_t CoSchedulerId);

函数说明: 启动指定的协程调度器,开始执行协程任务调度。

参数:

  • CoSchedulerId调度器ID

返回值:

  • 0启动成功
  • 错误码:启动失败

使用流程:

// 完整的调度器启动流程
CoScheduler_Init(0, GetTimerUsec, 1000);
testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
CoScheduler_Start(0);

CoScheduler_Exit - 退出协程调度器

函数原型:

int CoScheduler_Exit(uint32_t CoSchedulerId);

函数说明: 退出指定的协程调度器,停止所有协程任务并清理资源。

参数:

  • CoSchedulerId调度器ID

返回值:

  • COROUTINE_E_OK:退出成功

2. 协程任务管理接口

CoTask_Create - 创建协程任务

函数原型:

#define CoTask_Create(entry, ...)

函数说明: 创建一个新的协程任务,可以指定栈空间、参数和调度器。 套用 CoTask_t CoTask_CreateP(CoTaskEntry_t entry, CoTaskCreatePara *pPara); 函数,提供更多的初始化参数配置。

参数详解:

  • entry [必需]:协程入口函数指针,类型为void (*)(void*)
  • pStack [可选]栈空间地址如果为NULL则自动分配
  • stackSize [可选]:栈大小,未指定时使用默认值
  • arg [可选]:传递给协程的参数指针
  • schedulerId [可选]调度器ID负值表示自动分配

返回值:

  • CoTask_t:成功创建的协程任务句柄
  • NULL:创建失败

使用示例:

// 使用默认参数创建协程
CoTask_t testCoroutine = CoTask_Create(test);

// 指定栈空间创建协程
CoTask_t testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));

// 指定栈大小创建协程
CoTask_t testCoroutine = CoTask_Create(test, .stackSize=4096);

// 带参数创建协程
int arg = 4096;
CoTask_t testCoroutine = CoTask_Create(test, .arg=&arg);

CoTask_Delete - 删除协程任务

函数原型:

int CoTask_Delete(CoTask_t CoTask);

函数说明: 删除指定的协程任务,释放相关资源。

参数:

  • CoTask:要删除的协程任务句柄

返回值:

  • 0删除成功
  • COROUTINE_E_INVALID_PARAMETER:无效参数

CoTask_Self - 获取当前协程任务

函数原型:

CoTask_t CoTask_Self(void);

函数说明: 获取当前正在运行的协程任务句柄。

返回值:

  • CoTask_t:当前协程任务句柄
  • NULL:不在协程上下文中

CoTask_SchedulerId - 获取协程调度器ID

函数原型:

int CoTask_SchedulerId(void);

函数说明: 获取当前协程任务所属的调度器ID。

返回值:

  • 调度器ID0到(COROUTINE_SCHEDULER_NUMBER-1)

3. 协程等待机制接口

CoTask_Wait - 协程等待函数

函数原型:

#define CoTask_Wait(...)

函数说明: 使当前协程任务等待指定的事件或超时。 套用 uint32_t CoTask_WaitP(CoTaskWaitPara *pPara); 函数,提供更多的等待参数配置。

参数详解:

  • pEvent [可选]:等待的事件指针
  • ms [可选]:等待的毫秒数
  • tick [可选]等待的tick数

返回值:

  • uint32_t:触发的事件标志位

使用示例:

void *test(void *arg) {
    while (1) {
        // 放弃调度器访问权类似yield
        CoTask_Wait();
        
        // 等待1000ms
        CoTask_Wait(.ms=1000);
        
        // 等待事件发生
        uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
        if (evs & 0x01) {
            printf("event 0x01 triggered\n");
        }
        
        // 组合等待:事件+超时
        evs = CoTask_Wait(.pEvent=&g_Event, .ms=5000);
    }
}

套用 CoTask_Wait 可以形成以下常用方法:

#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)

4. 定时器管理接口

CoTimer_Create - 创建协程定时器

函数原型:

#define CoTimer_Create(entry, ...)

函数说明: 创建一个周期性的协程定时器。 套用 CoTimer_t CoTimer_CreateP(CoTimerEntry_t entry, CoTimerCreatePara *pPara); 函数,提供更多的初始化参数配置。

参数详解:

  • entry [必需]:定时器入口函数
  • ms [可选]:毫秒间隔
  • tick [可选]tick间隔

返回值:

  • CoTimer_t:成功创建的定时器句柄
  • NULL:创建失败

使用示例:

// 创建100ms间隔的定时器
CoTimer_t timer = CoTimer_Create(timer_entry, .ms=100);

CoTimer_Delete - 删除协程定时器

函数原型:

void CoTimer_Delete(CoTimer_t Timer);

函数说明: 删除指定的协程定时器。

参数:

  • Timer:要删除的定时器句柄

CoTimer_Self - 获取当前定时器

函数原型:

CoTimer_t CoTimer_Self(void);

函数说明: 获取当前正在运行的协程定时器句柄。

返回值:

  • CoTimer_t:当前定时器句柄
  • NULL:不在定时器上下文中

5. 事件同步接口

CoEvent_Init - 初始化事件

函数原型:

void CoEvent_Init(CoEvent *pEvent);

函数说明: 初始化协程事件,准备用于协程间同步。

参数:

  • pEvent:事件指针

使用示例:

CoEvent_Init(&g_Event);

此方法为运行时调用,静态初始化可以直接赋值为 COEVENT_STATIC_VALUE

CoEvent g_Event = COEVENT_STATIC_VALUE;

CoEvent_Notify - 通知事件

函数原型:

void CoEvent_Notify(CoEvent *pEvent, uint32_t evs);

函数说明: 通知指定的事件,唤醒等待该事件的协程任务。

参数详解:

  • pEvent:事件指针
  • evs:事件标志位,支持多事件同时通知

使用示例:

// 通知单个事件
CoEvent_Notify(&g_Event, 0x01);

// 同时通知多个事件
CoEvent_Notify(&g_Event, 0x01 | 0x02 | 0x04);

6. 监控统计接口

CoTask_StackMaxUsed - 获取协程栈最大使用量

函数原型:

size_t CoTask_StackMaxUsed(CoTask_t CoTask);

函数说明: 获取指定协程任务栈的最大使用量。

参数:

  • CoTask:协程任务句柄

返回值:

  • size_t:栈最大使用量(字节)

CoTask_StackCurUsed - 获取协程栈当前使用量

函数原型:

size_t CoTask_StackCurUsed(CoTask_t CoTask);

函数说明: 获取指定协程任务栈的当前使用量。

参数:

  • CoTask:协程任务句柄

返回值:

  • size_t:栈当前使用量(字节)

CoScheduler_CurLoad - 获取当前负载

函数原型:

uint16_t CoScheduler_CurLoad(uint32_t CoSchedulerId);

函数说明: 获取指定调度器的当前负载。

返回值:

  • uint16_t当前负载百分比0-10000表示0.00%-100.00%

CoScheduler_MaxLoad - 获取最大历史负载

函数原型:

uint16_t CoScheduler_MaxLoad(uint32_t CoSchedulerId);

函数说明: 获取指定调度器的历史最大负载。

返回值:

  • uint16_t:最大负载百分比

7. 统计计数接口

CoScheduler_TaskCount - 获取协程任务数量

函数原型:

int CoScheduler_TaskCount(uint32_t CoSchedulerId);

函数说明: 获取指定调度器中的协程任务数量。

参数:

  • CoSchedulerId调度器ID

返回值:

  • int:协程任务数量

CoScheduler_TimerCount - 获取定时器数量

函数原型:

int CoScheduler_TimerCount(uint32_t CoSchedulerId);

函数说明: 获取指定调度器中的协程定时器数量。

参数:

  • CoSchedulerId调度器ID

返回值:

  • int:定时器数量

配置选项

编译时配置 (coroutine_cfg.h)

基本配置:

// 调度器配置
#define COROUTINE_SCHEDULER_NUMBER             1   // 调度器数量

// 静态资源限制
#define COROUTINE_STATIC_TASK_MAX_NUMBER       8   // 静态任务最大数量
#define COROUTINE_STATIC_TIMER_MAX_NUMBER      8   // 静态定时器最大数量
#define COROUTINE_STATIC_STACK_MAX_NUMBER      8   // 静态栈最大数量

// 栈配置
#define COROUTINE_STACK_DEFAULT_SIZE           10240 // 默认栈大小

功能开关:

// 栈使用量计算
#define COROUTINE_ENABLE_STACK_CALCULATE        1   // 启用栈使用量计算

// 负载计算
#define COROUTINE_ENABLE_LOADING_CALCULATE     1   // 启用负载计算

// 调试支持
#define COROUTINE_ENABLE_DEBUG                   1   // 启用调试功能

使用指南

1. 基本使用流程

初始化阶段

#include "coroutine.h"

// 定义时钟函数
uint64_t GetTimerUsec(void) {
    // 返回微秒级时间戳
    return ...;
}

int main(void) {
    // 初始化调度器0使用微秒时钟1000us间隔
    CoScheduler_Init(0, GetTimerUsec, 1000);

协程定义

// 协程任务函数
void *test_task(void *arg) {
    int *p_count = (int*)arg;
    
    while (1) {
        // 协程任务逻辑
        (*p_count)++;
        
        // 主动让出CPU
        CoTask_Wait();
    }
    
    return NULL;
}

创建和启动

// 创建协程任务
int count = 0;
CoTask_t testCoroutine = CoTask_Create(test_task, .arg=&count);

2. 高级使用模式

事件驱动模式

void *event_driven_task(void *arg) {
    while (1) {
        // 等待事件
        uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
        
        if (evs & 0x01) {
            // 处理事件0x01
            handle_event_01();
        }
        
        if (evs & 0x02) {
            // 处理事件0x02
            handle_event_02();
        }
    }
}

定时器模式

void *timer_task(void *arg) {
    while (1) {
        // 每100ms执行一次
        CoTask_Wait(.ms=100);
        
        // 定时器逻辑
        periodic_work();
    }
}

高级功能

1. 栈使用量计算

启用 COROUTINE_ENABLE_STACK_CALCULATE 后,可以监控协程栈的使用情况:

// 获取协程栈最大使用量
size_t maxUsed = CoTask_StackMaxUsed(CoTask);

// 获取协程栈当前使用量
size_t curUsed = CoTask_StackCurUsed(CoTask);

2. 负载计算

启用 COROUTINE_ENABLE_LOADING_CALCULATE 后,可以计算调度器的负载:

// 获取当前负载
uint16_t curLoad = CoScheduler_CurLoad(CoSchedulerId);

// 获取最大历史负载
uint16_t maxLoad = CoScheduler_MaxLoad(CoSchedulerId);

3. 性能监控

// 监控协程调度性能
void monitor_scheduler_performance(void) {
    uint16_t curLoad = CoScheduler_CurLoad(0);
    uint16_t maxLoad = CoScheduler_MaxLoad(0);
    
    if (curLoad > 8000) { // 80%负载警告
        printf("Warning: Scheduler load is high: %d.%02d%%\n", 
           curLoad/100, curLoad%100);
}

错误处理

错误码定义

#define COROUTINE_E_OK                          (0)   // 成功
#define COROUTINE_E_INVALID_PARAMETER           (-1)  // 无效参数
#define COROUTINE_E_INVALID_TICK                (-2)  // 无效时钟函数
#define COROUTINE_E_INVALID_TICK_INTERVAL       (-3)  // 无效时钟间隔
#define COROUTINE_E_INVALID_LOCK                (-4)  // 无效锁函数
#define COROUTINE_E_INVALID_MHOOK               (-5)  // 无效内存钩子函数

性能优化

1. 上下文切换优化

  • 使用setjmp/longjmp替代完整的上下文保存
  • 最小化寄存器保存和恢复操作

2. 内存管理优化

  • 静态预分配减少动态内存分配
  • 栈空间复用机制

平台支持

该库通过内联汇编实现了跨平台的栈指针操作:

  • x86/x64使用esp/rsp寄存器
  • ARM使用sp寄存器
  • MIPS使用sp寄存器
  • RISC-V使用sp寄存器
  • PowerPC使用r1寄存器

平台特定实现

// x86架构实现
#if defined(__i386__)
    #define COROUTINE_GET_STACK_POINTER(p) \
        __asm__ __volatile__("movl %%esp, %0" : "=r"(p))

应用场景

1. 嵌入式系统

  • 实时任务调度
  • 低功耗设备管理
  • 传感器数据处理

2. 网络编程

  • 异步I/O处理
  • 连接池管理
  • 协议栈实现

3. 游戏开发

  • 游戏对象更新
  • AI行为调度
  • 动画系统管理

4. 物联网应用

  • 设备通信协议
  • 数据处理流水线
  • 远程控制接口

总结

coroutine 协程库是一个功能强大、性能优异的C语言协程解决方案。它提供了

  1. 完整的协程生命周期管理
  2. 高效的多任务调度机制
  3. 灵活的定时器和事件管理
  4. 完善的错误处理和监控功能