Skip to main content

Boost.Fiber

Boost.Fiber provides user-space cooperative threads (fibers). A fiber looks like a thread — it has its own stack and can block on mutexes, condition variables, and channels — but it is scheduled cooperatively within a single OS thread, not preemptively by the kernel. This makes fibers extremely lightweight: creating thousands of fibers is practical where creating thousands of OS threads is not.

The problem it solves

OS threads are expensive — each one consumes kernel resources and a megabyte or more of stack. When you have thousands of concurrent tasks that spend most of their time waiting (I/O, timers, channel reads), fibers give you the concurrency model of goroutines or Erlang processes in plain C++.

Launching fibers

A fiber is created like a thread — pass it a callable, and it begins executing. Fibers run on the thread that created them (or the thread running the fiber scheduler) until they yield, block, or complete.

fiber_basics.cpp
#include <boost/fiber/all.hpp>
#include <iostream>

void task(int id) {
std::cout << "fiber " << id << " start\n";
boost::this_fiber::yield(); // cooperatively yield to another fiber
std::cout << "fiber " << id << " resume\n";
}

int main() {
boost::fibers::fiber f1(task, 1);
boost::fibers::fiber f2(task, 2);
f1.join();
f2.join();
}

Fiber synchronization

Fibers have their own versions of mutex, condition_variable, and barrier — these yield the fiber instead of blocking the OS thread.

fiber_mutex.cpp
#include <boost/fiber/all.hpp>

boost::fibers::mutex mtx;
int shared = 0;

void increment(int n) {
for (int i = 0; i < n; ++i) {
std::lock_guard<boost::fibers::mutex> lock(mtx);
++shared;
}
}

int main() {
boost::fibers::fiber f1(increment, 1000);
boost::fibers::fiber f2(increment, 1000);
f1.join();
f2.join();
// shared == 2000
}
Do not mix fiber and thread primitives

Using std::mutex or boost::mutex inside a fiber blocks the entire OS thread, stalling all fibers on that thread. Always use boost::fibers::mutex and boost::fibers::condition_variable inside fiber code.

Channels

Fibers can communicate through unbuffered or buffered channels, similar to Go channels.

fiber_channel.cpp
#include <boost/fiber/all.hpp>
#include <iostream>

int main() {
boost::fibers::buffered_channel<int> ch(4);

boost::fibers::fiber producer([&] {
for (int i = 0; i < 5; ++i)
ch.push(i);
ch.close();
});

boost::fibers::fiber consumer([&] {
int val;
while (ch.pop(val) == boost::fibers::channel_op_status::success)
std::cout << val << " ";
std::cout << "\n";
});

producer.join();
consumer.join();
}

Scheduling

The default scheduler is round_robin — fibers run in the order they become ready. Custom schedulers can implement work-stealing across threads or priority-based dispatch.

Fibers versus threads versus coroutines

AspectOS ThreadFiberC++20 Coroutine
Stackkernel-allocated (~1MB)user-allocated (~64KB)stackless (frame only)
Schedulingpreemptive (kernel)cooperative (user)cooperative (user)
Creation costhigh (syscall)low (allocation)very low (frame)
Blocking I/Oblocks one threadyields fibermust use async I/O
Sync primitivesstd::mutex, etc.fiber::mutex, channelsawaitable objects
Scalabilitythousands at mostmillions possiblemillions possible
Integration with Asio

Boost.Fiber ships with an Asio integration scheduler (boost::fibers::asio::round_robin) that lets fibers yield on Asio async operations. This combines the readable, synchronous-looking fiber code with Asio's efficient I/O multiplexing.

See also