| Home | Libraries | People | FAQ | More

### Requirements on asynchronous operations

In Boost.Asio, an asynchronous operation is initiated by a function that is named with the prefix `async_`. These functions will be referred to as initiating functions.

All initiating functions in Boost.Asio take a function object meeting handler requirements as the final parameter. These handlers accept as their first parameter an lvalue of type `const error_code`.

Implementations of asynchronous operations in Boost.Asio may call the application programming interface (API) provided by the operating system. If such an operating system API call results in an error, the handler will be invoked with a `const error_code` lvalue that evaluates to true. Otherwise the handler will be invoked with a `const error_code` lvalue that evaluates to false.

Unless otherwise noted, when the behaviour of an asynchronous operation is defined "as if" implemented by a POSIX function, the handler will be invoked with a value of type `error_code` that corresponds to the failure condition described by POSIX for that function, if any. Otherwise the handler will be invoked with an implementation-defined `error_code` value that reflects the operating system error.

Asynchronous operations will not fail with an error condition that indicates interruption by a signal (POSIX `EINTR`). Asynchronous operations will not fail with any error condition associated with non-blocking operations (POSIX `EWOULDBLOCK`, `EAGAIN` or `EINPROGRESS`; Windows `WSAEWOULDBLOCK` or `WSAEINPROGRESS`).

All asynchronous operations have an associated `io_service` object. Where the initiating function is a member function, the associated `io_service` is that returned by the `get_io_service()` member function on the same object. Where the initiating function is not a member function, the associated `io_service` is that returned by the `get_io_service()` member function of the first argument to the initiating function.

Arguments to initiating functions will be treated as follows:

— If the parameter is declared as a const reference or by-value, the program is not required to guarantee the validity of the argument after the initiating function completes. The implementation may make copies of the argument, and all copies will be destroyed no later than immediately after invocation of the handler.

— If the parameter is declared as a non-const reference, const pointer or non-const pointer, the program must guarantee the validity of the argument until the handler is invoked.

The library implementation is only permitted to make calls to an initiating function's arguments' copy constructors or destructors from a thread that satisfies one of the following conditions:

— The thread is executing any member function of the associated `io_service` object.

— The thread is executing the destructor of the associated `io_service` object.

— The thread is executing one of the `io_service` service access functions `use_service`, `add_service` or `has_service`, where the first argument is the associated `io_service` object.

— The thread is executing any member function, constructor or destructor of an object of a class defined in this clause, where the object's `get_io_service()` member function returns the associated `io_service` object.

— The thread is executing any function defined in this clause, where any argument to the function has an `get_io_service()` member function that returns the associated `io_service` object.

The `io_service` object associated with an asynchronous operation will have unfinished work, as if by maintaining the existence of one or more objects of class `io_service::work` constructed using the `io_service`, until immediately after the handler for the asynchronous operation has been invoked.

When an asynchronous operation is complete, the handler for the operation will be invoked as if by:

1. Constructing a bound completion handler `bch` for the handler, as described below.
2. Calling `ios.post(bch)` to schedule the handler for deferred invocation, where `ios` is the associated `io_service`.

This implies that the handler must not be called directly from within the initiating function, even if the asynchronous operation completes immediately.

A bound completion handler is a handler object that contains a copy of a user-supplied handler, where the user-supplied handler accepts one or more arguments. The bound completion handler does not accept any arguments, and contains values to be passed as arguments to the user-supplied handler. The bound completion handler forwards the `asio_handler_allocate()`, `asio_handler_deallocate()`, and `asio_handler_invoke()` calls to the corresponding functions for the user-supplied handler. A bound completion handler meets the requirements for a completion handler.

For example, a bound completion handler for a `ReadHandler` may be implemented as follows:

    template<class ReadHandler>
    struct bound_read_handler
    {
      bound_read_handler(ReadHandler handler, const error_code& ec, size_t s)
        : handler_(handler), ec_(ec), s_(s)
      {
      }

      void operator()()
      {
        handler_(ec_, s_);
      }

      ReadHandler handler_;
      const error_code ec_;
      const size_t s_;
    };

    template<class ReadHandler>
    void* asio_handler_allocate(size_t size,
                                bound_read_handler<ReadHandler>* this_handler)
    {
      using boost::asio::asio_handler_allocate;
      return asio_handler_allocate(size, &this_handler->handler_);
    }

    template<class ReadHandler>
    void asio_handler_deallocate(void* pointer, std::size_t size,
                                 bound_read_handler<ReadHandler>* this_handler)
    {
      using boost::asio::asio_handler_deallocate;
      asio_handler_deallocate(pointer, size, &this_handler->handler_);
    }

    template<class F, class ReadHandler>
    void asio_handler_invoke(const F& f,
                             bound_read_handler<ReadHandler>* this_handler)
    {
      using boost::asio::asio_handler_invoke;
      asio_handler_invoke(f, &this_handler->handler_);
    }

If the thread that initiates an asynchronous operation terminates before the associated handler is invoked, the behaviour is implementation-defined. Specifically, on Windows versions prior to Vista, unfinished operations are cancelled when the initiating thread exits.

The handler argument to an initiating function defines a handler identity. That is, the original handler argument and any copies of the handler argument will be considered equivalent. If the implementation needs to allocate storage for an asynchronous operation, the implementation will perform `asio_handler_allocate(size, &h)`, where `size` is the required size in bytes, and `h` is the handler. The implementation will perform `asio_handler_deallocate(p, size, &h)`, where `p` is a pointer to the storage, to deallocate the storage prior to the invocation of the handler via `asio_handler_invoke`. Multiple storage blocks may be allocated for a single asynchronous operation.

##### Return type of an initiating function

By default, initiating functions return `void`. This is always the case when the handler is a function pointer, C++11 lambda, or a function object produced by `boost::bind` or `std::bind`.

For other types, the return type may be customised via a two-step process:

1. A specialisation of the `handler_type` template, which is used to determine the true handler type based on the asynchronous operation's handler's signature.
2. A specialisation of the `async_result` template, which is used both to determine the return type and to extract the return value from the handler.

These two templates have been specialised to provide support for stackful coroutines and the C++11 `std::future` class.

As an example, consider what happens when enabling `std::future` support by using the `boost::asio::use_future` special value, as in:

    std::future<std::size_t> length =
      my_socket.async_read_some(my_buffer, boost::asio::use_future);

When a handler signature has the form:

    void handler(error_code ec, result_type result);

the initiating function returns a `std::future` templated on `result_type`. In the above `async_read_some` example, this is `std::size_t`. If the asynchronous operation fails, the `error_code` is converted into a `system_error` exception and passed back to the caller through the future.

Where a handler signature has the form:

    void handler(error_code ec);

the initiating function instead returns `std::future<void>`.