Jank Concurrency Model Proposal: Stackless Coroutine Concurrency with C++20 and ASIO #707
Replies: 3 comments 8 replies
-
|
Hey @jeaye, our conversation on Reddit got me thinking. Your reference to C++ coroutines was especially interesting. I ended up spending my evening working on this proposal. I've tried to be thorough, and taken AI help to refine and compile it into the final document. That said, this is obviously a rough sketch cooked up in a short time. Would love to know your thoughts. |
Beta Was this translation helpful? Give feedback.
-
|
As a corollary, if jank's current |
Beta Was this translation helpful? Give feedback.
-
|
Hi, @jeaye! Hope you're well! I now have a stable build, and benchmarks to share. There are also helper libraries for: Now that benchmarks are stable and I have telemetry in place, I'll be working on further optimizations. LMK what you think! |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Jank Concurrency Model Proposal: Stackless Coroutines with C++20 and ASIO
API Reference
(future & body)(deref f)/@ffresolves; rethrows on exception or cancellation(cancelled? f)fis inCANCELLEDstate(cancel f)Suspension occurs only at
future/derefboundaries — ordinary functions need no async annotation. The top level runs in an implicit root coroutine.MVP scope: global pool, futures, deref, cancellation, shutdown, native async I/O.
Post-MVP: custom pools, blocking FFI offload (
on-io-pool), foreign-thread re-entrancy (jank_post).Architecture
asio::io_context)All ready-queue pushes and semaphore posts go through
enqueue— push and post are always paired:Each ForkJoin worker:
Startup:
Futures
States:
PENDING → {RESOLVED | EXCEPTION | CANCELLED}. Transitions are atomic and final. Always managed viashared_ptr.resultis written insidewaiters_mubeforestate.store. The waiter list is moved out before flushing — each handle enqueued exactly once:derefAwaitableawait_readyandawait_suspendare not atomic — re-checkclosedafter acquiring the lock. The awaitable holds ashared_ptrto its future for the entire suspension interval:Async I/O
The awaitable is heap-allocated via
shared_ptr— captured by value in the completion lambda, eliminating the dangling-thishazard. The reactor callsenqueue; a ForkJoin worker resumes the coroutine:Exceptions and Cancellation
Exceptions inside a
futurebody are caught at the coroutine boundary and rethrown byderef— matching Clojure semantics. The entire body includingco_returnis wrapped:cancelsetsalive = falsebefore flushing waiters. In-flight ASIO handlers check the flag and skipenqueue, preventing a cancelled coroutine from being re-queued. Running bodies must poll(cancelled? f)—canceldoes not preempt:Shutdown
initiate_shutdowncalled (idempotent; also called fromunhandled_exception)CancellationExceptionwork.reset()releases the ASIO work guard;io_ctx.run()returnsruntime::cleanuprunsCoroutine Frame GC Integration
Jank uses Boehm GC. Suspended frames live on the heap outside Boehm's stack scan and must be explicitly rooted. Frames are allocated via
GC_malloc_uncollectable; size is stored at allocation for use in register/unregister without ABI tricks. Roots are registered beforeenqueue— before any worker or GC cycle can see the handle:Known limitation: GC correctness under high optimization depends on the compiler not eliminating interior pointers inside the frame. MVP mitigations: build at
-O1initially; GC tests must include suspended coroutines holding the only live reference to a jank object; TSan + fuzzer pass required before shipping.Runtime Guarantees
derefsuspends viaco_await; pool thread picks up other workGC_malloc_uncollectable+ size stored at allocation; roots registered before enqueueenqueue; push and post always pairedalive=falseset before flush; in-flight handlers skip enqueueshared_ptr-managed; waiters hold a copy for the suspension intervalshared_ptr; captured by value in lambdastry/catchPost-MVP
on-io-pool/asio::thread_pooljank_post/ foreign-thread re-entrancymake-pool,future my-pool)Beta Was this translation helpful? Give feedback.
All reactions