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
20 KiB
Table of Contents
- Overview
- Core Features
- Architecture Design
- API Interface Details
- Configuration Options
- Usage Guide
- Advanced Features
- Error Handling
- Performance Optimization
- Platform Support
- Application Scenarios
Overview
The coroutine module is a lightweight user-mode thread management mechanism that plays an important role in program development. It is mainly used in scenarios requiring efficient concurrency, such as resource-constrained embedded devices, network programming, asynchronous I/O, and real-time data processing. Compared to traditional threads, it has lower resource consumption (no kernel-mode switching required) and faster context switching speed. Through the coroutine module, developers can achieve efficient task switching and concurrent execution, thereby improving program performance and response speed.
coroutine is a high-performance, lightweight C language coroutine library that uses an asymmetric stackful coroutine design. This library provides complete coroutine lifecycle management, supports multi-task scheduling, timer management, and event synchronization mechanisms. It offers concise APIs for creation, scheduling, and synchronization, and can be seamlessly integrated into various environments such as embedded systems and server programs. It is also relatively simple to port, requiring only compilation and linking of the module on the target platform (mainstream chip kernel architectures are already supported).
Design Philosophy
- Simple and Easy to Use: Provides intuitive API interfaces
- Easy to Port: Requires few resource dependencies for porting
- High Efficiency: Low-overhead context switching
- Stable and Reliable: Error handling and resource management
Core Features
1. Architecture Features
- Asymmetric Stackful Coroutines: Each coroutine has its own independent stack space
- Multiple Scheduler Support: Configurable with multiple independent coroutine schedulers
- Hybrid Memory Management: Supports both static pre-allocation and dynamic memory management
- Cross-Platform Compatibility: Supports various CPU architectures such as x86, x64, ARM, MIPS (not fully tested yet)
2. Coroutine State Management
Coroutine Scheduler State Definitions
#define COSCHEDULER_STATE_INTI (0) // Initialization state #define COSCHEDULER_STATE_EXIT (1) // Exit state #define COSCHEDULER_STATE_START (2) // Start state #define COSCHEDULER_STATE_RUNNING (3) // Running state #define COSCHEDULER_STATE_SCHEDULE (4) // Scheduling state
Coroutine Task State Definitions
#define COTASK_STATE_INTI (0) // Initialization state #define COTASK_STATE_READY (1) // Ready state #define COTASK_STATE_RUNNING (2) // Running state #define COTASK_STATE_SUSPEND (3) // Suspended state #define COTASK_STATE_DELETED (4) // Deleted state
3. Memory Management Configuration
Static Resource Limit Configuration
#define COROUTINE_SCHEDULER_NUMBER 1 // Number of schedulers #define COROUTINE_STATIC_TASK_MAX_NUMBER 8 // Maximum number of static tasks #define COROUTINE_STATIC_TIMER_MAX_NUMBER 8 // Maximum number of static timers #define COROUTINE_STATIC_STACK_MAX_NUMBER 8 // Maximum number of static stacks #define COROUTINE_STACK_DEFAULT_SIZE 10240 // Default stack size
Architecture Design
1. Overall Architecture
+----------------------------------------------------+ | Architecture | +------------------------------------+---------------+ | | +---------+ | | +--------+ +--------+ | | CoTimer | | | | | | | | +---------+ | | | | CoEvent | | CoEvent +---------+ | | | CoTask |<=======>| CoTask |<=======>| CoTimer | | | | | | | | +---------+ | | | | | | | +---------+ | | +--------+ +--------+ | | CoTimer | | +------------------------------------+ +---------+ | | CoScheduler | +----------------------------------------------------+
2. Coroutine Context Switching Mechanism
- Uses setjmp/longjmp to achieve fast context saving and restoration
- Each coroutine maintains independent stack space and environment
- Supports coroutine suspension, resumption, and deletion operations
API Interface Details
1. Scheduler Management Interface
CoScheduler_Init - Initialize Coroutine Scheduler
Function Prototype: #define CoScheduler_Init(CoSchedulerId, tick, tickInterval, ...)
Function Description: Initializes the specified coroutine scheduler, setting the clock function and clock interval. This function must be executed before calling any other coroutine functions. It wraps the int CoScheduler_InitP(uint32_t CoSchedulerId, CoTick_t tick, uint32_t tickInterval, CoSchedulerInitPara *pPara); function, providing more initialization parameter configuration.
Parameter Details:
- CoSchedulerId [Required]: Scheduler ID, range 0 to (COROUTINE_SCHEDULER_NUMBER-1)
- tick [Required]: Clock function pointer, used to get the current timestamp
- tickInterval [Required]: Clock interval, representing the number of nanoseconds corresponding to each tick
- lock [Optional]: Lock function pointer, used for resource protection in multi-threaded environments
- unlock [Optional]: Unlock function pointer, used for resource release in multi-threaded environments
- malloc [Optional]: Memory allocation function pointer
- free [Optional]: Memory release function pointer
Return Value:
- COROUTINE_E_OK (0): Initialization successful
- COROUTINE_E_INVALID_PARAMETER (-1): Invalid parameter
- COROUTINE_E_INVALID_TICK (-2): Invalid clock function
- COROUTINE_E_INVALID_TICK_INTERVAL (-3): Invalid clock interval
- COROUTINE_E_INVALID_LOCK (-4): Invalid lock function
- COROUTINE_E_INVALID_MHOOK (-5): Invalid memory hook function
Usage Example: // Basic usage: use microsecond clock, 1000us interval CoScheduler_Init(0, GetTimerUsec, 1000);
// Multi-thread support: with lock functions CoScheduler_Init(0, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock);
// Complete configuration: with memory management functions CoScheduler_Init(0, GetTimerUsec, 1000, .lock=thread_lock, .unlock=thread_unlock, .malloc=malloc, .free=free );
CoScheduler_Start - Start Coroutine Scheduler
Function Prototype: int CoScheduler_Start(uint32_t CoSchedulerId);
Function Description: Starts the specified coroutine scheduler and begins coroutine task scheduling.
Parameters:
- CoSchedulerId: Scheduler ID
Return Value:
- 0: Start successful
- Error code: Start failed
Usage Process: // Complete scheduler startup process CoScheduler_Init(0, GetTimerUsec, 1000); testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest)); CoScheduler_Start(0);
CoScheduler_Exit - Exit Coroutine Scheduler
Function Prototype: int CoScheduler_Exit(uint32_t CoSchedulerId);
Function Description: Exits the specified coroutine scheduler, stops all coroutine tasks, and cleans up resources.
Parameters:
- CoSchedulerId: Scheduler ID
Return Value:
- COROUTINE_E_OK: Exit successful
2. Coroutine Task Management Interface
CoTask_Create - Create Coroutine Task
Function Prototype: #define CoTask_Create(entry, ...)
Function Description: Creates a new coroutine task, allowing specification of stack space, parameters, and scheduler. It wraps the CoTask_t CoTask_CreateP(CoTaskEntry_t entry, CoTaskCreatePara *pPara); function, providing more initialization parameter configuration.
Parameter Details:
- entry [Required]: Coroutine entry function pointer, type void ()(void)
- pStack [Optional]: Stack space address, automatically allocated if NULL
- stackSize [Optional]: Stack size, uses default value if not specified
- arg [Optional]: Parameter pointer passed to the coroutine
- schedulerId [Optional]: Scheduler ID, negative value means automatic allocation
Return Value:
- CoTask_t: Successfully created coroutine task handle
- NULL: Creation failed
Usage Example: // Create coroutine with default parameters CoTask_t testCoroutine = CoTask_Create(test);
// Create coroutine with specified stack space CoTask_t testCoroutine = CoTask_Create(test, g_StackTest, sizeof(g_StackTest));
// Create coroutine with specified stack size CoTask_t testCoroutine = CoTask_Create(test, .stackSize=4096);
// Create coroutine with parameters int arg = 4096; CoTask_t testCoroutine = CoTask_Create(test, .arg=&arg);
CoTask_Delete - Delete Coroutine Task
Function Prototype: int CoTask_Delete(CoTask_t CoTask);
Function Description: Deletes the specified coroutine task and releases related resources.
Parameters:
- CoTask: Coroutine task handle to be deleted
Return Value:
- 0: Deletion successful
- COROUTINE_E_INVALID_PARAMETER: Invalid parameter
CoTask_Self - Get Current Coroutine Task
Function Prototype: CoTask_t CoTask_Self(void);
Function Description: Gets the handle of the currently running coroutine task.
Return Value:
- CoTask_t: Current coroutine task handle
- NULL: Not in coroutine context
CoTask_SchedulerId - Get Coroutine Scheduler ID
Function Prototype: int CoTask_SchedulerId(void);
Function Description: Gets the scheduler ID to which the current coroutine task belongs.
Return Value:
- Scheduler ID: 0 to (COROUTINE_SCHEDULER_NUMBER-1)
3. Coroutine Waiting Mechanism Interface
CoTask_Wait - Coroutine Wait Function
Function Prototype: #define CoTask_Wait(...)
Function Description: Causes the current coroutine task to wait for a specified event or timeout. It wraps the uint32_t CoTask_WaitP(CoTaskWaitPara *pPara); function, providing more waiting parameter configuration.
Parameter Details:
- pEvent [Optional]: Event pointer to wait for
- ms [Optional]: Number of milliseconds to wait
- tick [Optional]: Number of ticks to wait
Return Value:
- uint32_t: Triggered event flag bits
Usage Example: void *test(void *arg) { while (1) { // Give up scheduler access right, similar to yield CoTask_Wait();
// Wait for 1000ms
CoTask_Wait(.ms=1000);
// Wait for event to occur
uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
if (evs & 0x01) {
printf("event 0x01 triggered\n");
}
// Combined wait: event + timeout
evs = CoTask_Wait(.pEvent=&g_Event, .ms=5000);
}
}
Wrapping CoTask_Wait can form the following commonly used methods: #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. Timer Management Interface
CoTimer_Create - Create Coroutine Timer
Function Prototype: #define CoTimer_Create(entry, ...)
Function Description: Creates a periodic coroutine timer. It wraps the CoTimer_t CoTimer_CreateP(CoTimerEntry_t entry, CoTimerCreatePara *pPara); function, providing more initialization parameter configuration.
Parameter Details:
- entry [Required]: Timer entry function
- ms [Optional]: Millisecond interval
- tick [Optional]: Tick interval
Return Value:
- CoTimer_t: Successfully created timer handle
- NULL: Creation failed
Usage Example: // Create timer with 100ms interval CoTimer_t timer = CoTimer_Create(timer_entry, .ms=100);
CoTimer_Delete - Delete Coroutine Timer
Function Prototype: void CoTimer_Delete(CoTimer_t Timer);
Function Description: Deletes the specified coroutine timer.
Parameters:
- Timer: Timer handle to be deleted
CoTimer_Self - Get Current Timer
Function Prototype: CoTimer_t CoTimer_Self(void);
Function Description: Gets the handle of the currently running coroutine timer.
Return Value:
- CoTimer_t: Current timer handle
- NULL: Not in timer context
5. Event Synchronization Interface
CoEvent_Init - Initialize Event
Function Prototype: void CoEvent_Init(CoEvent *pEvent);
Function Description: Initializes a coroutine event, preparing it for inter-coroutine synchronization.
Parameters:
- pEvent: Event pointer
Usage Example: CoEvent_Init(&g_Event);
This method is called at runtime, while static initialization can directly assign COEVENT_STATIC_VALUE. CoEvent g_Event = COEVENT_STATIC_VALUE;
CoEvent_Notify - Notify Event
Function Prototype: void CoEvent_Notify(CoEvent *pEvent, uint32_t evs);
Function Description: Notifies the specified event, waking up coroutine tasks waiting for that event.
Parameter Details:
- pEvent: Event pointer
- evs: Event flag bits, supports simultaneous notification of multiple events
Usage Example: // Notify single event CoEvent_Notify(&g_Event, 0x01);
// Notify multiple events simultaneously CoEvent_Notify(&g_Event, 0x01 | 0x02 | 0x04);
6. Monitoring Statistics Interface
CoTask_StackMaxUsed - Get Coroutine Stack Maximum Usage
Function Prototype: size_t CoTask_StackMaxUsed(CoTask_t CoTask);
Function Description: Gets the maximum stack usage of the specified coroutine task.
Parameters:
- CoTask: Coroutine task handle
Return Value:
- size_t: Maximum stack usage (bytes)
CoTask_StackCurUsed - Get Coroutine Stack Current Usage
Function Prototype: size_t CoTask_StackCurUsed(CoTask_t CoTask);
Function Description: Gets the current stack usage of the specified coroutine task.
Parameters:
- CoTask: Coroutine task handle
Return Value:
- size_t: Current stack usage (bytes)
CoScheduler_CurLoad - Get Current Load
Function Prototype: uint16_t CoScheduler_CurLoad(uint32_t CoSchedulerId);
Function Description: Gets the current load of the specified scheduler.
Return Value:
- uint16_t: Current load percentage (0-10000, representing 0.00%-100.00%)
CoScheduler_MaxLoad - Get Maximum Historical Load
Function Prototype: uint16_t CoScheduler_MaxLoad(uint32_t CoSchedulerId);
Function Description: Gets the historical maximum load of the specified scheduler.
Return Value:
- uint16_t: Maximum load percentage
7. Statistical Counting Interface
CoScheduler_TaskCount - Get Coroutine Task Count
Function Prototype: int CoScheduler_TaskCount(uint32_t CoSchedulerId);
Function Description: Gets the number of coroutine tasks in the specified scheduler.
Parameters:
- CoSchedulerId: Scheduler ID
Return Value:
- int: Number of coroutine tasks
CoScheduler_TimerCount - Get Timer Count
Function Prototype: int CoScheduler_TimerCount(uint32_t CoSchedulerId);
Function Description: Gets the number of coroutine timers in the specified scheduler.
Parameters:
- CoSchedulerId: Scheduler ID
Return Value:
- int: Number of timers
Configuration Options
Compile-time Configuration (coroutine_cfg.h)
Basic Configuration: // Scheduler configuration #define COROUTINE_SCHEDULER_NUMBER 1 // Number of schedulers
// Static resource limits #define COROUTINE_STATIC_TASK_MAX_NUMBER 8 // Maximum number of static tasks #define COROUTINE_STATIC_TIMER_MAX_NUMBER 8 // Maximum number of static timers #define COROUTINE_STATIC_STACK_MAX_NUMBER 8 // Maximum number of static stacks
// Stack configuration #define COROUTINE_STACK_DEFAULT_SIZE 10240 // Default stack size
Feature Switches: // Stack usage calculation #define COROUTINE_ENABLE_STACK_CALCULATE 1 // Enable stack usage calculation
// Load calculation #define COROUTINE_ENABLE_LOADING_CALCULATE 1 // Enable load calculation
// Debug support #define COROUTINE_ENABLE_DEBUG 1 // Enable debug function
Usage Guide
1. Basic Usage Process
Initialization Phase
#include "coroutine.h"
// Define clock function uint64_t GetTimerUsec(void) { // Return microsecond-level timestamp return ...; }
int main(void) { // Initialize scheduler 0, use microsecond clock, 1000us interval CoScheduler_Init(0, GetTimerUsec, 1000); }
Coroutine Definition
// Coroutine task function void *test_task(void *arg) { int p_count = (int)arg;
while (1) {
// Coroutine task logic
(*p_count)++;
// Voluntarily yield CPU
CoTask_Wait();
}
return NULL;
}
Creation and Startup
// Create coroutine task int count = 0; CoTask_t testCoroutine = CoTask_Create(test_task, .arg=&count); }
2. Advanced Usage Patterns
Event-Driven Pattern
void *event_driven_task(void *arg) { while (1) { // Wait for event uint32_t evs = CoTask_Wait(.pEvent=&g_Event);
if (evs & 0x01) {
// Handle event 0x01
handle_event_01();
}
if (evs & 0x02) {
// Handle event 0x02
handle_event_02();
}
}
}
Timer Pattern
void *timer_task(void *arg) { while (1) { // Execute every 100ms CoTask_Wait(.ms=100);
// Timer logic
periodic_work();
}
}
Advanced Features
1. Stack Usage Calculation
After enabling COROUTINE_ENABLE_STACK_CALCULATE, you can monitor coroutine stack usage:
// Get coroutine stack maximum usage size_t maxUsed = CoTask_StackMaxUsed(CoTask);
// Get coroutine stack current usage size_t curUsed = CoTask_StackCurUsed(CoTask); }
2. Load Calculation
After enabling COROUTINE_ENABLE_LOADING_CALCULATE, you can calculate the scheduler's load:
// Get current load uint16_t curLoad = CoScheduler_CurLoad(CoSchedulerId);
// Get maximum historical load uint16_t maxLoad = CoScheduler_MaxLoad(CoSchedulerId); }
3. Performance Monitoring
// Monitor coroutine scheduling performance void monitor_scheduler_performance(void) { uint16_t curLoad = CoScheduler_CurLoad(0); uint16_t maxLoad = CoScheduler_MaxLoad(0);
if (curLoad > 8000) { // 80% load warning
printf("Warning: Scheduler load is high: %d.%02d%%\n",
curLoad/100, curLoad%100);
} }
Error Handling
Error Code Definitions
#define COROUTINE_E_OK (0) // Success #define COROUTINE_E_INVALID_PARAMETER (-1) // Invalid parameter #define COROUTINE_E_INVALID_TICK (-2) // Invalid clock function #define COROUTINE_E_INVALID_TICK_INTERVAL (-3) // Invalid clock interval #define COROUTINE_E_INVALID_LOCK (-4) // Invalid lock function #define COROUTINE_E_INVALID_MHOOK (-5) // Invalid memory hook function
Performance Optimization
1. Context Switching Optimization
- Uses setjmp/longjmp instead of complete context saving
- Minimizes register save and restore operations
2. Memory Management Optimization
- Static pre-allocation reduces dynamic memory allocation
- Stack space reuse mechanism
Platform Support
This library implements cross-platform stack pointer operations through inline assembly:
- x86/x64: Uses esp/rsp registers
- ARM: Uses sp register
- MIPS: Uses sp register
- RISC-V: Uses sp register
- PowerPC: Uses r1 register
Platform-Specific Implementation
// x86 architecture implementation
#if defined(i386)
#define COROUTINE_GET_STACK_POINTER(p)
asm volatile("movl %%esp, %0" : "=r"(p))
}
Application Scenarios
1. Embedded Systems
- Real-time task scheduling
- Low-power device management
- Sensor data processing
2. Network Programming
- Asynchronous I/O processing
- Connection pool management
- Protocol stack implementation
3. Game Development
- Game object updates
- AI behavior scheduling
- Animation system management
4. IoT Applications
- Device communication protocols
- Data processing pipelines
- Remote control interfaces
Summary
The coroutine coroutine library is a powerful, high-performance C language coroutine solution. It provides:
- Complete coroutine lifecycle management
- Efficient multi-task scheduling mechanism
- Flexible timer and event management
- Comprehensive error handling and monitoring functions