DEV Community

Cover image for OLSRT — A C11 Concurrency Runtime with Actors, Channels, and an Event Loop
Javad
Javad

Posted on

OLSRT — A C11 Concurrency Runtime with Actors, Channels, and an Event Loop

Hey Dev Community!

Four years ago, if you wanted to write concurrent C on Linux, you ended up writing the same code over and over: an epoll wrapper, a thread pool, a lock-free queue, a promise type, and a way to shut all of it down cleanly without leaking. Every project reinvented this. Some got it right (libuv). Most got it wrong.

OLSRT is our answer to that. It is an Apache-2.0 C11 runtime that ships all of those primitives in one place, with a coherent ownership model and a single event loop that ties them together.

What's actually in the box

  • Actors with process-style isolation — each actor has its own arena and its own green thread.
  • Channels — bounded and unbounded FIFO with deadlines and try-semantics.
  • Promises and Futures — with continuations and explicit value ownership.
  • Event loop — single-threaded, with timers and I/O. Pluggable poller: epoll on Linux, kqueue on BSD/macOS, select as fallback.
  • Parallel pool — N worker threads, FIFO queue, flush and shutdown.
  • Green threads — with assembly context switch on x86_64.
  • Coroutines — cooperative, built on top of green threads.
  • Semaphores — cross-platform, timed waits.
  • Supervisors — Erlang-style trees with restart strategies.
  • Reactive & Streams — observables, backpressure, operator composition.
  • Dataflow — nodes, ports, edges, worker pool.

All in plain C11. No C++, no garbage collector, no external dependencies beyond pthreads and the standard library.

v1.3.1 — the "we actually tested this" release

The previous release shipped without a test suite and, as we later discovered, with eleven real bugs. We spent a week writing regression tests before touching any of them. Then we fixed them one by one, with ASan, UBSan, and TSan running on every commit.

The list of bugs we found — including one ASan-only heap-use-after-free and one memory-ordering bug in a lock-free ring buffer — is more instructive than the feature list. For example:

  • The mailbox in the actor runtime was reading its head and tail pointers with plain loads instead of __atomic_load_n(..., __ATOMIC_ACQUIRE). On x86 this never bit us. On ARM it would have been a data race.
  • ol_actor_send_timeout was implemented as a busy-wait: while (!expired) { try_send(); sleep(1ms); }. The whole point of the function is to give up after a deadline, but the loop was spending most of its time in the kernel, not measuring.
  • ol_arena_free did not validate that the pointer it was handed actually belonged to the arena. This is fine until you have two arenas and mix them up.
  • ol_actor_try_send returned "would block" as soon as the ring buffer filled, ignoring the overflow list. Half of the mailbox was effectively dead.

We now have 22 assertions across six tests, all green under -fsanitize=address,undefined and -fsanitize=thread. That is not "a lot of tests". But it is the difference between a runtime you can trust with a side project and one you cannot.

The demos

Seven small programs live in demos/, each self-contained:

# What it shows
01 Actor + ask/reply + promises
02 1M messages through a bounded channel
03 100 tasks on a 4-worker pool
04 Event loop with one-shot and periodic timers
05 Promise states and .then() continuations
06 Dataflow graph: source → doubler → sink
07 Minimal HTTP server on OLSRT TCP

They are not benchmarks. They are honest "does this thing actually work" probes. Demo 02 ran 1,000,000 messages through a 1024-slot channel on an AMD E2-1800 (dual-core, 1.7 GHz) at roughly 386,000 msg/s. Demo 04 showed timer drift under two microseconds over six periodic fires. Those numbers are not impressive in absolute terms — the machine is from 2012 — but they tell us the primitives are doing what they claim.

What's next

The roadmap is public (see ROADMAP.md in the repo). Short version:

  • v1.3.2 — Drive the actor main loop from the green-thread scheduler. Right now you have to pump the mailbox manually; that is a design mistake we inherited and are fixing.
  • v1.3.3 — Dataflow edge-inbox fix, LSan re-enable.
  • v1.3.4 — ORoutines. A goroutine-like API on top of the green-thread layer.
  • v1.3.6 — Integration with NWP (see below).
  • v2.0 "Apollo" — Cross-platform (Windows IOCP, native macOS kqueue) and the ~66 network protocols currently stubbed in future/network/.

Getting it

git clone https://github.com/OverLab-Group/OLSRT.git
cd OLSRT
make linux
python3 verify.py      # ASan + UBSan + TSan
Enter fullscreen mode Exit fullscreen mode

verify.py takes about two minutes on a modern laptop and either passes all four checks or tells you exactly which file failed.

The README and the docs/ folder have the rest. We would genuinely like bug reports — especially the boring kind that involve an ASan backtrace.

We want you

"The trickiest bug we fixed was a memory-ordering issue in a lock-free ring buffer that only shows up on ARM. Has anyone else written a similar primitive and can share how they tested it?"

Share your ideas, feedbacks, and everything you want about OLSRT, we will listen carefully to your sounds.
Have nice times!

Top comments (0)