How to Use `asio bind_cancellation_slot`: Complete Guide with Examples

bind_cancellation_slot is a utility in the Asio library that associates a cancellation slot with any handler, completion token, or function object, enabling automatic cancellation of pending asynchronous operations when a signal is emitted.

The chriskohlhoff/asio repository provides this mechanism through the header asio/bind_cancellation_slot.hpp. By binding a cancellation slot to your asynchronous handlers, you allow the Asio framework to inject cancellation signals directly into your callbacks when operations are aborted.

Core Components

The implementation relies on several cooperating types that work together to wrap handlers and expose cancellation semantics to the Asio execution framework.

cancellation_slot_binder

cancellation_slot_binder is a callable wrapper defined at line 51 in asio/bind_cancellation_slot.hpp. It stores two members: a CancellationSlot and a target object T. When invoked, it forwards all arguments to the wrapped target while making the slot available to Asio's generic algorithms.

This binder is the primary mechanism that connects your handler to the cancellation signal infrastructure.

partial_cancellation_slot_binder

partial_cancellation_slot_binder acts as an intermediate completion token that captures a cancellation slot without immediately binding it to a handler. It enables a "partial application" syntax where you first bind the slot, then supply the handler later.

When invoked, this partial binder produces a full cancellation_slot_binder, allowing you to reuse the same slot binding across multiple operations.

Free Factory Functions

Two overloads of bind_cancellation_slot provide convenient construction:

  • bind_cancellation_slot(const CancellationSlot&) – Returns a partial_cancellation_slot_binder
  • bind_cancellation_slot(const CancellationSlot&, T&&) – Returns a cancellation_slot_binder wrapping the target

These functions handle the template deduction and move semantics automatically.

Trait Specializations

The header provides specializations for async_result (lines 84-136) that enable the binder to function as a completion token. Additionally, associator specializations (lines 79-98) make the binder transparently expose its associated executor, allocator, and cancellation slot to Asio's generic algorithms.

The associated_cancellation_slot trait (lines 100-108) specifically detects when a handler is a cancellation_slot_binder and returns the stored slot, allowing Asio to link the handler to the cancellation signal chain.

Practical Usage Patterns

The test suite in src/tests/unit/bind_cancellation_slot.cpp demonstrates three common patterns for applying cancellation slots to asynchronous operations.

Pattern 1: Binding to Function Objects

The most straightforward approach binds a cancellation slot directly to a function object or std::bind expression:

asio::io_context ioc;
asio::cancellation_signal sig;
int count = 0;

asio::steady_timer t(ioc, asio::chrono::seconds(5));
t.async_wait(
    asio::bind_cancellation_slot(sig.slot(),
      std::bind(&increment_on_cancel,
                &count, std::placeholders::_1)));

In this example, sig.slot() provides the cancellation slot, while std::bind creates a callable matching the timer's handler signature. When sig.emit(asio::cancellation_type::terminal) is called, the timer operation completes immediately with operation_aborted, and your handler receives the error code.

Pattern 2: Binding to Custom Completion Tokens

You can also bind slots to user-defined completion tokens that require async_result specialization:

struct incrementer_token_v1 {
    explicit incrementer_token_v1(int* c) : count(c) {}
    int* count;
};

struct incrementer_handler_v1 {
    explicit incrementer_handler_v1(incrementer_token_v1 t) : count(t.count) {}
    void operator()(asio::error_code ec) { 
        increment_on_cancel(count, ec); 
    }
    int* count;
};

// Async_result specialization for incrementer_token_v1 defined elsewhere...

asio::io_context ioc;
asio::cancellation_signal sig;
int count = 0;

asio::steady_timer t(ioc, asio::chrono::seconds(5));
t.async_wait(
    asio::bind_cancellation_slot(sig.slot(),
       incrementer_token_v1(&count)));

Here, bind_cancellation_slot wraps the custom token incrementer_token_v1. The associated async_result specialization (see lines 97-105 in the test file) adapts the token so that Asio constructs the handler through the binder, preserving the cancellation slot association.

Pattern 3: Partial Binding (Two-Step Syntax)

For scenarios where you want to bind the slot once and reuse it for multiple operations, use the partial application syntax:

asio::io_context ioc;
asio::cancellation_signal sig;
int count = 0;

auto slot_binder = asio::bind_cancellation_slot(sig.slot());

asio::steady_timer t(ioc, asio::chrono::seconds(5));
t.async_wait(
    slot_binder(
        std::bind(&increment_on_cancel,
                  &count, std::placeholders::_1)));

bind_cancellation_slot(sig.slot()) returns a partial_cancellation_slot_binder. The subsequent call operator () supplies the actual handler, producing a full cancellation_slot_binder. This pattern is particularly useful when managing multiple asynchronous operations that share the same cancellation signal.

How Cancellation Propagates

When an asynchronous operation starts, Asio queries the handler's associated cancellation slot via the associated_cancellation_slot trait. If the handler is a cancellation_slot_binder, the trait returns the slot stored inside the binder (as implemented in lines 100-108 of asio/bind_cancellation_slot.hpp).

Consequently, calling sig.emit(...) on the original cancellation_signal causes the bound handler to be invoked with an operation_aborted error code. The binder works for any callable type, including plain function objects, std::bind expressions, or user-defined completion tokens. It also supports nesting: you can bind multiple slots, or bind a slot to a token that itself creates a binder.

Summary

  • bind_cancellation_slot associates cancellation slots with handlers in asio/bind_cancellation_slot.hpp
  • cancellation_slot_binder wraps your callable and stores the slot at line 51 of the header
  • partial_cancellation_slot_binder enables two-step binding syntax for reusable slot configurations
  • Trait specializations (lines 79-136) make the binder transparent to Asio's executor and cancellation systems
  • Three patterns cover direct function binding, custom tokens, and partial application as shown in src/tests/unit/bind_cancellation_slot.cpp
  • Cancellation propagation occurs through associated_cancellation_slot detection when operations are initiated

Frequently Asked Questions

How do I create a cancellation slot to bind to my handler?

Create a cancellation_signal object, then call its slot() method. This method is defined in asio/cancellation_signal.hpp. The returned slot object can be passed to bind_cancellation_slot to associate it with your handler.

Can I use bind_cancellation_slot with standard function objects like lambdas?

Yes. The cancellation_slot_binder accepts any callable type, including lambdas, std::function objects, and std::bind expressions. The binder forwards all invocations to the wrapped target while maintaining the cancellation slot association.

What happens if I emit a cancellation signal after the operation has already completed?

Cancellation signals only affect pending operations. If the asynchronous operation has already completed and the handler has been invoked, emitting the signal has no effect on that specific operation. The signal affects only operations currently awaiting completion that were started with handlers bound to that signal's slot.

Where can I find the complete implementation of async_result for custom tokens?

The async_result specializations for bind_cancellation_slot are located at lines 84-136 in asio/bind_cancellation_slot.hpp. For examples of custom token implementations, see src/tests/unit/bind_cancellation_slot.cpp, specifically around lines 97-105 where the test suite defines specializations for incrementer_token_v1.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →