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_eventcan be used to synchronize coroutines even on different threads.cone_waitandcone_wakehave semantics similar to Linux'sFUTEX_WAITandFUTEX_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_waitaccepts 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_dropis 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_eventand therefore allows a coroutine on one thread to wait for the completion of a coroutine on another.cone_cancelis atomic and ordered w.r.t. all coroutine-blocking calls within its target.cone_deadlineis NOT thread-safe; it can only be used on coroutines within the same loop.