How to Use ASIO for Timer‑Based Operations: Complete Guide to Clocks, Waits, and Cancellation
ASIO provides clock‑agnostic timer classes through basic_waitable_timer that support both blocking synchronous waits and asynchronous completion handlers, with automatic cancellation semantics when expiry times are modified.
ASIO timer-based operations form the foundation of time‑driven asynchronous programming in the chriskohlhoff/asio library. Whether you need simple delays or complex scheduling within an I/O execution context, the timer subsystem integrates seamlessly with executors and completion tokens. This guide examines the implementation details found in the source code and demonstrates practical patterns for both system_timer and steady_timer usage.
Architecture of ASIO Timer Classes
The Generic Timer Implementation
The core timer logic resides in include/asio/basic_waitable_timer.hpp, which implements the template class basic_waitable_timer<Clock, WaitTraits, Executor>. This clock‑agnostic design allows the timer to work with any C++ chrono clock or Boost.Chrono equivalent, with the concrete type determined at compile time.
System Timer vs Steady Timer
The library provides two common typedefs for immediate use:
asio::system_timer– Defined ininclude/asio/system_timer.hpp, usesstd::chrono::system_clockfor wall‑clock time.asio::steady_timer– Defined ininclude/asio/steady_timer.hpp, usesstd::chrono::steady_clockfor monotonic timing immune to system clock adjustments.
Core Timer Operations and Executor Binding
Setting Expiry Times
Timers expose two primary methods for setting deadlines defined in basic_waitable_timer.hpp:
expires_at(const time_point&)– Sets an absolute expiry time.expires_after(const duration&)– Sets a relative expiry from the current time.
Both methods cancel any pending asynchronous waits, dispatching asio::error::operation_aborted to waiting handlers.
Synchronous vs Asynchronous Execution
wait()– Blocks the calling thread until expiry or cancellation.async_wait(handler)– Registers a completion handler with the timer's associated executor, returning immediately.
Cancellation Mechanics
Calling cancel() or cancel_one() aborts pending waits. Internally, the timer delegates to deadline_timer_service via the impl_ member (an io_object_impl wrapper), which manages the expiry state and translates errors through detail::throw_error.
Blocking Timer Operations Example
For single‑threaded scenarios, use wait() on a system_timer:
#include <asio/system_timer.hpp>
#include <asio/io_context.hpp>
#include <chrono>
#include <iostream>
asio::io_context ctx;
asio::system_timer t(ctx);
t.expires_after(std::chrono::seconds(3)); // 3‑second timer
t.wait(); // blocks until expiry
std::cout << "Timer fired\n";
Asynchronous Timer Operations with Handlers
For integration with ASIO's I/O loop, use async_wait with a lambda handler:
#include <asio/steady_timer.hpp>
#include <asio/io_context.hpp>
#include <chrono>
#include <iostream>
asio::io_context ctx;
asio::steady_timer t(ctx,
std::chrono::steady_clock::now() + std::chrono::seconds(5));
t.async_wait([](const asio::error_code& ec) {
if (!ec) {
std::cout << "Timer expired\n";
} else if (ec == asio::error::operation_aborted) {
std::cout << "Timer cancelled\n";
}
});
ctx.run(); // starts the I/O loop
Timer Cancellation and Resets
When modifying a timer while an async wait is pending, check the return value of expires_after to detect cancellations:
asio::io_context ctx;
asio::steady_timer t(ctx);
t.async_wait([](const asio::error_code& ec) {
if (!ec) std::cout << "First expiry\n";
});
if (t.expires_after(std::chrono::seconds(2)) > 0) {
// Cancelled the pending wait; start a new one.
t.async_wait([](const asio::error_code& ec) {
if (!ec) std::cout << "Second expiry\n";
});
}
ctx.run();
Modern C++ Coroutines Support
ASIO timer-based operations support C++20 coroutines via asio::use_awaitable:
asio::io_context ctx;
asio::steady_timer t(ctx);
co_await t.async_wait(asio::use_awaitable);
std::cout << "Timer completed via coroutine\n";
Summary
- ASIO timers are implemented via the
basic_waitable_timertemplate ininclude/asio/basic_waitable_timer.hpp, supporting any chrono clock. - Executor binding occurs at construction, ensuring completion handlers dispatch through the associated I/O context.
- Synchronization options include blocking
wait()or non‑blockingasync_wait()with handler callbacks. - Cancellation safety is built‑in; changing expiry times automatically aborts pending waits with
asio::error::operation_aborted. - Service implementation delegates to
deadline_timer_servicethrough theio_object_implwrapper for state management.
Frequently Asked Questions
What is the difference between asio::system_timer and asio::steady_timer?
asio::system_timer uses std::chrono::system_clock and tracks wall‑clock time, making it susceptible to system time adjustments. asio::steady_timer uses std::chrono::steady_clock and provides monotonic timing guarantees, ensuring the duration is always consistent regardless of system clock changes.
How do I cancel a pending asynchronous timer wait?
Call either cancel() to abort all pending waits or cancel_one() to abort just one. The handlers will be invoked with asio::error::operation_aborted. Alternatively, calling expires_at() or expires_after() automatically cancels pending waits and returns the number of cancelled handlers.
Can ASIO timers work with C++20 coroutines?
Yes. Pass asio::use_awaitable as the completion token to async_wait(), allowing you to co_await the timer directly within a coroutine. The timer completes on the executor associated with the timer object, maintaining strand safety.
Where does the timer store its expiry state?
The expiry state is managed by deadline_timer_service, accessed through the impl_ member of basic_waitable_timer. This io_object_impl wrapper couples the timer state with its executor, ensuring thread‑safe access to the expiry time and cancellation flags.
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 →