#### Per-Operation Cancellation

| Note
These type requirements and classes are the low level building blocks of cancellation. For most use cases, consider using a higher level abstraction, such as experimental::make_parallel_group or the logical operators for `awaitable`.

I/O objects, such as sockets and timers, support object-wide cancellation of outstanding asynchronous operations via their `close` or `cancel` member functions. However, certain asynchronous operations also support individual, targeted cancellation. This per-operation cancellation is enabled by specifying that a completion handler has an associated cancellation slot which satisfies the CancellationSlot type requirements. A cancellation slot is a lightweight channel used for delivering a cancellation request.

Given a copy of a user-defined `Handler` object `h`, if an asynchronous operation supports cancellation it will obtain a cancellation slot using the `get_associated_cancellation_slot` function. For example:

    asio::associated_cancellation_slot_t<Handler> s
      = asio::get_associated_cancellation_slot(h);

The associated cancellation slot must satisfy the CancellationSlot type requirements.

By default, handlers use a default-constructed `cancellation_slot`, which means that per-operation cancellation is disabled. The cancellation slot may be customised for a particular handler type by specifying a nested type `cancellation_slot_type` and member function `get_cancellation_slot()`:

    class my_handler
    {
    public:
      // Custom implementation of CancellationSlot type requirements.
      typedef my_cancellation_slot cancellation_slot_type;

      // Return a custom cancellation slot implementation.
      cancellation_slot_type get_cancellation_slot() const noexcept
      {
        return my_cancellation_slot(...);
      }

      void operator()() { ... }
    };

In more complex cases, the `associated_cancellation_slot` template may be partially specialised directly:

    namespace asio {

      template <typename CancellationSlot>
      struct associated_cancellation_slot<my_handler, CancellationSlot>
      {
        // Custom implementation of CancellationSlot type requirements.
        typedef my_cancellation_slot type;

        // Return a custom cancellation_slot implementation.
        static type get(const my_handler&,
            const CancellationSlot& a = CancellationSlot()) noexcept
        {
          return my_cancellation_slot(...);
        }
      };

    } // namespace asio

For convenience, a cancellation slot may be associated with a handler by using the `bind_cancellation_slot` function. This is particularly useful when associating a cancellation slot with a lambda:

    asio::async_read(my_socket, my_buffer,
        asio::bind_cancellation_slot(
          my_cancellation_slot,
          [](asio::error_code e, std::size_t n)
          {
            ...
          }
        )
      );

Asio provides a ready-to-use cancellation slot in the form of `cancellation_slot` and its counterpart `cancellation_signal`. These two classes implement a one-to-one pairing of producer (signal) and consumer (slot) interfaces. The following example shows its use:

    class session
      : public std::enable_shared_from_this<proxy>
    {
      ...

      void do_read()
      {
        auto self = shared_from_this();
        socket_.async_read_some(
            buffer(data_),
            asio::bind_cancellation_slot(
              cancel_signal_.slot(),
              [self](std::error_code error, std::size_t n)
              {
                ...
              }
            )
          );
      }

      ...

      void request_cancel()
      {
        cancel_signal_.emit(asio::cancellation_type::total);
      }

      ...

      asio::cancellation_signal cancel_signal_;
    };

A `cancellation_signal` contains a single slot, and consequently a cancellation signal/slot pair may be used with at most one operation at a time. However, the same slot may be reused for subsequent operations.

To support cancellation, an asynchronous operation installs a cancellation handler into the slot by calling the slot's `assign` or `emplace` functions. This handler will be invoked when a cancellation signal is emitted. A slot holds exactly one handler at a time, and installing a new handler will overwrite any previously installed handler.

When emitting a cancellation signal, the caller must specify a cancellation type. This value is a bitmask that dictates what guarantees the cancellation target must make if successful cancellation occurs. The possible bit values are, from weakest to strongest guarantee, are:

Table 1. cancellation types

Bit | Guarantee if cancellation is successful | Examples where this is the strongest supported guarantee
--- | --- | ---
`terminal` | The operation had unspecified side effects, and it is only safe to close or destroy the I/O object. | A stateful implementation of a message framing protocol, where an asynchronous operation sends or receives a complete message. If cancellation occurs part-way through the message body, it is not possible to report a sensible state to the completion handler.
`partial` | The operation had well-defined side effects, and the completion handler for the operation indicates what these side effects were. | Composed operations such as `async_read` and `async_write`. If cancellation occurs before all bytes are transferred, the completion handler is passed the total bytes transferred so far. The caller may use this information to start another operation to transfer the remaining bytes.
`total` | The operation had no side effects that are observable through the API. | Low level system calls that transfer either zero or non-zero bytes. Wait-for-readiness operations that have no side effects, even when successful. A fully buffered message framing protocol implementation, where partial messages are stored so that they may be reused on the next operation.

For example, if application logic requires that an operation complete with all-or-nothing side effects, it should emit only the `total` cancellation type. If this type is unsupported by the target operation, no cancellation will occur.

Furthermore, a stronger guarantee always satisfies the requirements of a weaker guarantee. The `partial` guarantee still satisfies the `terminal` guarantee. The `total` guarantee satisfies both `partial` and `terminal`. This means that when an operation supports a given cancellation type as its strongest guarantee, it should honour cancellation requests for any of the weaker guarantees.

Cancellation requests should not be emitted during an asynchronous operation's initiating function. Cancellation requests that are emitted before an operation starts have no effect. Similarly, cancellation requests made after completion have no effect.

When emitting a cancellation signal, the thread safety rules apply as if calling a member function on the target operation's I/O object. For non-composed operations, this means that it is safe to emit the cancellation signal from any thread provided there are no other concurrent calls to the I/O object, and no other concurrent cancellation signal requests. For composed operations, care must be taken to ensure the cancellation request does not occur concurrently with the operation's intermediate completion handlers.

###### Supported Operations

Consult the documentation for individual asynchronous operations for their supported cancellation types, if any. The ability to cancel individual operations, or composed operations, is currently supported by:

- timers
- sockets on POSIX and Windows
- POSIX descriptors
- Windows HANDLEs
- signal sets
- serial ports
- SSL streams
- all Asio-provided composed operations such as `async_read` and `async_write`
- compositions based on `async_compose`
- C++20 coroutines that use `awaitable`
- C++20 coroutines that use `experimental::coro`
- the `experimental::parallel_group` operation
- the `experimental::promise` class

###### See Also

CancellationSlot, associated_cancellation_slot, bind_cancellation_slot, cancellation_signal, cancellation_slot, cancellation_state, cancellation_type, get_associated_cancellation_slot, experimental::parallel_group, experimental::make_parallel_group