Skip to content

Instantly share code, notes, and snippets.

@losfair
Created January 14, 2019 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save losfair/60af3f0c565470e305b6c021ce57e3dc to your computer and use it in GitHub Desktop.
Save losfair/60af3f0c565470e305b6c021ce57e3dc to your computer and use it in GitHub Desktop.
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