#zigcoro

1 messages · Page 1 of 1 (latest)

mighty portal
#

https://github.com/rsepassi/zigcoro

An implementation of stackful asymmetric coroutines in Zig (and some assembly).

Allows for 3 kinds of usage: suspend/resume, yield/next (coroutine-based generators), and async/await.

Supports Mac, Linux, and Windows, x86_64 and aarch64 (x86_64 under CI on GitHub).

Planning on hooking this up with libuv or libxev soon so that I can start using async/await-like code before (stackless) async lands.

Was fun learning some simple assembly and exploring how clean and powerful coroutines can be. Curious to hear what you think, if you see places where things can be improved, or if you'd like to see or contribute some features. It's my second Zig library, so any and all comments welcome!

GitHub

A Zig coroutine library. Contribute to rsepassi/zigcoro development by creating an account on GitHub.

neat quail
#

@echo pier :)

next python
#

more coroutines libs

#

lel

heavy bolt
# mighty portal https://github.com/rsepassi/zigcoro An implementation of stackful asymmetric co...

Looking neat, but I dont understand the Performance section:
I am looking at "2 coroutines millions of times." and suddently at the bottom comes up "Verified that a Linux box with ~400GB of memory can spawn and swap between 100M simple coroutines without issue."

I guess that are 2 different benchmark setups you did, which would be nice to be explicit about.
Also, it would be very nice, if the user could reproduce the benchmarks.

mighty portal
heavy bolt
#

Thanks for making it understandable and reproducible.

mighty portal
#

for sure! thanks for taking a look

mighty portal
next python
#

i have my own in my game engine, is fun seeing other takes

mighty portal
#

sweet. have a link? coros plus asyncio give "Fibers", i.e. lightweight threads. next bit i'm thinking about is how to cleanly enable parallel io so that you can launch a bunch of stuff and wait for its completion (either asReady or waitAll). seems like a FiberPool and Futures of some kind might be the way to go. not sure yet, but would be curious about others' takes.

low idol
echo pier
low idol
mighty portal
#

Update: v0.3.0 provides asyncio.run to run a coroutine from the top-level and asyncio.xawait to launch and await several coroutines concurrently. Starting to feel pretty close to async/await.

mighty portal
#

Update: v0.4.0 has an API that lines up with Zig's 0.10.1/expected async API. I wrote up what the translation would look like here: https://github.com/rsepassi/zigcoro#switching-to-zigs-asyncawait. Seems to me like it'd be a trivial switch, but I wonder if I'm missing anything; would be nice to get another pair of eyes on it. @echo pier you seem to be Zig's concurrency king :), would love to get your thoughts if you end up taking a look.

GitHub

A Zig coroutine library. Contribute to rsepassi/zigcoro development by creating an account on GitHub.

echo pier
mighty portal
#

thanks! yeah, go for it. seeming like we can really get started with stackful async/await and have it be a trivial swap later. on suspend blocks: yeah, i suppose that's a missing feature in zigcoro right now...let me see what i can do.

#

i haven't needed it, even when integrating with the libxev event loop, so i'm not really sure what it's use case is...

#

but for completeness/parity, it'd be nice to support it...

echo pier
#

guess you could also technically have xasync/xresume hold a lock on the frame while its running to prevent that (but thats kinda inefficient)

mighty portal
#

hmm...yeah i haven't yet considered multi-threading in detail. i suppose having the discipline of only stashing frames in suspend blocks ensures that you can't resume a still-running coroutine...

echo pier
#

thinking about it more, it could actually be solved with a single atomic u8, instead of a suspend block or lock

mighty portal
#

exactly what i was thinking!

#

the coro status could be atomic

#

CoroStatus is currently enum { Start, Suspended, Active, Done }

#

just because you have a frame pointer doesn't mean it's safe to resume the coro. so the suspend block doesn't really seem to buy you anything safety wise.

echo pier
#

xresumes try to CAS it from suspend->active or from active->notified (new state). When a suspend ends, it either transitions from active->suspended, or notified->active and resumes itself again (to account for any racing xresumes)

mighty portal
#

is the new state needed for functionality? seems like we could just make the invariant that xresume only works if status is Start/Suspended. on resume, atomic swap to Active. on suspend, atomic swap to Suspended. on completion, atomic swap to Done. error otherwise.

#

and if you want to wait, you can wait

echo pier
#

suspend block grants you MT-safe resumes without synchronization
lock grants you same but with synchronization and potential thread blocking
atomic u8 grants you same with synchronization but non-blocking

mighty portal
#

hmm...so is it illegal/compile error to call @frame outside of a suspend block? also even with that, there's no guarantees on the frame pointer lifetime. i.e. you can stash that somewhere and then at runtime illegally call resume on a frame that can't be resumed. what did 0.10 do in that case? runtime error?

echo pier
#

you can call frame outside suspend block i.e. single threaded event loops or even to implement frame-local (like threadlocal) state. The lifetime issue also occurs without a suspend block (e.g. xresume(xframe())). In 0.10, its designated as UB to do that, but similar to undefined being 0xaaaaa, it added some atomic checks to print out an error to try and help debugging

mighty portal
#

ok, i think i'm seeing. the suspend block allows for the communication to another thread to be guaranteed to be after suspend. that way you don't need any additional work to check if the frame is resumable.

echo pier
#

yea, guaranteed to be resumable in the block. I think it can be implemented with like an extra function pointer on a stackful coroutine thats called after xsuspend() switches stacks. Or tail called in the stack switching logic itself

#

tbh, you could just say dont use lib in multi-threaded setting. Could reserve threads for thread pools which are stackless coroutines or straight data parallelism, and stackful concurrency for single thread event loops

mighty portal
#

yeah, for now the lib is definitely only usable single-threaded, which is fine bc libxev is single-threaded (i.e. all completion callbacks run on the loop thread)

#

but i'd like to make things usable multi-threaded; at first, without coro migration, and then later with

#

let me see what i can do with suspend blocks...seems like i could smuggle a couple pointers to the resumer (function pointer, data pointer) and then call the fn if present. so the block would run in the resumer's coroutine.

#

will report back soon.

mint cape
#

wow "all vector registers are call-clobbered" is a great decision for those of us who want to jump between stacks

mighty portal
#

(am i misreading your snark? lmk if there's functionality that you'd like to see)

#

@mint cape

#

i believe for x86 microsoft it's the same

mighty portal
#

windows requires the callee to save the vec registers, and zigcoro does that

mighty portal
mighty portal
#

v0.5.0 adds Channels to allow for communicating between coroutines.

mint cape
austere condor
#

is the CoroT stuff meant to be public? its documented and the functions are public but not the struct itself