cone icon indicating copy to clipboard operation
cone copied to clipboard

Fearful concurrency

cone

Stackful coroutines in the least moral language ever.

On supported platforms

Probably all UNIX-like OS on x86-64. 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.

Building libraries for dummies

# 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

An overview of available functions

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 for libcone.a.

  • CONE_DEFAULT_STACK: (bytes; default = 64k) the stack size for coroutines created via the cone(f, arg) macro (as opposed to cone_spawn(stksz, cone_bind(f, arg))).

In which various details are documented

  • 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 and cone_wake have semantics similar to Linux's FUTEX_WAIT and FUTEX_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 also cone_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, aka cone_join, is implemented in terms of cone_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.