Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
N

neco

> 编程语言
Open source

Concurrency library for C (coroutines)

1.4K stars0 likes1 views
WebsiteGitHub

About

Concurrency library for C (coroutines)

Neco is a C library that provides concurrency using coroutines. It's small & fast, and intended to make concurrent I/O & network programming easy.

Features

  • Coroutines: starting, sleeping, suspending, resuming, yielding, and joining.
  • Synchronization: channels, generators, mutexes, condition variables, and waitgroups.
  • Support for deadlines and cancelation.
  • Posix friendly interface using file descriptors.
  • Additional APIs for networking, signals, random data, streams, and buffered I/O.
  • Lightweight runtime with a fair and deterministic scheduler.
  • Fast user-space context switching. Uses assembly in most cases.
  • Stackful coroutines that are nestable, with their life times fully managed by the scheduler.
  • Cross-platform. Linux, Mac, FreeBSD. (Also WebAssembly and Windows with some limitations).
  • Single file amalgamation. No dependencies.
  • Test suite with 100% coverage using sanitizers and Valgrind.

For a deeper dive, check out the API reference.

It may also be worthwhile to see the Bluebox project for a more complete example of using Neco, including benchmarks.

Goals

  • Give C programs fast single-threaded concurrency.
  • To use a concurrency model that resembles the simplicity of pthreads or Go.
  • Provide an API for concurrent networking and I/O.
  • Make it easy to interop with existing Posix functions.

It's a non-goal for Neco to provide a scalable multithreaded runtime, where the coroutine scheduler is shared among multiple cpu cores. Or to use other concurrency models like async/await.

Using

Just drop the "neco.c" and "neco.h" files into your project. Most modern C compilers should work.

bash
cc -c neco.c

Example 1 (Start a coroutine)

A coroutine is started with the neco_start() function.

When neco_start() is called for the first time it will initialize a Neco runtime and scheduler for the current thread, and then blocks until the coroutine and all child coroutines have terminated.

c
#include <stdio.h>
#include "neco.h"

void coroutine(int argc, void *argv[]) {
    printf("main coroutine started\n");
}

int main(int argc, char *argv[]) {
    neco_start(coroutine, 0);
    return 0;
}

Example 2 (Use neco_main instead of main)

Optionally, neco_main() can be used in place of the standard main(). This is for when the entirety of your program is intended to be run from only coroutines. It adjusts the behavior of the program slightly to make development and error checking easier.

c
#include <stdio.h>
#include "neco.h"

int neco_main(int argc, char *argv[]) {
    printf("main coroutine started\n");
    return 0;
}

Example 3 (Multiple coroutines)

Here we'll start two coroutines that continuously prints "tick" every one second and "tock" every two.

c
#include <stdio.h>
#include "neco.h"

void ticker(int argc, void *argv[]) {
    while (1) {
        neco_sleep(NECO_SECOND);
        printf("tick\n");
    }
}

void tocker(int argc, void *argv[]) {
    while (1) {
        neco_sleep(NECO_SECOND*2);
        printf("tock\n");
    }
}

int neco_main(int argc, char *argv[]) {
    neco_start(ticker, 0);
    neco_start(tocker, 0);
    
    // Keep the program alive for an hour.
    neco_sleep(NECO_HOUR);
    return 0;
}

Example 4 (Coroutine arguments)

A coroutine is like its own little program that accepts any number of arguments.

c
void coroutine(int argc, void *argv[])

The arguments are a series of pointers passed to the coroutine. All arguments are guaranteed to be in scope when the coroutine starts and until the first neco_ function is called. This allows you an opportunity to validate and/or copy them.

…

Example 5 (Channels)

A channel is a mechanism for communicating between two or more coroutines.

Here we'll create a second coroutine that sends the message 'ping' to the first coroutine.

…

Example 6 (Generators)

A generator is like channel but is stricly bound to a coroutine and is intended to treat the coroutine like an iterator.

…

Example 7 (Connect to server)

Neco provides neco_dial() for easily connecting to server.

Here we'll performing a (very simple) HTTP request which prints the homepage of the http://example.com website.

…

Example 8 (Create a server)

Use neco_serve() to quickly bind and listen on an address.

Here we'll run a tiny webserver at http://127.0.0.1:8080

…

Example 9 (Echo server and client)

Run server with:

bash
cc neco.c echo-server.c && ./a.out

Run client with:

bash
cc neco.c echo-client.c && ./a.out

echo-server.c

…

echo-client.c

…

Example 10 (Suspending and resuming a coroutine)

Any coroutines can suspended itself indefinetly and then be resumed by other coroutines by using neco_suspend() and neco_resume().

…

More examples

You can find more examples here.

Platform notes

Linux, Mac, and FreeBSD supports all features.

Windows and WebAssembly support the core coroutine features, but have some key limitiations, mostly with working with file descriptors and networking. This is primarly because the Neco event queue works with epoll and kqueue, which are only available on Linux and Mac/BSD respectively. This means that the neco_wait() (which allows for a coroutine to wait for a file descriptor to be readable or writeable) is not currently available on those platforms.

Other limitations include:

  • Windows only supports amd64.
  • Windows and WebAssembly use smaller default stacks of 1MB.
  • Windows and WebAssembly do not support guards or gaps.
  • Windows and WebAssembly do not support NECO_CSPRNG (Cryptographically secure pseudorandom number generator)
  • Windows does not support stack unwinding.

Other than that, Neco works great on those platforms.

Any contributions towards making Windows and WebAssembly feature complete are welcome.

The scheduler

Neco uses sco, which is a fair and deterministic scheduler. This means that no coroutine takes priority over another and that all concurrent operations will reproduce in an expected order.

Fast context switching

The coroutine context switching is powered by llco and uses assembly code in most cases. On my lab machine (AMD Ryzen 9 5950X) a context switch takes about 11 nanoseconds.

Thread local runtime

There can be no more than one scheduler per thread.

When the first coroutine is started using neco_start(), a new Neco runtime is initialized in the current thread, and each runtime has its own scheduler.

Communicating between coroutines that are running in different threads will require I/O mechanisms that do not block the current schedulers, such as pipe(), eventfd() or atomics.

Pthread utilties such as pthread_mutex_t and pthread_cond_t do not work very well in coroutines.

For example, here we'll create two threads, running their own Neco schedulers. Each using pipes to communicate with the other.

…

License

Source code is available under the MIT License.

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

C

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言