# Fiber: fast and lightweight cross-platform coroutines - Fast by using custom assembly per platform: switching coroutines is just a few memory/register swaps (e.g. on amd64: 9 registers) - Minimal: about 300 lines of C and under 100 lines of assembly per platform ## Usecases - replace your state machine with something more readable - replace your event loop callbacks with a more natural thread-like control flow - lazy streams and generators - agent systems (e.g. AI in games) - cooperative threading in general (see `test1.c` for an example) ## Supported platforms - ELF Targets, 32 or 64-bit, running on x86, ARM (e.g. Linux, BSD's), RISC-V 32/64 and PPC64 - Windows 32 or 64-bit x86 - MacOS 64bit x86 and Apple Silicon (special thanks to [@rsmmr](https://github.com/rsmmr) for Apple Silicon support!) Tested on the following machines: - ArchLinux: clang/gcc: 32 and 64-bit builds - FreeBSD 11: clang: 32 and 64-bit builds - Windows x86: MSVC: 32 and 64-bit builds - Windows x86: MinGW: 64-bit builds - RaspberryPI (armv6): gcc builds - RaspberryPI 3 (aarch64): gcc/clang builds - MacOS 10.14: clang 64-bit builds ## Caveats - Its possible to use very small stacks, but it is not recommended, since signal handlers or linkers might run code on your coroutine stack at unexpected times, better use a more conservative stack size of e.g 32kb. - Moving fibers from one OS thread to another is problematic: (caching of thread locals and platform specific use of registers) - If you use C++ exceptions: The top stack frame of each coroutine should catch and rethrow all exceptions in a toplevel fiber (this also applies to Windows SEH) - Interleaving setjmp/longjmp with fiber switching is not allowed - Debuggers are confused by the switching of stacks, in particular backtraces only show the frames of the currently active fiber. ## Building ```shell git clone https://github.com/simonfxr/fiber git submodule update --init mkdir build cd build cmake .. make ``` Or integrate it in your cmake build ```cmake add_subdirectory(path/to/fiber) target_link_libraries(your-target fiber::fiber) ``` ## Example ```c #include #include #include #include typedef struct { Fiber *caller; Fiber *self; const char *str; } Args; /* invoked if coroutine is called after it finished */ void guard(Fiber *self, void *null) { (void) self; (void) null; abort(); } /* entry point */ void coroutine(void *args0) { Args *args = (Args *) args0; printf("From coroutine: %s\n", args->str); fiber_switch(args->self, args->caller); } int main() { Fiber main_fiber; fiber_init_toplevel(&main_fiber); Fiber crt; /* allocate a stack of 16kb, additionally add an unmapped page to detect overflows */ fiber_alloc(&crt, 1024 * 16, guard, NULL, FIBER_FLAG_GUARD_LO); Args args; args.caller = &main_fiber; args.self = &crt; args.str = "Hello"; /* push a new return stack frame, defines the entry point of our coroutine */ fiber_push_return(&crt, coroutine, &args, sizeof args); /* arguments are copied into the coroutine, its safe to destroy them here */ memset(&args, 0, sizeof args); /* run our coroutine */ fiber_switch(&main_fiber, &crt); fiber_destroy(&crt); /* main_fiber does not have to be destroyed */ return 0; } ```