How to Use the C++ go_fiber API with Lambda Expressions in libfiber
The go_fiber API enables launching C++ fibers using intuitive lambda syntax via operator overloading on temporary go_fiber objects created by macros like go, go_wait_fiber, and go_wait_thread.
The iqiyi/libfiber library provides a high-performance coroutine implementation for C++, and its go_fiber wrapper allows you to use modern C++ lambda expressions to spawn and manage fibers. This API abstracts the underlying C-style fiber implementation into a type-safe, ergonomic interface that feels like a native language feature. Understanding how to use the C++ go_fiber API with lambda expressions unlocks efficient concurrent programming without callback complexity.
Architecture of the go_fiber Wrapper
Core Components
The implementation centers on five key components defined in cpp/include/fiber/go_fiber.hpp and related headers:
- go_fiber class: Defined at lines 43‑78, this class holds optional stack configuration and implements three critical operators that accept lambdas.
- fiber_ctx: A context struct at lines 24‑33 that stores the user lambda as
std::function<void()>until the fiber begins execution. - fiber class: The underlying coroutine engine in
cpp/include/fiber/fiber.hpp(lines 32‑60) that handles creation viafiber::fiber_create. - fiber_tbox: A synchronization primitive from
cpp/include/fiber/fiber_tbox.hpp(lines 40‑66) that enables blocking semantics for the wait operators. - Convenience Macros: The
go,go_wait_fiber, andgo_wait_threadmacros at lines 35‑42 instantiate temporarygo_fiberobjects and apply the appropriate operator.
The Three Operator Overloads
The go_fiber class overloads three operators to control execution semantics:
- operator>: Creates a new fiber and immediately returns a
std::shared_ptr<fiber>. The implementation at lines 48‑52 allocates afiber_ctx, invokesfiber::fiber_createwithfiber_mainas the entry point, and wraps the result. - operator<: Runs a lambda inside a new fiber and blocks the current fiber until completion. This uses a
fiber_tbox<int>as a one-element barrier (lines 54‑62). - operator<<: Executes the lambda in a detached OS thread while blocking the current fiber until the thread finishes, also utilizing
fiber_tbox<int>for synchronization (lines 64‑73).
Launching Fibers with Lambda Expressions
Fire-and-Forget with operator>
Use the go macro to spawn independent fibers that run concurrently without blocking the caller.
#include "fiber/go_fiber.hpp"
#include "fiber/fiber.hpp"
void hello() {
printf("Hello from fiber %u\n", acl::fiber::self());
}
int main() {
// Launch a fiber that runs hello(). No waiting occurs.
go[&] { hello(); };
// Start the scheduler
acl::fiber::schedule();
return 0;
}
This pattern from samples/cxx/fiber/main.cpp demonstrates the basic launch mechanism. The go macro expands to a temporary go_fiber instance followed by operator>, which stores your lambda in a fiber_ctx and creates the underlying fiber via fiber::fiber_create.
Blocking Fiber-to-Fiber Waits with operator<
When you need to spawn a fiber and suspend the current one until the child completes, use go_wait_fiber.
#include "fiber/go_fiber.hpp"
#include "fiber/fiber.hpp"
void task(int id) {
printf("Fiber %d (id=%u) running\n", id, acl::fiber::self());
}
void master_fiber() {
// Blocks until the lambda finishes
go_wait_fiber[&] { task(42); };
printf("Fiber %u resumed after task\n", acl::fiber::self());
}
int main() {
go[&] { master_fiber(); };
acl::fiber::init(acl::FIBER_EVENT_T_KERNEL, true);
return 0;
}
The go_wait_fiber macro utilizes operator< (lines 54‑62 in go_fiber.hpp), which creates a fiber_tbox<int> barrier. The current fiber suspends on pop() until the child fiber pushes a completion signal.
Blocking Fiber-to-Thread Waits with operator<<
For offloading blocking operations to OS threads without stalling the fiber scheduler, use go_wait_thread or its alias go_wait.
#include "fiber/go_fiber.hpp"
#include "fiber/fiber.hpp"
void heavy_work() {
std::this_thread::sleep_for(std::chrono::seconds(2));
printf("Thread work done\n");
}
int main() {
go[&] {
go_wait_thread[&] { heavy_work(); };
printf("Back in fiber after thread\n");
};
acl::fiber::init(acl::FIBER_EVENT_T_KERNEL, true);
return 0;
}
The operator<< implementation (lines 64‑73) spawns a detached std::thread, executes the lambda, and signals completion through a fiber_tbox to resume the waiting fiber.
Customizing Stack Behavior
Private Stack Sizes with go_stack
Control memory allocation for deep recursion or large frame sizes using go_stack(size), which expands to go_fiber(size, false) as defined at lines 36‑37.
#include "fiber/go_fiber.hpp"
#include "fiber/fiber.hpp"
void deep_recursion(int depth) {
if (depth == 0) return;
deep_recursion(depth - 1);
}
int main() {
// Allocate a 1 MiB private stack
go_stack(1024 * 1024)[&] { deep_recursion(5000); };
acl::fiber::schedule();
return 0;
}
Shared-Stack Mode with go_share
Reduce per-fiber memory footprint when spawning thousands of concurrent fibers by enabling shared-stack mode via go_share(size), equivalent to go_fiber(size, true).
// Allocate a 1 MiB shared stack
go_share(1024 * 1024)[&] { deep_recursion(5000); };
This mode allows multiple fibers to time-share a single stack allocation, significantly reducing memory pressure in high-concurrency scenarios while maintaining the same lambda execution semantics.
Summary
- The go_fiber API wraps libfiber's C-style implementation with type-safe C++ lambda support through
cpp/include/fiber/go_fiber.hpp. - Three operators control execution semantics:
>for fire-and-forget,<for fiber-blocking waits, and<<for thread-blocking waits. - Capture semantics work naturally with
[&],[=], or[this], as the lambda is stored asstd::function<void()>infiber_ctx. - Synchronization relies on
fiber_tboxto implement blocking behavior without busy-waiting. - Stack customization macros
go_stackandgo_shareprovide fine-grained control over memory allocation strategies.
Frequently Asked Questions
Can I capture local variables by reference in go_fiber lambdas?
Yes. The operators accept std::function<void()> parameters, so any valid C++ capture mode works as expected. The lambda executes within the scope where it was defined, maintaining references to captured variables according to standard C++ semantics.
What is the difference between go_wait_fiber and go_wait_thread?
go_wait_fiber (using operator<) creates a new fiber within the scheduler to run your lambda and blocks the current fiber until completion. go_wait_thread (using operator<<) launches a separate OS thread to execute the lambda, allowing blocking system calls without stalling the fiber scheduler, then resumes the fiber when the thread exits.
How do I retrieve the return value from a lambda executed via go_fiber?
The current API uses void() signatures, so direct return value capture is not supported. You must use capture-by-reference to modify variables in the parent scope, or implement a custom synchronization mechanism using fiber_tbox to pass results back to the waiting fiber.
Where can I find production examples of go_fiber usage?
The repository provides working samples in samples/cxx/fiber/main.cpp, which demonstrates basic launching, waiting, and stack options. Additional examples covering thread pools and high-throughput scenarios appear in samples/cxx/fiber_pool/main.cpp.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →