/********************************************************************************************************* * ------------------------------------------------------------------------------------------------------ * 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 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 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 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 #include #include 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; }