301 lines
12 KiB
Plaintext
301 lines
12 KiB
Plaintext
[/
|
|
/ Copyright (c) 2003-2019 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
|
/
|
|
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
/]
|
|
|
|
[section:asynchronous_operations Requirements on asynchronous operations]
|
|
|
|
This section uses the names `Alloc1`, `Alloc2`, `alloc1`, `alloc2`, `Args`,
|
|
`CompletionHandler`, `completion_handler`, `Executor1`, `Executor2`, `ex1`,
|
|
`ex2`, `f`, [^['i]], [^['N]], `Signature`, `token`, [^T[sub ['i]]], [^t[sub
|
|
['i]]], `work1`, and `work2` as placeholders for specifying the requirements
|
|
below.
|
|
|
|
[heading General asynchronous operation concepts]
|
|
|
|
An ['initiating function] is a function which may be called to start an
|
|
asynchronous operation. A ['completion handler] is a function object that will
|
|
be invoked, at most once, with the result of the asynchronous operation.
|
|
|
|
The lifecycle of an asynchronous operation is comprised of the following events
|
|
and phases:
|
|
|
|
[mdash] Event 1: The asynchronous operation is started by a call to the
|
|
initiating function.
|
|
|
|
[mdash] Phase 1: The asynchronous operation is now ['outstanding].
|
|
|
|
[mdash] Event 2: The externally observable side effects of the asynchronous
|
|
operation, if any, are fully established. The completion handler is submitted
|
|
to an executor.
|
|
|
|
[mdash] Phase 2: The asynchronous operation is now ['completed].
|
|
|
|
[mdash] Event 3: The completion handler is called with the result of the
|
|
asynchronous operation.
|
|
|
|
In this library, all functions with the prefix `async_` are initiating
|
|
functions.
|
|
|
|
[heading Completion tokens and handlers]
|
|
|
|
Initiating functions:
|
|
|
|
[mdash] are function templates with template parameter `CompletionToken`;
|
|
|
|
[mdash] accept, as the final parameter, a ['completion token] object `token`
|
|
of type `CompletionToken`;
|
|
|
|
[mdash] specify a ['completion signature], which is a call signature (C++Std
|
|
[func.def]) `Signature` that determines the arguments to the completion
|
|
handler.
|
|
|
|
An initiating function determines the type `CompletionHandler` of its
|
|
completion handler function object by performing `typename
|
|
async_result<decay_t<CompletionToken>, Signature>::completion_handler_type`.
|
|
The completion handler object `completion_handler` is initialized with
|
|
`forward<CompletionToken>(token)`. [inline_note No other requirements are
|
|
placed on the type `CompletionToken`.]
|
|
|
|
The type `CompletionHandler` must satisfy the requirements of `Destructible`
|
|
(C++Std [destructible]) and `MoveConstructible` (C++Std
|
|
[moveconstructible]), and be callable with the specified call signature.
|
|
|
|
In this library, all initiating functions specify a ['Completion signature]
|
|
element that defines the call signature `Signature`. The ['Completion
|
|
signature] elements in this Technical Specification have named parameters, and
|
|
the results of an asynchronous operation are specified in terms of these names.
|
|
|
|
[heading Automatic deduction of initiating function return type]
|
|
|
|
The return type of an initiating function is `typename
|
|
async_result<decay_t<CompletionToken>, Signature>::return_type`.
|
|
|
|
For the sake of exposition, this library sometimes annotates functions with a
|
|
return type ['[^DEDUCED]]. For every function declaration that returns
|
|
['[^DEDUCED]], the meaning is equivalent to specifying the return type as
|
|
`typename async_result<decay_t<CompletionToken>, Signature>::return_type`.
|
|
|
|
[heading Production of initiating function return value]
|
|
|
|
An initiating function produces its return type as follows:
|
|
|
|
[mdash] constructing an object `result` of type
|
|
`async_result<decay_t<CompletionToken>, Signature>`, initialized as
|
|
`result(completion_handler)`; and
|
|
|
|
[mdash] using `result.get()` as the operand of the return statement.
|
|
|
|
\[['Example:] Given an asynchronous operation with ['Completion signature]
|
|
`void(R1 r1, R2 r2)`, an initiating function meeting these requirements may be
|
|
implemented as follows:
|
|
|
|
template<class CompletionToken>
|
|
auto async_xyz(T1 t1, T2 t2, CompletionToken&& token)
|
|
{
|
|
typename async_result<decay_t<CompletionToken>, void(R1, R2)>::completion_handler_type
|
|
completion_handler(forward<CompletionToken>(token));
|
|
|
|
async_result<decay_t<CompletionToken>, void(R1, R2)> result(completion_handler);
|
|
|
|
// initiate the operation and cause completion_handler to be invoked with
|
|
// the result
|
|
|
|
return result.get();
|
|
}
|
|
|
|
For convenience, initiating functions may be implemented using the
|
|
`async_completion` template:
|
|
|
|
template<class CompletionToken>
|
|
auto async_xyz(T1 t1, T2 t2, CompletionToken&& token)
|
|
{
|
|
async_completion<CompletionToken, void(R1, R2)> init(token);
|
|
|
|
// initiate the operation and cause init.completion_handler to be invoked
|
|
// with the result
|
|
|
|
return init.result.get();
|
|
}
|
|
|
|
'''—'''['end example]\]
|
|
|
|
[heading Lifetime of initiating function arguments]
|
|
|
|
Unless otherwise specified, the lifetime of arguments to initiating functions
|
|
shall be treated as follows:
|
|
|
|
[mdash] If the parameter has a pointer type or has a type of lvalue reference
|
|
to non-const, the implementation may assume the validity of the pointee or
|
|
referent, respectively, until the completion handler is invoked. [inline_note
|
|
In other words, the program must guarantee the validity of the argument until
|
|
the completion handler is invoked.]
|
|
|
|
[mdash] Otherwise, the implementation must not assume the validity of the
|
|
argument after the initiating function completes. [inline_note In other words,
|
|
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 shall be destroyed no later than immediately after
|
|
invocation of the completion handler.
|
|
|
|
[heading Non-blocking requirements on initiating functions]
|
|
|
|
An initiating function shall not block (C++Std [defns.block]) the calling
|
|
thread pending completion of the outstanding operation.
|
|
|
|
[std_note Initiating functions may still block the calling thread for other
|
|
reasons. For example, an initiating function may lock a mutex in order to
|
|
synchronize access to shared data.]
|
|
|
|
[heading Associated executor]
|
|
|
|
Certain objects that participate in asynchronous operations have an
|
|
['associated executor]. These are obtained as specified in the sections below.
|
|
|
|
[heading Associated I/O executor]
|
|
|
|
An asynchronous operation has an associated executor satisfying the [link
|
|
boost_asio.reference.Executor1 `Executor`] requirements. If not otherwise specified by
|
|
the asynchronous operation, this associated executor is an object of type
|
|
`system_executor`.
|
|
|
|
All asynchronous operations in this library have an associated executor object
|
|
that is determined as follows:
|
|
|
|
[mdash] If the initiating function is a member function, the associated
|
|
executor is that returned by the `get_executor` member function on the same
|
|
object.
|
|
|
|
[mdash] If the initiating function is not a member function, the associated
|
|
executor is that returned by the `get_executor` member function of the first
|
|
argument to the initiating function.
|
|
|
|
Let `Executor1` be the type of the associated executor. Let `ex1` be a value of
|
|
type `Executor1`, representing the associated executor object obtained as
|
|
described above.
|
|
|
|
[heading Associated completion handler executor]
|
|
|
|
A completion handler object of type `CompletionHandler` has an associated
|
|
executor of type `Executor2` satisfying the [link boost_asio.reference.Executor1
|
|
Executor requirements]. The type `Executor2` is
|
|
`associated_executor_t<CompletionHandler, Executor1>`. Let `ex2` be a value of
|
|
type `Executor2` obtained by performing
|
|
`get_associated_executor(completion_handler, ex1)`.
|
|
|
|
[heading Outstanding work]
|
|
|
|
Until the asynchronous operation has completed, the asynchronous operation
|
|
shall maintain:
|
|
|
|
[mdash] an object `work1` of type `executor_work_guard<Executor1>`, initialized
|
|
as `work1(ex1)`, and where `work1.owns_work() == true`; and
|
|
|
|
[mdash] an object `work2` of type `executor_work_guard<Executor2>`, initialized
|
|
as `work2(ex2)`, and where `work2.owns_work() == true`.
|
|
|
|
[heading Allocation of intermediate storage]
|
|
|
|
Asynchronous operations may allocate memory. [inline_note Such as a data
|
|
structure to store copies of the `completion_handler` object and the initiating
|
|
function's arguments.]
|
|
|
|
Let `Alloc1` be a type, satisfying the [link boost_asio.reference.ProtoAllocator
|
|
`ProtoAllocator`] requirements, that represents the asynchronous operation's
|
|
default allocation strategy. [inline_note Typically `std::allocator<void>`.]
|
|
Let `alloc1` be a value of type `Alloc1`.
|
|
|
|
A completion handler object of type `CompletionHandler` has an associated
|
|
allocator object `alloc2` of type `Alloc2` satisfying the [link
|
|
boost_asio.reference.ProtoAllocator `ProtoAllocator`] requirements. The type `Alloc2`
|
|
is `associated_allocator_t<CompletionHandler, Alloc1>`. Let `alloc2` be a value
|
|
of type `Alloc2` obtained by performing
|
|
`get_associated_allocator(completion_handler, alloc1)`.
|
|
|
|
The asynchronous operations defined in this library:
|
|
|
|
[mdash] If required, allocate memory using only the completion handler's
|
|
associated allocator.
|
|
|
|
[mdash] Prior to completion handler execution, deallocate any memory allocated
|
|
using the completion handler's associated allocator.
|
|
|
|
[std_note The implementation may perform operating system or underlying API
|
|
calls that perform memory allocations not using the associated allocator.
|
|
Invocations of the allocator functions may not introduce data races (See C++Std
|
|
\[res.on.data.races\]).]
|
|
|
|
[heading Execution of completion handler on completion of asynchronous operation]
|
|
|
|
Let `Args...` be the argument types of the completion signature `Signature` and
|
|
let [^['N]] be `sizeof...(Args)`. Let [^['i]] be in the range [half_open_range
|
|
`0`,[^['N]]]. Let [^T[sub ['i]]] be the [^['i]]th type in `Args...` and let
|
|
[^t[sub ['i]]] be the [^['i]]th completion handler argument associated with
|
|
[^T[sub ['i]]].
|
|
|
|
Let `f` be a function object, callable as `f()`, that invokes
|
|
`completion_handler` as if by [^completion_handler(forward<T[sub ['0]]>(t[sub
|
|
['0]]), ..., forward<T[sub ['N-1]]>(t[sub ['N-1]]))].
|
|
|
|
If an asynchonous operation completes immediately (that is, within the thread
|
|
of execution calling the initiating function, and before the initiating
|
|
function returns), the completion handler shall be submitted for execution as
|
|
if by performing `ex2.post(std::move(f), alloc2)`. Otherwise, the completion
|
|
handler shall be submitted for execution as if by performing
|
|
`ex2.dispatch(std::move(f), alloc2)`.
|
|
|
|
[heading Completion handlers and exceptions]
|
|
|
|
Completion handlers are permitted to throw exceptions. The effect of any
|
|
exception propagated from the execution of a completion handler is determined
|
|
by the executor which is executing the completion handler.
|
|
|
|
[heading Default completion tokens]
|
|
|
|
Every I/O executor type has an associated default completion token type. This is
|
|
specified via the `default_completion_token` trait. This trait may be used in
|
|
asynchronous operation declarations as follows:
|
|
|
|
template <
|
|
typename IoObject,
|
|
typename CompletionToken =
|
|
typename default_completion_token<
|
|
typename IoObject::executor_type
|
|
>::type
|
|
>
|
|
auto async_xyz(
|
|
IoObject& io_object,
|
|
CompletionToken&& token =
|
|
typename default_completion_token<
|
|
typename IoObject::executor_type
|
|
>::type{}
|
|
);
|
|
|
|
If not specialised, this trait type is `void`, meaning no default completion
|
|
token type is available for the given I/O executor.
|
|
|
|
\[['Example:] The `default_completion_token` trait is specialised for the
|
|
`use_awaitable` completion token so that it may be used as shown in the
|
|
following example:
|
|
|
|
auto socket = use_awaitable.as_default_on(tcp::socket(my_context));
|
|
// ...
|
|
co_await socket.async_connect(my_endpoint); // Defaults to use_awaitable.
|
|
|
|
In this example, the type of the `socket` object is transformed from
|
|
`tcp::socket` to have an I/O executor with the default completion token set to
|
|
`use_awaitable`.
|
|
|
|
Alternatively, the socket type may be computed directly:
|
|
|
|
using tcp_socket = use_awaitable_t<>::as_default_on_t<tcp::socket>;
|
|
tcp_socket socket(my_context);
|
|
// ...
|
|
co_await socket.async_connect(my_endpoint); // Defaults to use_awaitable.
|
|
|
|
'''—'''['end example]\]
|
|
|
|
[endsect]
|