top | item 47054517

Show HN: Writing a C++20M:N Scheduler from Scratch (EBR, Work-Stealing)

18 points| lixiasky | 13 days ago |github.com

tiny_coro is a lightweight, educational M:N asynchronous runtime written from scratch using C++20 coroutines. It's designed to strip away the complexity of industrial libraries (like Seastar or Folly) to show the core mechanics clearly.

Key Technical Features:

M:N Scheduling: Maps M coroutines to N kernel threads (Work-Stealing via Chase-Lev deque).

Memory Safety: Implements EBR (Epoch-Based Reclamation) to manage memory safely in lock-free structures without GC.

Visualizations: I used Manim (the engine behind 3Blue1Brown) to create animations showing exactly how tasks are stolen and executed.

Why I built it: To bridge the gap between "using coroutines" and "understanding the runtime." The code is kept minimal (~1k LOC core) so it can be read in a weekend.

20 comments

order

srean|13 days ago

Any Fortran folks around ?

Since old Fortran does not have function pointers I used to wonder how does one write gradient descent routine for a user defined function in Fortran.

In other languages, one typically passes the user defined function and it's gradient to the routine as callbacks, so that the routine can call them at will.

It was a moment of great amusement and joy (grinning like a silly kid) when I learned how Fortran did it. It was through a pared down form of a coroutine. Inversion of control.

The gradient descent routine would effectively suspend and 'return' with a flag indicating that it needs the user defined function computed, or the gradient function computed.

These are computed outside and then the routine is called again with these recently computed values. At this point it resumes from where it had yielded. Pretty neat.

The routine's return value indicated whether it is done, or that it is suspended and expects to be called again to resume.

Now I don't remember if the suspended state was stored transparently by the run time, or explicitly in the arguments of the routine (pass by reference, I think), or whether the local, routine specific 'program counter' was managed explicitly or transparently by the compiler and run time.

lixiasky|13 days ago

That is a fascinating comparison! History really does rhyme. What you described—suspending with a flag and resuming with new values—is surprisingly similar to how the C++20 compiler transforms coroutines into a state machine under the hood. In tiny_coro, the promise_type essentially manages that "inversion of control" you mentioned, but hides the manual state management behind the co_await syntax. It's really cool to see that these fundamental patterns for "stopping and resuming" computation have existed for so long, just in different forms.

throwaway81523|13 days ago

Fortran has always had function pointers though they were called something different, and could only be used in limited contexts. But look at any numerical integration or root finding library. You just pass the function name in as a parameter to another function.

throwaway81523|13 days ago

This looks cool, but says it's simpler than Seastar. Seastar last time I looked at it was fairly simple, but didn't use coroutines at all. Instead it used a futures/promise scheme. It was in C++14, back when C++20 coroutines didn't exist yet.

C++20 coroutines have seemed very complex to me and I haven't sat down to try to understand them. I guess tiny_coro could help for that. I do remember that they were stackless.

I'm not that keen on coroutine and async approaches to concurrency anyway these days. I'd rather use something with actual multitasking, either with Posix threads or with lightweight processes like Erlang's or Go's.

Also, C++ itself is apparently in the process of sinking into a bog. Rust is taking over, which is not entirely good news. I had wanted to spend more time on Ada.

lixiasky|13 days ago

You hit the nail on the head. C++20 coroutines are indeed complex and the barrier to entry is high. However, that complexity actually forced me to start from first principles. It drove me to tackle the essential problems from the ground up, which gave me a much deeper understanding of how coroutines truly work. That is exactly why I built this project—I wanted to create a minimal "laboratory" to dissect stackless coroutines without the overhead of a massive framework like Seastar. Regarding your point on Erlang/Go: That's actually the goal of this scheduler! It implements the M:N threading model (Work-Stealing) to simulate that kind of "lightweight process" concurrency, but giving you manual control over the mechanics. Hope this helps you finally wrap your head around co_await!

srean|13 days ago

Does anyone know where to find the mailing list archives of Stackless Python ?

I want to track how its design evolved over time.

Sincere apologies for the hijack. Hopefully it too will be of interest to those who are interested in coroutines.