Stackful coroutines in the least moral language ever.
Probably all UNIX-like OS on x86-64 or arm64. Tested on Linux and macOS.
Supporting other architectures is a simple matter of adding relevant assembler code
to cone_switch
and cone_body
, as well as stack setup code to cone_spawn_on
.
In theory.
# The pure C version:
make CFLAGS=-O3 obj/libcone.a
# The version that requires linking with libc++abi or similar, but supports cone-local exception state:
make CFLAGS=-O3 obj/libcxxcone.a
Tests:
# The fast ones:
make CFLAGS=-O3 tests/cone
# The slow ones, which measure the time spent doing some pointless operations:
make CFLAGS=-O3 tests/perf
See cone.h
. Error handling is in mun.h
. There are coroutine-blocking versions of
some standard library functions in cold.h
.
Some options (CFLAGS="... -DOPTION=VALUE"
):
-
CONE_EV_{SELECT,EPOLL,KQUEUE}: (0 or 1 each) default is epoll on Linux, kqueue on macOS and FreeBSD (got lazy with macros for other BSDs there), select everywhere else.
-
CONE_CXX: (0 or 1) whether to save exception state to the stack before switching. This requires a C++ ABI library. Enabled for
libcxxcone.a
, disabled forlibcone.a
. -
CONE_DEFAULT_STACK: (bytes; default = 64k) the stack size for coroutines created via the
cone(f, arg)
macro (as opposed tocone_spawn(stksz, cone_bind(f, arg))
).
-
Address sanitizer: supported.
-
Shadow call stacks: will break everything. Don't use them.
-
Parallelism: the scheduler is N:1. Each event loop is bound to a thread, and coroutines spawned on that loop will stay on it.
cone_event
can be used to synchronize coroutines even on different threads.cone_wait
andcone_wake
have semantics similar to Linux'sFUTEX_WAIT
andFUTEX_WAKE
, except the storage for "implementation artifacts", as the kernel calls them, is provided by the user of the library (for performance and simplicity of the implementation), and alsocone_wait
accepts an arbitrary expression that is evaluated atomically instead of only checking for equality.(Note that the locks are kind of bad, though. My advice is to only use events in non performance critical or low contention places. Or even better, don't share events between threads.)
-
Thread-safety:
cone_drop
is atomic. The coroutine will be freed either by the calling thread, or by the thread to which it is pinned.cone_cowait
, akacone_join
, is implemented in terms ofcone_event
and therefore allows a coroutine on one thread to wait for the completion of a coroutine on another.cone_cancel
is atomic and ordered w.r.t. all coroutine-blocking calls within its target.cone_deadline
is NOT thread-safe; it can only be used on coroutines within the same loop.