Skip to content

iwadon/continuation-scribble

Repository files navigation

continuation-scribble

English | 日本語

Experimental project exploring low-level continuation and cooperative fiber (coroutine) implementation in C and assembly. Supports multiple CPU architectures.

Supported 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

Build Prerequisites

m68k-xelf

Run scripts/setup-x68k-tools.sh beforehand. This installs run68x and has060x.x into m68k-xelf/tools/.

% ./scripts/setup-x68k-tools.sh

Build Instructions

Building 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)

Running Tests

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

Layer Structure

1. Context Layer (lowest level)

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)

2. Fiber Layer

Cooperative userspace threads with explicit yielding. Each fiber has its own stack.

3. Scheduler Layer

Round-robin cooperative scheduler for managing fiber execution.

4. Continuation Layer

Full continuation support with stack image capture/restore. Supports multi-shot continuations (can resume the same continuation multiple times).

5. Effect Handler Layer

Algebraic effects using delimited continuations. Provides EFF_HANDLE/EFF_WITH/EFF_END macros for structured effect handling.

Using the Continuation Layer

Basic API

#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 up

Multi-shot Continuations

cont_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 behavior

Important Usage Notes

1. Store continuations outside the captured stack

Continuation 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
    }
}

2. Set cont_stack_base before cont_save

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;
}

3. Use nested functions for proper stack capture

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;
}

Error Handling

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;
    // ...
}

License

This project is licensed under MIT No Attribution (MIT-0). See LICENSE for details.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors