https://github.com/bugaevc/c_yield
A simple stackful coroutines implementation in C with a few demos
https://github.com/bugaevc/c_yield
Last synced: about 1 year ago
JSON representation
A simple stackful coroutines implementation in C with a few demos
- Host: GitHub
- URL: https://github.com/bugaevc/c_yield
- Owner: bugaevc
- Created: 2017-06-17T22:54:10.000Z (almost 9 years ago)
- Default Branch: master
- Last Pushed: 2017-06-17T22:55:19.000Z (almost 9 years ago)
- Last Synced: 2025-01-25T21:11:57.445Z (about 1 year ago)
- Language: C
- Size: 3.91 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Writing generators
The first thing the generator does should be calling `gen_init()`, storing the
returned value (generator handle).
Return type of the function should be declared as `gen_t *`. To actually return,
do `gen_return(handle, val); return handle;`. Never exit the generator without
calling `gen_return()`, even if there's no important value to pass.
To yield, call `gen_yield(handle, val)`. The call returns the value sent via
`gen_send()`, so make sure to save it if it's important.
# Calling generators
Call the generator as a usual function. It won't do anything immediately, but
return the handle you will use to control it.
Interact with the generator by calling `gen_send(handle, val)`. The value you
send will be returned inside the generator from `gen_yield()`, whereas the value
that the generator yields next time will be returned from the `gen_send()` call.
Note: the first call of `gen_send()` has no corresponding `gen_yield()` call to
return value from, so the value you pass will be silently discarded.
When the generator returns, the return value is passed as the result of
`gen_send()` in the same way that yielded values are. Use `gen_is_done()` to
distinguish between yielded and return values. After the generator returns, call
`free()` on the handle.
# Notes
It should work fine to pass handles around and call `gen_*()` on them from
somewhat unexpected places (ex. yield the value from a generator from inside
some other generator that was called by the first one). However, generator
invocations are not reentrant, that is, you should not call `gen_yield()` on a
handle twice without a `gen_send()` call on the same handle in between, and vice
versa.
All `gen_*()` functions are thread-safe.
As `gen_return()` function never returns, putting `return handle;` after it is
merely a convention, doubled as a way to silence the compiler warning about not
returning a `gen_t *` value.
As you could see by reading the source code, `gen_send()` and `gen_yield()` are
actually the same function. What it does is it dumps the state, switches to the
other stack, loads the state saved there and returns the value it got passed in
the first place.
We tried hard to make generators debug-friendly. In particular, we display the
original generator call in the call stack, rather than the place `gen_send()`
was called (especially useful in coroutines running on an event loop).