varch/source/08_coroutine/coroutine_hook.c
Lamdonn c0a7f18093 Update coroutine
1. Compatibility with coroutine API highly intensive IO, sockets, etc
2. Fix running fail on arm64
3. Add low power configuration hook
TODO:
1. Add a scheduling ready task queue mechanism
2. Add CoTask communication channel
2026-10-11 00:01:44 +08:00

470 lines
18 KiB
C

/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file coroutine_hook.c
* \unit coroutine
* \brief Predefined idleHook and wakeupHook implementations for Bare-Metal, RTOS and OS platforms
* \author Lamdonn
* \version v0.2.0
* \license GPL-2.0
* \copyright Copyright (C) 2025 Lamdonn.
********************************************************************************************************/
#include "coroutine_hook.h"
#include "coroutine.h"
/* =======================================================================================================
* 1. Bare-metal / Embedded MCU Hooks
* ======================================================================================================= */
void CoHook_Baremetal_Idle(uint32_t schedulerId, uint32_t idleUs)
{
(void)schedulerId;
(void)idleUs;
#if defined(__arm__) || defined(__aarch64__)
__asm__ volatile ("wfi");
#elif defined(__riscv__) || defined(__riscv)
__asm__ volatile ("wfi");
#elif defined(__xtensa__)
__asm__ volatile ("waiti 0");
#elif defined(__CSKY__)
__asm__ volatile ("wait");
#elif defined(__MSP430__)
__asm__ volatile ("bis #0x0018, r2");
#elif defined(__ARC__) || defined(__arc__)
__asm__ volatile ("sleep");
#elif defined(__mips__) || defined(__mips64__)
__asm__ volatile ("wait");
#elif defined(__loongarch__)
__asm__ volatile ("idle 0");
#elif defined(__sh__) || defined(__sh64__)
__asm__ volatile ("sleep");
#elif defined(__m68k__)
__asm__ volatile ("stop #0x2000");
#elif defined(__AVR__)
__asm__ volatile ("sleep");
#elif defined(__powerpc__) || defined(__powerpc64__)
__asm__ volatile ("or 31, 31, 31");
#elif defined(__i386__) || defined(__x86_64__)
__asm__ volatile ("pause");
#else
/* Freestanding CPU architecture fallback: no-op */
__asm__ volatile ("");
#endif
}
void CoHook_Baremetal_Wakeup(uint32_t schedulerId)
{
(void)schedulerId;
#if defined(__arm__) || defined(__aarch64__)
__asm__ volatile ("sev");
#elif defined(__MSP430__)
__asm__ volatile ("bic #0x0018, 0(r1)");
#else
/* Handled automatically by hardware interrupt return (NVIC/CLIC/PLIC) */
__asm__ volatile ("");
#endif
}
/* =======================================================================================================
* 2. FreeRTOS Hooks (Task Notification based)
* ======================================================================================================= */
#if defined(INC_FREERTOS_H) || defined(COROUTINE_HOOK_FREERTOS)
#include "FreeRTOS.h"
#include "task.h"
static TaskHandle_t s_freertos_tasks[COROUTINE_SCHEDULER_NUMBER] = {0};
int CoHook_FreeRTOS_Init(uint32_t schedulerId, void *taskHandle)
{
if (schedulerId >= COROUTINE_SCHEDULER_NUMBER) return COROUTINE_E_INVALID_PARAMETER;
s_freertos_tasks[schedulerId] = (taskHandle != NULL) ? (TaskHandle_t)taskHandle : xTaskGetCurrentTaskHandle();
return 0;
}
void CoHook_FreeRTOS_Deinit(uint32_t schedulerId)
{
if (schedulerId < COROUTINE_SCHEDULER_NUMBER)
{
s_freertos_tasks[schedulerId] = NULL;
}
}
void CoHook_FreeRTOS_Idle(uint32_t schedulerId, uint32_t idleUs)
{
if (schedulerId >= COROUTINE_SCHEDULER_NUMBER) return;
TickType_t xTicks = (idleUs == 0) ? portMAX_DELAY : (TickType_t)((uint64_t)idleUs * configTICK_RATE_HZ / 1000000ULL);
if (xTicks == 0 && idleUs > 0) xTicks = 1;
ulTaskNotifyTake(pdTRUE, xTicks);
}
void CoHook_FreeRTOS_Wakeup(uint32_t schedulerId)
{
if (schedulerId < COROUTINE_SCHEDULER_NUMBER && s_freertos_tasks[schedulerId] != NULL)
{
xTaskNotifyGive(s_freertos_tasks[schedulerId]);
}
}
#else
int CoHook_FreeRTOS_Init(uint32_t schedulerId, void *taskHandle) { (void)schedulerId; (void)taskHandle; return -1; }
void CoHook_FreeRTOS_Idle(uint32_t schedulerId, uint32_t idleUs) { (void)schedulerId; (void)idleUs; }
void CoHook_FreeRTOS_Wakeup(uint32_t schedulerId) { (void)schedulerId; }
void CoHook_FreeRTOS_Deinit(uint32_t schedulerId) { (void)schedulerId; }
#endif
/* =======================================================================================================
* 3. RT-Thread Hooks (rt_sem semaphore based)
* ======================================================================================================= */
#if defined(RT_TICK_PER_SECOND) || defined(COROUTINE_HOOK_RTTHREAD)
#include <rtthread.h>
static rt_sem_t s_rtt_sems[COROUTINE_SCHEDULER_NUMBER] = {0};
int CoHook_RTThread_Init(uint32_t schedulerId)
{
if (schedulerId >= COROUTINE_SCHEDULER_NUMBER) return COROUTINE_E_INVALID_PARAMETER;
if (s_rtt_sems[schedulerId] == RT_NULL)
{
s_rtt_sems[schedulerId] = rt_sem_create("co_sem", 0, RT_IPC_FLAG_PRIO);
if (s_rtt_sems[schedulerId] == RT_NULL) return -1;
}
return 0;
}
void CoHook_RTThread_Deinit(uint32_t schedulerId)
{
if (schedulerId < COROUTINE_SCHEDULER_NUMBER && s_rtt_sems[schedulerId] != RT_NULL)
{
rt_sem_delete(s_rtt_sems[schedulerId]);
s_rtt_sems[schedulerId] = RT_NULL;
}
}
void CoHook_RTThread_Idle(uint32_t schedulerId, uint32_t idleUs)
{
if (schedulerId >= COROUTINE_SCHEDULER_NUMBER || s_rtt_sems[schedulerId] == RT_NULL) return;
rt_int32_t ticks = (idleUs == 0) ? RT_WAITING_FOREVER : (rt_int32_t)((uint64_t)idleUs * RT_TICK_PER_SECOND / 1000000ULL);
if (ticks == 0 && idleUs > 0) ticks = 1;
rt_sem_take(s_rtt_sems[schedulerId], ticks);
}
void CoHook_RTThread_Wakeup(uint32_t schedulerId)
{
if (schedulerId < COROUTINE_SCHEDULER_NUMBER && s_rtt_sems[schedulerId] != RT_NULL)
{
rt_sem_release(s_rtt_sems[schedulerId]);
}
}
#else
int CoHook_RTThread_Init(uint32_t schedulerId) { (void)schedulerId; return -1; }
void CoHook_RTThread_Idle(uint32_t schedulerId, uint32_t idleUs) { (void)schedulerId; (void)idleUs; }
void CoHook_RTThread_Wakeup(uint32_t schedulerId) { (void)schedulerId; }
void CoHook_RTThread_Deinit(uint32_t schedulerId) { (void)schedulerId; }
#endif
/* =======================================================================================================
* 4. CMSIS-RTOS2 Hooks (ARM Standard - Keil RTX5, FreeRTOS-CMSIS wrapper, etc.)
* ======================================================================================================= */
#if defined(CMSIS_OS2_H_) || defined(COROUTINE_HOOK_CMSIS_RTOS2)
#include "cmsis_os2.h"
static osThreadId_t s_cmsis_threads[COROUTINE_SCHEDULER_NUMBER] = {0};
int CoHook_CMSIS_Init(uint32_t schedulerId, void *threadId)
{
if (schedulerId >= COROUTINE_SCHEDULER_NUMBER) return COROUTINE_E_INVALID_PARAMETER;
s_cmsis_threads[schedulerId] = (threadId != NULL) ? (osThreadId_t)threadId : osThreadGetId();
return 0;
}
void CoHook_CMSIS_Deinit(uint32_t schedulerId)
{
if (schedulerId < COROUTINE_SCHEDULER_NUMBER)
{
s_cmsis_threads[schedulerId] = NULL;
}
}
void CoHook_CMSIS_Idle(uint32_t schedulerId, uint32_t idleUs)
{
if (schedulerId >= COROUTINE_SCHEDULER_NUMBER) return;
uint32_t millis = (idleUs == 0) ? osWaitForever : (uint32_t)((idleUs + 999) / 1000);
osThreadFlagsWait(0x01, osFlagsWaitAny, millis);
}
void CoHook_CMSIS_Wakeup(uint32_t schedulerId)
{
if (schedulerId < COROUTINE_SCHEDULER_NUMBER && s_cmsis_threads[schedulerId] != NULL)
{
osThreadFlagsSet(s_cmsis_threads[schedulerId], 0x01);
}
}
#else
int CoHook_CMSIS_Init(uint32_t schedulerId, void *threadId) { (void)schedulerId; (void)threadId; return -1; }
void CoHook_CMSIS_Idle(uint32_t schedulerId, uint32_t idleUs) { (void)schedulerId; (void)idleUs; }
void CoHook_CMSIS_Wakeup(uint32_t schedulerId) { (void)schedulerId; }
void CoHook_CMSIS_Deinit(uint32_t schedulerId) { (void)schedulerId; }
#endif
/* =======================================================================================================
* 5. uC/OS-III Hooks (Task Semaphore based)
* ======================================================================================================= */
#if defined(OS_CFG_H) || defined(COROUTINE_HOOK_UCOS)
#include <os.h>
static OS_TCB *s_ucos_tcbs[COROUTINE_SCHEDULER_NUMBER] = {0};
int CoHook_UCOS_Init(uint32_t schedulerId, void *pTcb)
{
if (schedulerId >= COROUTINE_SCHEDULER_NUMBER) return COROUTINE_E_INVALID_PARAMETER;
s_ucos_tcbs[schedulerId] = (pTcb != NULL) ? (OS_TCB *)pTcb : OSTCBCurPtr;
return 0;
}
void CoHook_UCOS_Deinit(uint32_t schedulerId)
{
if (schedulerId < COROUTINE_SCHEDULER_NUMBER)
{
s_ucos_tcbs[schedulerId] = NULL;
}
}
void CoHook_UCOS_Idle(uint32_t schedulerId, uint32_t idleUs)
{
if (schedulerId >= COROUTINE_SCHEDULER_NUMBER) return;
OS_ERR err;
CPU_TS ts;
OS_TICK dly = (idleUs == 0) ? 0 : (OS_TICK)((uint64_t)idleUs * OSCfg_TickRate_Hz / 1000000ULL);
if (dly == 0 && idleUs > 0) dly = 1;
OSTaskSemPend(dly, OS_OPT_PEND_BLOCKING, &ts, &err);
}
void CoHook_UCOS_Wakeup(uint32_t schedulerId)
{
if (schedulerId < COROUTINE_SCHEDULER_NUMBER && s_ucos_tcbs[schedulerId] != NULL)
{
OS_ERR err;
OSTaskSemPost(s_ucos_tcbs[schedulerId], OS_OPT_POST_NONE, &err);
}
}
#else
int CoHook_UCOS_Init(uint32_t schedulerId, void *pTcb) { (void)schedulerId; (void)pTcb; return -1; }
void CoHook_UCOS_Idle(uint32_t schedulerId, uint32_t idleUs) { (void)schedulerId; (void)idleUs; }
void CoHook_UCOS_Wakeup(uint32_t schedulerId) { (void)schedulerId; }
void CoHook_UCOS_Deinit(uint32_t schedulerId) { (void)schedulerId; }
#endif
/* =======================================================================================================
* 6. Windows Implementation
* ======================================================================================================= */
#if defined(_WIN32)
#include <windows.h>
static HANDLE s_win32_events[COROUTINE_SCHEDULER_NUMBER] = {0};
int CoHook_Win32_Init(uint32_t schedulerId)
{
if (schedulerId >= COROUTINE_SCHEDULER_NUMBER)
{
return COROUTINE_E_INVALID_PARAMETER;
}
if (s_win32_events[schedulerId] == NULL)
{
s_win32_events[schedulerId] = CreateEventA(NULL, FALSE, FALSE, NULL); // Auto-reset event
if (s_win32_events[schedulerId] == NULL)
{
return -1;
}
}
return 0;
}
void CoHook_Win32_Deinit(uint32_t schedulerId)
{
if (schedulerId < COROUTINE_SCHEDULER_NUMBER && s_win32_events[schedulerId] != NULL)
{
CloseHandle(s_win32_events[schedulerId]);
s_win32_events[schedulerId] = NULL;
}
}
void CoHook_Win32_Idle(uint32_t schedulerId, uint32_t idleUs)
{
if (schedulerId < COROUTINE_SCHEDULER_NUMBER && s_win32_events[schedulerId] != NULL)
{
DWORD ms = (idleUs == 0) ? INFINITE : (DWORD)((idleUs + 999) / 1000);
WaitForSingleObject(s_win32_events[schedulerId], ms);
}
}
void CoHook_Win32_Wakeup(uint32_t schedulerId)
{
if (schedulerId < COROUTINE_SCHEDULER_NUMBER && s_win32_events[schedulerId] != NULL)
{
SetEvent(s_win32_events[schedulerId]);
}
}
#endif
/* =======================================================================================================
* 7. POSIX / Linux / Android / macOS / iOS / QNX / VxWorks / NuttX Implementation
* ======================================================================================================= */
#if defined(__linux__) || defined(__unix__) || defined(__APPLE__) || defined(__QNX__) || defined(__QNXNTO__) || defined(__ANDROID__) || defined(__VXWORKS__) || defined(__NuttX__)
#include <pthread.h>
#include <time.h>
#include <errno.h>
static pthread_mutex_t s_posix_mutex[COROUTINE_SCHEDULER_NUMBER];
static pthread_cond_t s_posix_cond[COROUTINE_SCHEDULER_NUMBER];
static uint8_t s_posix_inited[COROUTINE_SCHEDULER_NUMBER] = {0};
int CoHook_Posix_Init(uint32_t schedulerId)
{
if (schedulerId >= COROUTINE_SCHEDULER_NUMBER)
{
return COROUTINE_E_INVALID_PARAMETER;
}
if (!s_posix_inited[schedulerId])
{
pthread_mutex_init(&s_posix_mutex[schedulerId], NULL);
pthread_cond_init(&s_posix_cond[schedulerId], NULL);
s_posix_inited[schedulerId] = 1;
}
return 0;
}
void CoHook_Posix_Deinit(uint32_t schedulerId)
{
if (schedulerId < COROUTINE_SCHEDULER_NUMBER && s_posix_inited[schedulerId])
{
pthread_mutex_destroy(&s_posix_mutex[schedulerId]);
pthread_cond_destroy(&s_posix_cond[schedulerId]);
s_posix_inited[schedulerId] = 0;
}
}
void CoHook_Posix_Idle(uint32_t schedulerId, uint32_t idleUs)
{
if (schedulerId >= COROUTINE_SCHEDULER_NUMBER || !s_posix_inited[schedulerId]) return;
pthread_mutex_lock(&s_posix_mutex[schedulerId]);
if (idleUs == 0)
{
pthread_cond_wait(&s_posix_cond[schedulerId], &s_posix_mutex[schedulerId]);
}
else
{
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
uint64_t nsec = (uint64_t)ts.tv_nsec + (uint64_t)idleUs * 1000ULL;
ts.tv_sec += (time_t)(nsec / 1000000000ULL);
ts.tv_nsec = (long)(nsec % 1000000000ULL);
pthread_cond_timedwait(&s_posix_cond[schedulerId], &s_posix_mutex[schedulerId], &ts);
}
pthread_mutex_unlock(&s_posix_mutex[schedulerId]);
}
void CoHook_Posix_Wakeup(uint32_t schedulerId)
{
if (schedulerId < COROUTINE_SCHEDULER_NUMBER && s_posix_inited[schedulerId])
{
pthread_mutex_lock(&s_posix_mutex[schedulerId]);
pthread_cond_signal(&s_posix_cond[schedulerId]);
pthread_mutex_unlock(&s_posix_mutex[schedulerId]);
}
}
#endif
/* Fallback stubs if compiled on non-matching platform */
#if !defined(_WIN32)
int CoHook_Win32_Init(uint32_t schedulerId) { (void)schedulerId; return -1; }
void CoHook_Win32_Idle(uint32_t schedulerId, uint32_t idleUs) { (void)schedulerId; (void)idleUs; }
void CoHook_Win32_Wakeup(uint32_t schedulerId) { (void)schedulerId; }
void CoHook_Win32_Deinit(uint32_t schedulerId) { (void)schedulerId; }
#endif
#if !defined(__linux__) && !defined(__unix__) && !defined(__APPLE__) && !defined(__QNX__) && !defined(__QNXNTO__) && !defined(__ANDROID__) && !defined(__VXWORKS__) && !defined(__NuttX__)
int CoHook_Posix_Init(uint32_t schedulerId) { (void)schedulerId; return -1; }
void CoHook_Posix_Idle(uint32_t schedulerId, uint32_t idleUs) { (void)schedulerId; (void)idleUs; }
void CoHook_Posix_Wakeup(uint32_t schedulerId) { (void)schedulerId; }
void CoHook_Posix_Deinit(uint32_t schedulerId) { (void)schedulerId; }
#endif
/* =======================================================================================================
* 8. Unified / Auto-Detect Platform Dispatcher
* ======================================================================================================= */
int CoScheduler_BindDefaultHooks(uint32_t schedulerId)
{
if (schedulerId >= COROUTINE_SCHEDULER_NUMBER)
{
return COROUTINE_E_INVALID_PARAMETER;
}
#if defined(INC_FREERTOS_H) || defined(COROUTINE_HOOK_FREERTOS)
int ret = CoHook_FreeRTOS_Init(schedulerId, NULL);
if (ret != 0) return ret;
CoScheduler_SetIdleHook(schedulerId, CoHook_FreeRTOS_Idle);
CoScheduler_SetWakeupHook(schedulerId, CoHook_FreeRTOS_Wakeup);
#elif defined(RT_TICK_PER_SECOND) || defined(COROUTINE_HOOK_RTTHREAD)
int ret = CoHook_RTThread_Init(schedulerId);
if (ret != 0) return ret;
CoScheduler_SetIdleHook(schedulerId, CoHook_RTThread_Idle);
CoScheduler_SetWakeupHook(schedulerId, CoHook_RTThread_Wakeup);
#elif defined(CMSIS_OS2_H_) || defined(COROUTINE_HOOK_CMSIS_RTOS2)
int ret = CoHook_CMSIS_Init(schedulerId, NULL);
if (ret != 0) return ret;
CoScheduler_SetIdleHook(schedulerId, CoHook_CMSIS_Idle);
CoScheduler_SetWakeupHook(schedulerId, CoHook_CMSIS_Wakeup);
#elif defined(OS_CFG_H) || defined(COROUTINE_HOOK_UCOS)
int ret = CoHook_UCOS_Init(schedulerId, NULL);
if (ret != 0) return ret;
CoScheduler_SetIdleHook(schedulerId, CoHook_UCOS_Idle);
CoScheduler_SetWakeupHook(schedulerId, CoHook_UCOS_Wakeup);
#elif defined(_WIN32)
int ret = CoHook_Win32_Init(schedulerId);
if (ret != 0) return ret;
CoScheduler_SetIdleHook(schedulerId, CoHook_Win32_Idle);
CoScheduler_SetWakeupHook(schedulerId, CoHook_Win32_Wakeup);
#elif defined(__linux__) || defined(__unix__) || defined(__APPLE__) || defined(__QNX__) || defined(__QNXNTO__) || defined(__ANDROID__) || defined(__VXWORKS__) || defined(__NuttX__)
int ret = CoHook_Posix_Init(schedulerId);
if (ret != 0) return ret;
CoScheduler_SetIdleHook(schedulerId, CoHook_Posix_Idle);
CoScheduler_SetWakeupHook(schedulerId, CoHook_Posix_Wakeup);
#else
CoScheduler_SetIdleHook(schedulerId, CoHook_Baremetal_Idle);
CoScheduler_SetWakeupHook(schedulerId, CoHook_Baremetal_Wakeup);
#endif
return COROUTINE_E_OK;
}
int CoScheduler_UnbindDefaultHooks(uint32_t schedulerId)
{
if (schedulerId >= COROUTINE_SCHEDULER_NUMBER)
{
return COROUTINE_E_INVALID_PARAMETER;
}
CoScheduler_SetIdleHook(schedulerId, NULL);
CoScheduler_SetWakeupHook(schedulerId, NULL);
#if defined(INC_FREERTOS_H) || defined(COROUTINE_HOOK_FREERTOS)
CoHook_FreeRTOS_Deinit(schedulerId);
#elif defined(RT_TICK_PER_SECOND) || defined(COROUTINE_HOOK_RTTHREAD)
CoHook_RTThread_Deinit(schedulerId);
#elif defined(CMSIS_OS2_H_) || defined(COROUTINE_HOOK_CMSIS_RTOS2)
CoHook_CMSIS_Deinit(schedulerId);
#elif defined(OS_CFG_H) || defined(COROUTINE_HOOK_UCOS)
CoHook_UCOS_Deinit(schedulerId);
#elif defined(_WIN32)
CoHook_Win32_Deinit(schedulerId);
#elif defined(__linux__) || defined(__unix__) || defined(__APPLE__) || defined(__QNX__) || defined(__QNXNTO__) || defined(__ANDROID__) || defined(__VXWORKS__) || defined(__NuttX__)
CoHook_Posix_Deinit(schedulerId);
#endif
return COROUTINE_E_OK;
}