Last active
October 12, 2021 03:06
-
-
Save yrvsyh/189293a31e8458aa5c497b4585cd28b6 to your computer and use it in GitHub Desktop.
基于ucontext的非对称独立栈协程实现
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdint.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <ucontext.h> | |
#define COROUTINE_DEAD 0 | |
#define COROUTINE_READY 1 | |
#define COROUTINE_RUNNING 2 | |
#define COROUTINE_SUSPEND 3 | |
#define COROUTINE_STACK_SIZE 1024 * 1024 | |
typedef void (*co_func_t)(void *); | |
typedef struct coroutine { | |
ucontext_t ctx; | |
ucontext_t swap_ctx; | |
int status; | |
size_t stack_size; | |
co_func_t func; | |
void *arg; | |
} coroutine_t; | |
static __thread coroutine_t *g_current = NULL; | |
typedef struct co_freelist_node { | |
void *co; | |
void *stack; | |
size_t stack_size; | |
} co_freelist_node_t; | |
static void __co_freelist_add(coroutine_t *co) { | |
co_freelist_node_t *node = (co_freelist_node_t *)malloc(sizeof(co_freelist_node_t)); | |
node->co = co; | |
node->stack = co->ctx.uc_stack.ss_sp; | |
node->stack_size = co->stack_size; | |
// TODO add node to freelist | |
} | |
static coroutine_t *__co_freelist_get() { | |
// TODO get node from freelist | |
return NULL; | |
} | |
static inline void __co_free(coroutine_t *co) { __co_freelist_add(co); } | |
static inline void __co_swap_in(coroutine_t *co) { swapcontext(&co->swap_ctx, &co->ctx); } | |
static inline void __co_swap_out(coroutine_t *co) { swapcontext(&co->ctx, &co->swap_ctx); } | |
static void __co_run(uint32_t low32, uint32_t high32) { | |
uintptr_t ptr = (uintptr_t)low32 | ((uintptr_t)high32 << 32); | |
coroutine_t *co = (coroutine_t *)ptr; | |
co->func(co->arg); | |
co->status = COROUTINE_DEAD; | |
__co_free(co); | |
__co_swap_out(co); | |
} | |
void co_resume(coroutine_t *co) { | |
if (co->status == COROUTINE_READY) { | |
getcontext(&co->ctx); | |
if (!co->ctx.uc_stack.ss_sp) { | |
co->ctx.uc_stack.ss_sp = malloc(co->stack_size); | |
co->ctx.uc_stack.ss_size = co->stack_size; | |
co->ctx.uc_stack.ss_flags = 0; | |
co->ctx.uc_link = &co->swap_ctx; | |
} | |
uintptr_t ptr = (uintptr_t)co; | |
makecontext(&co->ctx, (void (*)(void))__co_run, 2, (uint32_t)ptr, (uint32_t)(ptr >> 32)); | |
} | |
if (co->status == COROUTINE_READY || co->status == COROUTINE_SUSPEND) { | |
co->status = COROUTINE_RUNNING; | |
coroutine_t *self = g_current; | |
g_current = co; | |
__co_swap_in(co); | |
g_current = self; | |
} | |
} | |
void co_yield() { | |
if (g_current) { | |
g_current->status = COROUTINE_SUSPEND; | |
__co_swap_out(g_current); | |
} | |
} | |
coroutine_t *co_create(co_func_t co_func, void *arg) { | |
coroutine_t *co = __co_freelist_get(); | |
if (!co) { | |
co = (coroutine_t *)malloc(sizeof(coroutine_t)); | |
co->ctx.uc_stack.ss_sp = NULL; | |
} | |
co->status = COROUTINE_READY; | |
co->stack_size = COROUTINE_STACK_SIZE; | |
co->func = co_func; | |
co->arg = arg; | |
return co; | |
} | |
void foo(void *arg) { | |
coroutine_t *co_bar = arg; | |
printf("foo begin\n"); | |
co_resume(co_bar); | |
co_yield(); | |
printf("foo end\n"); | |
} | |
void bar(void *arg) { | |
printf("bar begin\n"); | |
co_yield(); | |
printf("bar end\n"); | |
} | |
int main() { | |
coroutine_t *co_bar = co_create(bar, NULL); | |
coroutine_t *co_foo = co_create(foo, co_bar); | |
co_resume(co_foo); | |
printf("main_1\n"); | |
co_resume(co_bar); | |
printf("main_2\n"); | |
co_resume(co_foo); | |
printf("done\n"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment