# What Is an ASIO Executor and How Does It Function?

> Understand ASIO executors, the core abstraction in Boost.Asio that dictates where, when, and how async handlers run. Learn their function in work tracking and dispatching.

- Repository: [chriskohlhoff/asio](https://github.com/chriskohlhoff/asio)
- Tags: deep-dive
- Published: 2026-07-12

---

**An ASIO executor is the core abstraction in Boost.Asio that defines where, when, and how asynchronous completion handlers execute, responsible for work tracking and handler dispatching through a type-erased polymorphic wrapper.**

In the `chriskohlhoff/asio` repository, an **ASIO executor** represents the fundamental mechanism that separates execution policy from I/O mechanics. Every asynchronous agent—whether a socket, timer, or coroutine—carries an associated executor that determines exactly how and where callback code will run.

## Core Responsibilities of an ASIO Executor

An ASIO executor fulfills two primary duties within the asynchronous execution model.

### Work Tracking

The executor monitors outstanding asynchronous operations through **`on_work_started()`** and **`on_work_finished()`** methods. These calls notify the executor when active work exists, preventing the underlying I/O resources from shutting down prematurely while handlers remain pending.

### Handler Dispatching

The executor provides three distinct functions for scheduling completion handlers:

- **dispatch** – Executes the handler immediately if the current thread can run it (fast-dispatch optimization), otherwise behaves like `post`.
- **post** – Always queues the handler for later execution on the executor's associated execution context.
- **defer** – Queues the handler but may allow the executor to coalesce multiple deferred operations for improved efficiency.

## Type-Erased Implementation

The `asio::executor` class serves as a polymorphic wrapper that can hold any concrete executor type while maintaining runtime flexibility.

### The executor Class

In [`include/asio/executor.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/executor.hpp), the public `executor` class implements a type-erased storage mechanism using an internal **`impl_base`** pointer. This design allows code to store a generic executor object and later retrieve the concrete type via **`target<T>()`** or **`target_type()`**.

### Reference Counting and Copy Semantics

The implementation in [`include/asio/impl/executor.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/impl/executor.hpp) provides the concrete template **`executor::impl<Executor, Allocator>`** that forwards calls to the wrapped executor. These implementation objects utilize reference counting via **`detail::atomic_count`**, enabling copy-on-write semantics where multiple executor objects share the same underlying implementation safely across threads.

### Allocator Awareness

All dispatch operations receive an allocator parameter, allowing the executor to allocate necessary storage for handlers in a manner consistent with the executor's memory model. This design supports custom allocation strategies that minimize memory fragmentation in high-performance I/O scenarios.

## Practical Usage Examples

The following examples demonstrate how to create, use, and introspect ASIO executors in real code:

```cpp
// Example 1 – Creating a generic executor from a concrete one.
asio::io_context ctx;
asio::executor gen_exec = ctx.get_executor();          // type‑erased executor

```

```cpp
// Example 2 – Using the executor to post a handler.
gen_exec.post([]{ std::cout << "Handler runs on the io_context\n"; },
              asio::allocator_arg, std::allocator<void>{});

```

```cpp
// Example 3 – Retrieving the underlying concrete executor.
if (auto* concrete = gen_exec.target<asio::io_context::executor_type>()) {
    // We now have a pointer to the original io_context executor.
    concrete->dispatch([]{ std::cout << "Direct dispatch\n"; },
                       std::allocator<void>{});
}

```

```cpp
// Example 4 – Using a system executor (runs on the calling thread).
asio::system_executor sys_exec;
asio::executor poly_exec = sys_exec;                   // implicit conversion
poly_exec.dispatch([]{ std::cout << "Runs instantly\n"; },
                   std::allocator<void>{});

```

## Key Source Files

Understanding the ASIO executor implementation requires examining these specific files in the `chriskohlhoff/asio` repository:

- **[`include/asio/executor.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/executor.hpp)** – Declares the public `executor` class, its interface, and the type-erased storage (`impl_base`).
- **[`include/asio/impl/executor.hpp`](https://github.com/chriskohlhoff/asio/blob/main/include/asio/impl/executor.hpp)** – Provides the concrete template implementation (`executor::impl<Executor, Allocator>`) that forwards calls to the wrapped executor and manages reference counting.
- **`src/doc/overview/model/executors.qbk`** – Contains the conceptual documentation explaining executor roles within Asio's execution model.

## Summary

- An ASIO executor defines **where**, **when**, and **how** asynchronous completion handlers execute.
- Work tracking via `on_work_started()` and `on_work_finished()` prevents premature resource shutdown.
- The three dispatch mechanisms—**dispatch**, **post**, and **defer**—offer different scheduling guarantees.
- The `asio::executor` class provides a **type-erased polymorphic wrapper** with reference-counted copy semantics.
- Allocator-aware operations allow custom memory management strategies for handler execution.

## Frequently Asked Questions

### What is the difference between dispatch, post, and defer in ASIO?

**dispatch** executes handlers immediately if the current thread can run them, otherwise queuing for later. **post** always queues the handler for subsequent execution regardless of the current context. **defer** also queues the handler but may allow the executor to coalesce multiple deferred operations, potentially improving batch processing efficiency.

### How does work tracking prevent premature shutdown?

When `on_work_started()` is called, the executor increments an internal work count that keeps the `io_context` and associated I/O resources alive. When handlers complete and `on_work_finished()` is invoked, the count decrements. The underlying context only stops when the work count reaches zero, ensuring no pending handlers are discarded prematurely.

### Can I retrieve the concrete executor type from a polymorphic executor?

Yes. The `target<T>()` member function returns a pointer to the concrete executor type if the stored implementation matches type `T`, otherwise returning `nullptr`. The `target_type()` function returns `type_info` for runtime type checking without downcasting.

### Why does ASIO use type erasure for executors?

Type erasure allows asynchronous agents to store executors without knowing their concrete types at compile time. This enables runtime flexibility where code can work with `asio::system_executor`, strand executors, or custom thread-pool executors through a single interface, supporting powerful composition patterns like strands and GUI-thread executors while maintaining zero-overhead abstraction for concrete types.