Coroutines: Lightweight Threads for Concurrency - Example code
#include <stdint.h> | |
#include <stdlib.h> | |
#include <stdio.h> | |
asm( | |
"_co_switch:\n" | |
"push %rbx\n" | |
"push %rbp\n" | |
"push %r12\n" | |
"push %r13\n" | |
"push %r14\n" | |
"push %r15\n" | |
"mov (%rdi), %rax\n" | |
"mov %rsp, (%rdi)\n" | |
"mov %rax, %rsp\n" | |
"pop %r15\n" | |
"pop %r14\n" | |
"pop %r13\n" | |
"pop %r12\n" | |
"pop %rbp\n" | |
"pop %rbx\n" | |
"ret\n" | |
"_pre_call_entry:\n" | |
"pop %rax\n" // entry | |
"pop %rdi\n" // co | |
"call *%rax\n" | |
"ud2\n" | |
); | |
void co_switch(void **stack); | |
void pre_call_entry(); | |
struct Coroutine; | |
typedef void (*CoEntry)(struct Coroutine *co); | |
struct Coroutine { | |
void *stack; | |
CoEntry entry; | |
int terminated; | |
}; | |
void call_entry(struct Coroutine *co) { | |
co_switch(&co->stack); | |
co->entry(co); | |
co->terminated = 1; | |
co_switch(&co->stack); | |
} | |
void start_coroutine(struct Coroutine *co) { | |
void **stack = (void **) co->stack; | |
*(--stack) = co; | |
*(--stack) = call_entry; // 16-byte aligned | |
*(--stack) = pre_call_entry; | |
*(--stack) = 0; | |
*(--stack) = 0; | |
*(--stack) = 0; | |
*(--stack) = 0; | |
*(--stack) = 0; | |
*(--stack) = 0; | |
co->stack = (void *) stack; | |
co_switch(&co->stack); | |
} | |
void test_entry(struct Coroutine *co) { | |
int i; | |
for(i = 0; i < 10; i++) { | |
printf("Iteration %d\n", i); | |
co_switch(&co->stack); | |
} | |
} | |
int main() { | |
const int STACK_SIZE = 32768; | |
char *stack = malloc(STACK_SIZE); | |
struct Coroutine co = { | |
.stack = stack + STACK_SIZE, | |
.entry = test_entry, | |
.terminated = 0, | |
}; | |
start_coroutine(&co); | |
while(!co.terminated) { | |
printf("Switching to coroutine\n"); | |
co_switch(&co.stack); | |
} | |
free(stack); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment