Skip to content

Instantly share code, notes, and snippets.

@yrvsyh
Last active October 12, 2021 03:06
Show Gist options
  • Save yrvsyh/189293a31e8458aa5c497b4585cd28b6 to your computer and use it in GitHub Desktop.
Save yrvsyh/189293a31e8458aa5c497b4585cd28b6 to your computer and use it in GitHub Desktop.
基于ucontext的非对称独立栈协程实现
#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