# How to Use ASIO for Timer‑Based Operations: Complete Guide to Clocks, Waits, and Cancellation

> Master ASIO timer operations. Learn to implement clocks waits and cancellation with basic_waitable_timer for efficient asynchronous programming. Explore this complete guide now.

- Repository: [chriskohlhoff/asio](https://github.com/chriskohlhoff/asio)
- Tags: how-to-guide
- Published: 2026-07-12

---

**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`](https://github.com/chriskohlhoff/asio/blob/main/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 in [`include/asio/system_timer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/system_timer.hpp), uses `std::chrono::system_clock` for wall‑clock time.
- **`asio::steady_timer`** – Defined in [`include/asio/steady_timer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/steady_timer.hpp), uses `std::chrono::steady_clock` for 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`](https://github.com/chriskohlhoff/asio/blob/main/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`:

```cpp
#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:

```cpp
#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:

```cpp
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`:

```cpp
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_timer` template in [`include/asio/basic_waitable_timer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/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‑blocking `async_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_service` through the `io_object_impl` wrapper 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.