libmeng is a lightweight, ultra-fast, cross-platform coroutine / fiber library written in C and modern C++. Designed for high-performance concurrent systems, it provides ultra-low latency context switching (~25 ns) and supports tens of millions of concurrent coroutines.
- Blazing Fast Context Switching: Handcrafted lightweight assembly switching achieves latency down to ~25 ns per context switch and a throughput of ~40 million switches/sec.
- Cross-Platform & Multi-Architecture:
- Linux: x86_64, aarch64 / ARM64 (GCC, Clang)
- Windows: x86_64, x86 (MinGW-w64)
- macOS: Apple Silicon (ARM64), Intel (x86_64) (Apple Clang)
- Dual API Design:
- Pure C API (
meng.h): Minimal, zero dependencies, ABI stable, and 100% backward compatible. - Modern C++ API (
meng.hpp): C++11/14/17/20 ready, featuring RAII resource management, lambda closures with variable captures, exception propagation across coroutines, and move semantics.
- Pure C API (
- Nested Coroutine Support: Coroutines can create and schedule sub-coroutines seamlessly.
- Thread-Safe: Uses
thread_localstate isolation, allowing concurrent coroutine execution across multiple worker threads. - Strict ABI Compliance: Enforces 16-byte stack alignment, ensuring safe execution of vectorized SSE/AVX instructions and modern runtime libraries.
- Stack Overflow Canary: Includes built-in magic canary detection for stack boundary violation prevention.
- Modern CMake Integration: First-class support for CMake 3.15+,
FetchContent,find_package(meng), and CTest automated testing.
Measured on a Linux x86_64 machine with 10,000,000 ping-pong context switches:
| Metric | Measurement |
|---|---|
| Switch Latency | ~25.08 ns / switch |
| Throughput | ~39.87 M switches / second |
| Memory Footprint | Extremely compact control block, configurable stack size (default 64 KB) |
Use modern C++ lambdas, captures, and RAII:
#include "meng.hpp"
#include <iostream>
int main() {
int counter = 0;
// Create a coroutine with lambda and variable capture
libmeng::coroutine co([&counter]() {
for (int i = 0; i < 3; ++i) {
counter++;
std::cout << "Coroutine step " << i << ", counter = " << counter << std::endl;
libmeng::yield(); // Yield execution back to the caller
}
});
// Schedule and resume until completion
while (co.resume()) {
std::cout << "Back in main thread" << std::endl;
}
std::cout << "Done! Final counter: " << counter << std::endl;
return 0; // RAII automatically frees coroutine resources
}Compatible with standard C codebases:
#include "meng.h"
#include <stdio.h>
void worker(meng * m, void * arg, size_t argsize) {
int id = *(int*)arg;
for (int i = 0; i < 5; i++) {
printf("Coroutine %d step %d\n", id, i);
meng_yield(m); // Or call meng_yield_current()
}
}
int main() {
int id1 = 1, id2 = 2;
meng * m1 = meng_create(worker, 16 * 1024, &id1, sizeof(id1));
meng * m2 = meng_create(worker, 16 * 1024, &id2, sizeof(id2));
while (!meng_end(m1) || !meng_end(m2)) {
if (!meng_end(m1)) meng_run(m1);
if (!meng_end(m2)) meng_run(m2);
}
meng_delete(m1);
meng_delete(m2);
return 0;
}- CMake 3.15 or newer
- GCC 4.8+ / Clang 3.8+ / MSVC 2015+ (C++11 support)
./build.sh # Builds in Debug mode and runs all unit tests
./build.sh release # Builds in Release mode# Configure
cmake -B build -DCMAKE_BUILD_TYPE=Release -DMENG_BUILD_TESTS=ON
# Build
cmake --build build -j
# Run automated tests
ctest --test-dir build --output-on-failure
# Run benchmark
./build/bin/benchmarkinclude(FetchContent)
FetchContent_Declare(
meng
GIT_REPOSITORY https://github.com/esrrhs/libmeng.git
GIT_TAG master
)
FetchContent_MakeAvailable(meng)
target_link_libraries(your_target PRIVATE meng::meng)cmake --build build --target installIn your CMakeLists.txt:
find_package(meng REQUIRED)
target_link_libraries(your_target PRIVATE meng::meng)meng * meng_create(meng_main func, size_t stacksize, const void * arg, size_t argsize): Create a new coroutine with specified entry function, stack size, and user data.void meng_run(meng * m): Run or resume the coroutine until it yields or terminates.void meng_yield(meng * m): Yield CPU execution back to the caller.void meng_yield_current(void): Yield execution from the currently running coroutine without needing the handle.bool meng_end(meng * m): Check if the coroutine has finished execution.void meng_delete(meng * m): Destroy the coroutine and release all allocated memory.meng * meng_running(void): Returns the currently running coroutine in this thread (or NULL if in main thread).void * meng_get_arg(meng * m): Retrieve the pointer to user arguments.size_t meng_get_arg_size(meng * m): Retrieve user argument size in bytes.size_t meng_get_stack_size(meng * m): Retrieve coroutine stack size in bytes.
libmeng::coroutine:coroutine(Callable&& fn, size_t stack_size = 64 * 1024): Constructs a coroutine from any callable (lambda, functor, function).bool resume(): Resumes execution. Propagates uncaught exceptions back to the caller. Returnstrueif suspended and still runnable;falseif finished.void yield(): Yields execution back to the caller.bool done() const: Returnstruewhen finished.- Destructor automatically reclaims all coroutine resources (RAII).
libmeng::yield(): Yield the currently running coroutine from any call stack depth.libmeng::current(): Retrieve the currentlibmeng::coroutine*pointer.
MIT License