# How to Use Asio steady_timer and deadline_timer for Timeouts

> Learn to use asio steady_timer and deadline_timer for reliable asynchronous timeouts in C++. Explore modern chrono support and legacy compatibility for effective async programming.

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

---

**`asio::steady_timer` and `asio::deadline_timer` provide portable asynchronous timeout mechanisms built on the `basic_waitable_timer` template, with `steady_timer` preferred for modern C++ chrono support and `deadline_timer` maintained for legacy Boost.Date_Time compatibility.**

The chriskohlhoff/asio repository offers two distinct timer implementations for managing timeouts in asynchronous operations. Understanding how to use asio steady_timer and deadline_timer for timeouts enables developers to implement robust deadline semantics, whether using modern C++11 chrono facilities or maintaining legacy codebases. Both classes share underlying mechanics through `basic_waitable_timer` but expose different clock abstractions and header interfaces.

## Understanding Asio Timer Types

### steady_timer vs deadline_timer

The Asio library provides these timers through separate headers with distinct clock semantics:

- **`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 time measurement. This is the recommended choice for new code because it is immune to system clock adjustments.
- **`deadline_timer`** (defined in [`include/asio/deadline_timer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/deadline_timer.hpp)): Deprecated typedef using `boost::posix_time::ptime` from Boost.Date_Time. Useful for legacy integrations but lacks the monotonic guarantees of `steady_timer`.

Both are specializations of the `basic_waitable_timer` template found in [`include/asio/basic_waitable_timer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/basic_waitable_timer.hpp), which handles the core timer state machine and asynchronous wait operations.

### Core Architecture

The timer implementation delegates to `asio::detail::deadline_timer_service` (located in [`include/asio/detail/deadline_timer_service.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/detail/deadline_timer_service.hpp)), which maintains a timer queue integrated with the I/O reactor. When you call `async_wait()`, the service registers the operation; when the expiry time passes, the handler is posted to the timer's associated executor.

## Implementing Timeouts with steady_timer

For modern C++ applications, `steady_timer` provides intuitive chrono-based APIs.

### Synchronous Timeout

Use `wait()` to block the current thread until the timer expires:

```cpp
#include <asio.hpp>
#include <chrono>

int main() {
    asio::io_context io;
    asio::steady_timer t(io);
    
    t.expires_after(std::chrono::seconds(2));
    t.wait();  // Blocks until expiry
    
    std::cout << "Timeout completed\n";
}

```

### Asynchronous Timeout

Use `async_wait()` with a completion handler for non-blocking operations:

```cpp
t.expires_after(std::chrono::seconds(5));
t.async_wait([](const asio::error_code& ec) {
    if (!ec) {
        std::cout << "Timer expired normally\n";
    }
});

```

The `expires_after()` method accepts any `std::chrono::duration`, while `expires_at()` accepts a `std::chrono::time_point` for absolute deadlines.

## Legacy deadline_timer Usage

