I/O Context
The io_context class provides the execution environment for asynchronous
I/O operations. It maintains a queue of pending work and processes completions
from the underlying platform reactor (IOCP on Windows).
| Code snippets assume the following declarations are in effect: |
#include <boost/corosio.hpp>
namespace corosio = boost::corosio;
Creating an I/O Context
The default constructor creates an io_context with a concurrency hint equal to the number of hardware threads:
corosio::io_context ioc;
For single-threaded use, pass a concurrency hint of 1 to avoid synchronization overhead:
corosio::io_context ioc(1); // Single-threaded, no locking
Running the Event Loop
The run() function processes I/O completions and executes ready coroutines
until no work remains:
ioc.run(); // Blocks until all work completes
For finer control, use the variations:
| Function | Behavior |
|---|---|
|
Process all work until stopped or exhausted |
|
Process at most one completion |
|
Process work for the specified duration |
|
Process work until the specified time |
|
Process ready work without blocking |
|
Process at most one ready item without blocking |
Stopping and Restarting
To interrupt run() from another thread or coroutine:
ioc.stop();
After run() returns (either from stop() or work exhaustion), you must
restart before calling run() again:
if (ioc.stopped())
ioc.restart();
ioc.run();
The Executor
The io_context provides an executor via get_executor(). The executor is
a lightweight handle that can be copied and passed around:
auto ex = ioc.get_executor();
The executor satisfies the capy::executor concept and provides the dispatcher
interface for coroutine resumption.
Thread Safety
The io_context uses internal synchronization when the concurrency hint is
greater than 1. Multiple threads can safely call run() concurrently:
corosio::io_context ioc; // Default concurrency hint
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i)
threads.emplace_back([&]{ ioc.run(); });
for (auto& t : threads)
t.join();
For single-threaded applications, use a concurrency hint of 1 to eliminate synchronization overhead.
Work Tracking
The io_context tracks outstanding work through two mechanisms:
-
Pending I/O operations — Each socket operation in flight counts as work
-
Manual work guards — Call
on_work_started()/on_work_finished()on the executor
When the work count reaches zero, run() returns.
Example: Timed Operations
Use run_for() to implement timeouts:
capy::task<void> timed_operation(corosio::io_context& ioc)
{
corosio::socket s(ioc);
s.open();
// Start connect
auto connect_task = s.connect(
corosio::endpoint(addr, 8080));
// Run for at most 5 seconds
auto n = ioc.run_for(std::chrono::seconds(5));
if (ioc.stopped())
{
s.cancel(); // Cancel the pending connect
std::cerr << "Connection timed out\n";
}
}
Next Steps
-
Sockets — Socket operations in detail
-
Affine Awaitables — How dispatching works