English | 日本語
Experimental project exploring low-level continuation and cooperative fiber (coroutine) implementation in C and assembly. Supports multiple CPU architectures.
- arm64-mac: ARM64 (M1 and later Mac) / macOS / clang
- arm64-linux: ARM64 / Linux / gcc
- arm64-win: ARM64 / Windows / MSVC
- x86_64-mac: Intel Mac / macOS / clang
- x86_64-linux: x86_64 / Linux / gcc
- x86_64-win: x86_64 / Windows / MSVC
- m68k-xelf: X68000 / Human68k / elf2x68k + has060x.x
Run scripts/setup-x68k-tools.sh beforehand. This installs run68x and has060x.x into m68k-xelf/tools/.
% ./scripts/setup-x68k-tools.shBuilding requires ninja and a C compiler toolchain.
Each architecture has its own build configuration:
ninja -C arm64-mac # ARM64 (macOS Apple Silicon)
ninja -C arm64-linux # ARM64 (Linux)
ninja -C arm64-win # ARM64 (Windows)
ninja -C x86_64-mac # x86_64 (macOS Intel)
ninja -C x86_64-linux # x86_64 (Linux)
ninja -C x86_64-win # x86_64 (Windows)
ninja -C m68k-xelf # m68k (X68000 cross-compilation)Test binaries are output to build/{arch}/. Run pattern:
./build/{arch}/{test_name} # Unix
build\{arch}\{test_name}.exe # Windows
run68 ./build/m68k-xelf/{test_name}.x # m68k
Available tests (common to all architectures):
| Test | Description |
|---|---|
{arch}_ctx_test |
Context save/resume primitives |
fiber_test |
Cooperative fiber creation, yielding, and scheduling |
cont_test |
Continuation save/resume with stack capture |
cont_clone_test |
Multi-shot continuation cloning |
eff_test |
Algebraic effect handlers using delimited continuations |
Architecture-specific register save/restore. Similar to setjmp/longjmp semantics.
*_ctx_save(): Returns 0 on save, non-zero when resumed*_ctx_resume(): Restores context (never returns)
Cooperative userspace threads with explicit yielding. Each fiber has its own stack.
Round-robin cooperative scheduler for managing fiber execution.
Full continuation support with stack image capture/restore. Supports multi-shot continuations (can resume the same continuation multiple times).
Algebraic effects using delimited continuations. Provides EFF_HANDLE/EFF_WITH/EFF_END macros for structured effect handling.
#include "cont.h"
cont_t cont;
cont_init(&cont); // Initialize continuation structure
if (cont_save(&cont) == 0) {
// First time: continuation saved
// ... do something ...
cont_resume(&cont); // Jump back to cont_save point
}
// Resumed here (cont_save returns 1)
cont_term(&cont); // Clean upcont_t base, c1, c2;
cont_init(&base);
cont_init(&c1);
cont_init(&c2);
if (cont_save(&base) == 0) {
cont_clone(&c1, &base); // Copy continuation
cont_clone(&c2, &base); // Can clone multiple times
cont_resume(&c1); // Resume first copy
}
// Can resume c2 later for multi-shot behaviorContinuation structures must be stored in static/global memory or on the heap, NOT on the stack being captured. Otherwise, resuming will corrupt the continuation structure itself.
// WRONG: cont is on the stack being captured
void bad_example(void) {
cont_t cont; // Stack variable - will be corrupted!
cont_init(&cont);
if (cont_save(&cont) == 0) {
cont_resume(&cont); // Undefined behavior
}
}
// CORRECT: cont is in static memory
static cont_t cont;
void good_example(void) {
cont_init(&cont);
if (cont_save(&cont) == 0) {
cont_resume(&cont); // Works correctly
}
}The cont_stack_base global must be set to mark the top of the stack region to capture. Set it at the entry point, before calling any functions that use continuations.
char *cont_stack_base;
static void get_stack_base(void) {
#if defined(__aarch64__)
asm volatile("mov %0, sp" : "=r"(cont_stack_base));
#elif defined(__human68k__)
asm volatile("move.l %%a7, %0" : "=r"(cont_stack_base));
#elif defined(_MSC_VER) && defined(_WIN64)
cont_stack_base = (char *)_AddressOfReturnAddress();
#elif defined(__x86_64__)
asm volatile("movq %%rsp, %0" : "=r"(cont_stack_base));
#endif
}
int main(void) {
get_stack_base(); // Set stack base FIRST
run_application(); // Then call your code
return 0;
}The stack region captured is from cont_stack_base down to the SP at cont_save time. To capture meaningful state, ensure there's a function call between setting cont_stack_base and calling cont_save.
static cont_t cont;
static void do_work(void) {
// This function's stack frame will be captured
if (cont_save(&cont) == 0) {
// ...
cont_resume(&cont);
}
}
int main(void) {
get_stack_base();
do_work(); // Stack frame of do_work is captured
return 0;
}Set cont_panic_fn to handle errors:
void my_panic(const char *msg) {
fprintf(stderr, "Continuation error: %s\n", msg);
}
int main(void) {
cont_panic_fn = my_panic;
// ...
}This project is licensed under MIT No Attribution (MIT-0). See LICENSE for details.