https://github.com/lbphacker/coro
A very simple coroutine library for C based on getcontext
https://github.com/lbphacker/coro
Last synced: 11 months ago
JSON representation
A very simple coroutine library for C based on getcontext
- Host: GitHub
- URL: https://github.com/lbphacker/coro
- Owner: LBPHacker
- License: mit
- Created: 2021-03-24T16:42:26.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2021-08-27T15:07:52.000Z (almost 5 years ago)
- Last Synced: 2025-04-14T09:21:22.002Z (about 1 year ago)
- Language: C
- Homepage:
- Size: 29.3 KB
- Stars: 5
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.md
Awesome Lists containing this project
README
# Coro
Or: _as if we don't have enough things to worry about_
## What (is all this)?
A very simple coroutine library for C that is semantically similar to
[Lua's](https://www.lua.org/manual/5.1/manual.html#2.11), except there's no
GC involved so you have to free coroutines yourself, which you can only do
if they're dead (have terminated). Currently heavily depends on the
[getcontext](https://linux.die.net/man/3/getcontext) family of
functions, which seem to have been phased out of POSIX, very sad.
This library is not thread-safe, which, seeing as it's a coroutine library,
shouldn't come as a surprise if you're considering using it. Regardless, you can
configure (see below) the library to make its global state `_Thread_local`
rather than `static` if you so wish, and if your compiler supports it, to get
safety guarantees for at least the case in which your threads talk to each other
very little and never try to use the same coroutines.
## Why (would you do this)?
I just really wanted to get the itch to write some sort of coroutine library
for C out of my system.
## When (should I use this)?
Never, unless you're sure that the rest of your program can handle the sort
of context transfers [setcontext](https://linux.die.net/man/3/getcontext) does.
Especially not when using C++; this library will eat your exceptions for
breakfast. There might be platform-dependent ways to solve this problem, but I
haven't yet looked into these; PRs are welcome.
Also don't use this if you can't guarantee that you won't overflow the stacks
allocated for your coroutines; see the code below. ~If you can't guarantee
this but still would like to use the library, consider building (see way below)
with `-Duse_mmap_stack=true` to have the library ask Linux for stacks that
immediately and reliably crash your program with a `SIGSEGV` when overflowed,
rather than break your allocator and cause a double free two weeks later.~
[`MAP_GROWSDOWN` seems to be useless](https://stackoverflow.com/questions/56888725/why-is-map-growsdown-mapping-does-not-grow); requires further investigation.
## Who (should I blame if my program dies)?
Yourself, see the [license](LICENSE.md).
## How (would I use this)?
The same way you'd do it in Lua. See [coro.h](include/coro.h) and
[03.c](test/03.c). But [tl;dr](deptest/deptest.c):
```c
#include
#include
#include "coro.h"
struct context
{
int input;
};
static void *cofunc(void *data)
{
struct context *pctx = (struct context *)data;
pctx->input *= pctx->input;
printf("hello from cofunc\n");
// * There first parameter of coro_yield is similar to the second parameter
// of coro_resume, see below.
assert(!coro_yield((void **)&pctx));
pctx->input += 42;
return data;
}
static int func(int input)
{
struct context ctx;
struct context *pctx = &ctx;
pctx->input = input;
struct coro *co;
// * All functions barring coro_status and coro_getudata return
// CORO_OK = 0 on success. See coro.h for other possible return values.
// * Tell the library to allocate 0x1000 bytes to be used as stack
// by the new coroutine.
assert(!coro_create(&co, cofunc, 0x1000U));
printf("input is now %i\n", pctx->input);
// * Pass a pointer to a pointer-sized area as the second parameter.
// This is necessary because not only do we want to send data to the
// coroutine, we also want to receive data from it.
assert(!coro_resume(co, (void **)&pctx));
printf("input is now %i\n", pctx->input);
assert(!coro_resume(co, (void **)&pctx));
printf("input is now %i\n", pctx->input);
assert(!coro_free(co));
return pctx->input;
}
int main(int argc, char *argv[])
{
printf("result is %i\n", func(argc));
return 0;
}
```
Build and install with [Meson](https://mesonbuild.com/) and
[Ninja](https://ninja-build.org/) like so:
```sh
cd coro
meson build
cd build
ninja test
sudo ninja install
```
If you wish to make the global state thread-local, configure the build site
with `use_thread_local` set to `true`:
```sh
cd coro
meson -Duse_thread_local=true build
cd build
ninja test
sudo ninja install
```
The Meson script exports the dependency `coro_dep`. Installing also creates a
`coro.pc` [pkg-config](https://linux.die.net/man/1/pkg-config) file, which lets
you link against this dependency with other build systems, and with
`dependency('coro')` in Meson.
You can uninstall the library with:
```sh
sudo ninja uninstall
```
Build options:
| name | type | default | description |
|-|-|-|-|
| `use_thread_local` | boolean | `true` | Make the global coro state `_Thread_local` rather than `static` |
| `use_mmap_stack` | boolean | ~`true`~ `false` | ~Allocate coro stacks with `mmap` and `MAP_GROWSDOWN` (Linux) rather than `malloc`~ `MAP_GROWSDOWN` seems to be useless, see above |