When working with existing Boost.Date_Time code, include [`include/asio/deadline_timer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/deadline_timer.hpp) and use the legacy API:

```cpp
#include <asio/deadline_timer.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

void handler(const asio::error_code& ec);

int main() {
    asio::io_context io;
    asio::deadline_timer t(io);
    
    // Relative timeout
    t.expires_from_now(boost::posix_time::seconds(3));
    
    // Absolute timeout
    t.expires_at(boost::posix_time::second_clock::universal_time() 
                 + boost::posix_time::seconds(5));
    
    t.async_wait(handler);
    io.run();
}

```

Note that `expires_from_now()` and `expires_at()` here use Boost.Date_Time types rather than chrono.

## Timer Cancellation Mechanics

### Cancelling Pending Waits

Modifying a timer's expiry while an asynchronous wait is outstanding cancels that wait. Both `expires_after()` and `expires_at()` return the number of cancelled handlers (0 if too late to cancel, 1 if successful):

```cpp
// Returns 1 if handler was cancelled, 0 if already running
std::size_t cancelled = t.expires_after(std::chrono::seconds(10));

```

Cancelled handlers receive `asio::error::operation_aborted` as the error code, allowing you to distinguish between natural expiry and cancellation.

### Implementing a Reset-on-Activity Timeout

To create a watchdog timer that resets when activity occurs, use `asio::bind_executor` with a strand for thread safety:

```cpp
#include <asio.hpp>
#include <chrono>

class watchdog {
public:
    explicit watchdog(asio::io_context& ctx)
        : timer_(ctx), strand_(asio::make_strand(ctx)) {}
    
    void kick() {
        asio::post(strand_, [this]() {
            // Cancel pending wait and restart timer
            if (timer_.expires_after(std::chrono::seconds(5)) > 0) {
                timer_.async_wait(
                    asio::bind_executor(strand_, 
                        &watchdog::expired, this));
            }
        });
    }
    
private:
    void expired(const asio::error_code& ec) {
        if (ec != asio::error::operation_aborted) {
            std::cout << "Timeout - no activity for 5 seconds\n";
        }
    }
    
    asio::steady_timer timer_;
    asio::strand<asio::io_context::executor_type> strand_;
};

```

This pattern leverages the fact that `expires_after()` cancels existing waits before establishing the new deadline.

## Composing Multiple Timers

You can chain timers using the `expiry()` method to retrieve a timer's current deadline:

```cpp
asio::steady_timer t1(io);
asio::steady_timer t2(io);

t1.expires_after(std::chrono::seconds(2));
t1.async_wait([](auto){ std::cout << "First timer done\n"; });

// t2 expires 3 seconds after t1 expires
t2.expires_at(t1.expiry() + std::chrono::seconds(3));
t2.async_wait([](auto){ std::cout << "Second timer done\n"; });

```

## Summary

- **Use `steady_timer`** for new development in [`include/asio/steady_timer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/steady_timer.hpp) to leverage C++11 chrono and monotonic clock guarantees.
- **Use `deadline_timer`** only when maintaining legacy code requiring Boost.Date_Time integration.
- **Modify expiry** via `expires_after()` or `expires_at()` to cancel pending operations; these methods return the count of cancelled handlers.
- **Check for `asio::error::operation_aborted`** in completion handlers to handle cancellation cases correctly.
- **Access deadline information** using the `expiry()` method to compose complex timer sequences.

## Frequently Asked Questions

### What is the difference between `expires_after` and `expires_from_now`?

`expires_after()` is the modern method used with `steady_timer`, accepting `std::chrono::duration` values like `std::chrono::seconds`. `expires_from_now()` is the legacy method for `deadline_timer` that accepts `boost::posix_time::duration` objects. Both set relative timeouts, but `expires_after` is preferred for type safety and standard library compatibility.

### How do I cancel an active asynchronous wait on an Asio timer?

Call `expires_after()`, `expires_at()`, or `cancel()` on the timer instance. These methods immediately cancel any pending wait operations and return the number of handlers cancelled (typically 0 or 1). The cancelled handlers will be invoked with `asio::error::operation_aborted` guaranteed by the `basic_waitable_timer` implementation in [`include/asio/basic_waitable_timer.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/basic_waitable_timer.hpp).

### Why should I prefer `steady_timer` over `deadline_timer`?

`steady_timer` uses `std::chrono::steady_clock`, which provides monotonic time (always moves forward) and is immune to system clock adjustments. `deadline_timer` uses `boost::posix_time::ptime` based on the system clock, which can jump backward or forward if the system time is changed, potentially causing missed deadlines or premature timeouts.

### What error code do cancelled timer handlers receive?

Cancelled handlers receive `asio::error::operation_aborted` in their `asio::error_code` parameter. This specific error code indicates that the wait operation was cancelled explicitly via `cancel()`, `expires_after()`, or destruction of the timer object, distinguishing it from successful expiry or other system errors.