-
-
Notifications
You must be signed in to change notification settings - Fork 12
async_system.md
The qb::io::async namespace is the powerhouse behind qb-io's non-blocking capabilities. It provides a complete, event-driven asynchronous programming model built around a high-performance event loop. While it seamlessly integrates with the qb-core actor system, qb::io::async is also designed to be fully usable as a standalone toolkit for any C++17 application requiring efficient asynchronous operations.
At the heart of the asynchronous system is the qb::io::async::listener class (defined in qb/io/async/listener.h). This class is your primary interface to the event loop.
-
Underlying Engine:
listenerwraps thelibevC library, known for its high performance and portability across POSIX systems (epoll, kqueue) and Windows (IOCP, select). -
Thread-Local Instance (
listener::current): Crucially,qb-ioprovides a thread-local static instance of thelistenernamedqb::io::async::listener::current. This means each thread that intends to perform asynchronous operations withqb-iohas its own dedicated event loop manager. You don't need to pass listener objects around; you simply accesslistener::currentfrom the thread where you want to register or manage events. -
Initialization (Standalone Usage): If you're using
qb-iowithoutqb-core, you must initialize the listener for each participating thread by callingqb::io::async::init();once per thread before using otherqb::io::asyncfeatures. (When usingqb-core,qb::Mainhandles this for itsVirtualCorethreads).
-
Monitoring Event Sources: The
listenercontinuously monitors various sources of events:-
I/O Events (
qb::io::async::event::io): Readiness of file descriptors (sockets, pipes, files) for reading or writing. -
Timer Events (
qb::io::async::event::timer): Expiration of scheduled timers for delayed or periodic actions. -
Signal Events (
qb::io::async::event::signal): Asynchronous notification of operating system signals (e.g., SIGINT, SIGTERM). -
File System Stat Events (
qb::io::async::event::file): Notifications about changes to file attributes (used byfile_watcheranddirectory_watcher).
-
I/O Events (
-
Event Dispatching: When an event source becomes active (e.g., data arrives on a socket, a timer expires), the
listeneridentifies the corresponding registered handler (an object implementingIRegisteredKernelEvent) and invokes itsinvoke()method. This, in turn, typically calls the user-definedon(SpecificEvent&)method within the I/O component or handler class.
For the listener to process events, its loop must be actively run:
-
qb::io::async::run(int flag = 0): This function executes the event loop forlistener::current. Theflagargument controls its behavior:-
0(default): Runs the loop untilqb::io::async::break_parent()is called or no active event watchers remain. This is a blocking call. -
EVRUN_ONCE: Waits for at least one event to occur, processes the block of available events, and then returns. -
EVRUN_NOWAIT: Checks for any immediately pending events, processes them, and returns without waiting if none are pending.
-
-
qb::io::async::run_once(): Convenience forrun(EVRUN_ONCE). -
qb::io::async::run_until(const bool& status_flag): Runs the loop withEVRUN_NOWAITrepeatedly as long asstatus_flagis true. -
qb::io::async::break_parent(): Signalslistener::currentto stop its currentrun()cycle.
// Standalone qb-io example sketch
#include <qb/io/async.h>
#include <iostream>
#include <atomic>
std::atomic<bool> g_is_running = true;
void my_periodic_task() {
std::cout << "Periodic task executed at: " << qb::UtcTimePoint::now().to_iso8601() << std::endl;
if (g_is_running) {
// Reschedule self
qb::io::async::callback(my_periodic_task, 1.0); // Every 1 second
}
}
int main() {
qb::io::async::init(); // Initialize listener for the main thread
std::cout << "Starting standalone qb-io event loop demo.\n";
std::cout << "Press Ctrl+C to exit (if signal handling is set up, or wait for tasks).\n";
// Schedule an initial periodic task
qb::io::async::callback(my_periodic_task, 1.0);
// Schedule a one-shot task to stop the loop after 5 seconds
qb::io::async::callback([]() {
std::cout << "5 seconds elapsed. Signaling application to stop.\n";
g_is_running = false;
qb::io::async::break_parent(); // Request the run loop to exit
}, 5.0);
// Run the event loop until break_parent() is called or g_is_running is false
// In a real app, this might be: while(g_is_running) { qb::io::async::run(EVRUN_ONCE); /* other logic */ }
qb::io::async::run(); // Runs until break_parent() or no more active watchers
std::cout << "Event loop finished.\n";
return 0;
}The listener::current.registerEvent<_Event, _Actor, _Args...>(actor, args...) method is the core mechanism for associating an event watcher (like qb::io::async::event::io or timer) with a handler object (_Actor) and the event loop.
-
_Event: The qb-io event type (e.g.,qb::io::async::event::timer). This wraps a specificlibevwatcher. -
_Actor: The class instance that will handle the event (must have anon(_Event&)method). -
_Args...: Arguments specific to thelibevwatcher being set up (e.g., file descriptor andEV_READ/EV_WRITEflags for anevent::iowatcher, or timeout values for anevent::timer).
Most qb-io components designed for asynchronous operations (e.g., those inheriting from qb::io::async::io, qb::io::async::file_watcher, or qb::io::async::with_timeout) handle this registration internally when they are constructed or started.
(qb/io/async/io.h)
This utility function provides a straightforward way to schedule a callable (lambda, function pointer, functor) for execution by the current thread's event loop, optionally after a delay.
- Execution: The callback runs on the same thread that scheduled it.
-
Lifetime: The internal timer object (
qb::io::async::Timeout) created bycallbackis self-managing; it registers with the listener and deletes itself after the callback is invoked. -
Usage:
qb::io::async::callback(my_function, delay_seconds);orqb::io::async::callback([]{ /* lambda body */ }); - Use Cases: Delaying operations, breaking work into smaller non-blocking chunks, scheduling retries, and simple periodic tasks (by re-scheduling from within the callback).
(See practical examples in: Core Concepts: Asynchronous I/O Model in QB for actor-centric usage, and example1_async_io.cpp for general usage.)
(qb/io/async/io.h)
This CRTP base class allows you to easily add timeout functionality to your own classes.
-
Inheritance:
class MyClass : public qb::io::async::with_timeout<MyClass> { ... }; -
Handler Method: Implement
void on(qb::io::async::event::timer const& event)inMyClassto define what happens when the timeout occurs. -
Control:
- The constructor
with_timeout(timeout_in_seconds)initializes and starts the timer. - Call
updateTimeout()on any activity that should reset the timeout countdown. - Use
setTimeout(new_duration_seconds)to change the timeout period or disable it (by passing0.0).
- The constructor
This mechanism is ideal for implementing inactivity timeouts in network sessions, operation timeouts, or any scenario where an action needs to be taken if something doesn't happen within a specific timeframe.
(A detailed example is available in: Core Concepts: Asynchronous I/O Model in QB and test-async-io.cpp::TimerHandler)
(qb/io/async/event/all.h)
qb-io defines a suite of event structures used to notify components about various asynchronous occurrences. When building custom I/O components (often by inheriting from qb::io::async::input, qb::io::async::output, or qb::io::async::io), you will typically override on(SpecificEvent&) methods to handle these:
-
disconnected: A network connection was closed or an error occurred. -
eof: End-of-file reached on an input stream; no more data to read. -
eos: End-of-stream reached on an output stream; all buffered data has been sent. -
file: Attributes of a monitored file or directory have changed (used byfile_watcher). -
io: Raw low-level I/O readiness on a file descriptor (e.g., socket is readable/writable). -
pending_read/pending_write: Data remains in input/output buffers after a partial operation. -
signal: An OS signal was caught. -
timer/timeout: A previously set timer or timeout has expired.
These events form the backbone of communication between the listener and the I/O handling components, enabling a fully event-driven architecture.
(Next: QB-IO: Transports to see how these async mechanisms are applied to TCP, UDP, etc.**)