The io_context class is the heart of Corosio. It’s an event loop that
processes asynchronous I/O operations, manages timers, and coordinates
coroutine execution.
|
Note
|
Code snippets assume: #include <boost/corosio/io_context.hpp>
namespace corosio = boost::corosio; |
Every Corosio program needs at least one io_context:
corosio::io_context ioc;
// ... create I/O objects and launch coroutines ...
ioc.run(); // Process events until all work completesThe io_context:
-
Owns the platform-specific I/O backend (IOCP on Windows)
-
Maintains a queue of pending work items
-
Provides an executor for coroutine dispatch
-
Tracks outstanding work to know when to stop
corosio::io_context ioc;Creates an io_context with a concurrency hint equal to
std::thread::hardware_concurrency(). If more than one thread is available,
thread-safe synchronization is enabled.
corosio::io_context ioc(1); // Single-threaded, no synchronization
corosio::io_context ioc(4); // Up to 4 threads, thread-safeThe concurrency hint affects:
-
Internal synchronization strategy
-
IOCP thread pool size on Windows
Use 1 for single-threaded programs to avoid synchronization overhead.
Processes all pending work until stopped:
std::size_t n = ioc.run();
std::cout << "Processed " << n << " handlers\n";This function:
-
Blocks until all work completes or
stop()is called -
Returns the number of handlers executed
-
Automatically stops when no outstanding work remains
Processes at most one work item:
std::size_t n = ioc.run_one(); // Returns 0 or 1Useful for manual event loop control or interleaving with other work.
Process work for a limited time:
using namespace std::chrono_literals;
auto n = ioc.run_for(100ms); // Run for 100 milliseconds
auto m = ioc.run_until(deadline); // Run until time pointThese return the number of handlers executed within the time limit.
Signal the event loop to stop:
ioc.stop();This causes run() to return as soon as possible. Pending work remains
queued but won’t be processed.
Check if the context has been stopped:
if (ioc.stopped())
std::cout << "Event loop stopped\n";The io_context::executor_type provides the interface for dispatching work:
auto ex = ioc.get_executor();
// Launch a coroutine
capy::run_async(ex)(my_coroutine());
// Access the context
corosio::io_context& ctx = ex.context();
// Check if running inside the event loop
if (ex.running_in_this_thread())
std::cout << "Inside run()\n";auto ex = ioc.get_executor();
// Dispatch: symmetric transfer if inside run(), otherwise post
ex(handle);
// Post: always queue for later execution
ex.post(handle);
// Defer: same as post (conveys continuation intent)
ex.defer(handle);The dispatch operation ex(handle) enables symmetric transfer when already
running inside run(). This is how child coroutines resume parents
efficiently.
int main()
{
corosio::io_context ioc;
// Create I/O objects
corosio::tcp_socket sock(ioc);
corosio::timer timer(ioc);
// Launch initial coroutine
capy::run_async(ioc.get_executor())(main_coroutine(sock, timer));
// Run until all work completes
ioc.run();
}The io_context can be used from multiple threads when constructed with
a concurrency hint greater than 1:
corosio::io_context ioc(4);
std::vector<std::thread> threads;
for (int i = 0; i < 4; ++i)
threads.emplace_back([&ioc] { ioc.run(); });
for (auto& t : threads)
t.join();Multiple threads can call run() concurrently. The io_context distributes
work across threads.
|
Warning
|
Individual I/O objects (sockets, timers) are not thread-safe. Don’t access the same socket from multiple threads without synchronization. |
io_context inherits from capy::execution_context, providing service
management:
// Create or get a service
my_service& svc = ioc.use_service<my_service>();
// Check if service exists
if (ioc.has_service<my_service>())
// ...Services are destroyed when the io_context is destroyed.
On Windows, the io_context uses I/O Completion Ports:
-
Scalable to thousands of concurrent connections
-
Efficient thread pool utilization
-
Native async I/O with zero-copy potential
On Linux, the io_context uses epoll:
-
Scalable to large numbers of file descriptors
-
Edge-triggered notifications
-
Efficient for long-lived connections