32 Thread support library [thread]

32.9 Futures [futures]

32.9.9 Function template async [futures.async]

The function template async provides a mechanism to launch a function potentially in a new thread and provides the result of the function in a future object with which it shares a shared state.
template<class F, class... Args> [[nodiscard]] future<invoke_result_t<decay_t<F>, decay_t<Args>...>> async(F&& f, Args&&... args); template<class F, class... Args> [[nodiscard]] future<invoke_result_t<decay_t<F>, decay_t<Args>...>> async(launch policy, F&& f, Args&&... args);
Mandates: The following are all true:
  • is_­constructible_­v<decay_­t<F>, F>,
  • (is_­constructible_­v<decay_­t<Args>, Args> &&...),
  • is_­move_­constructible_­v<decay_­t<F>>,
  • (is_­move_­constructible_­v<decay_­t<Args>> &&...), and
  • is_­invocable_­v<decay_­t<F>, decay_­t<Args>...>.
Preconditions: decay_­t<F> and each type in decay_­t<Args> meet the Cpp17MoveConstructible requirements.
Effects: The first function behaves the same as a call to the second function with a policy argument of launch​::​async | launch​::​deferred and the same arguments for F and Args.
The second function creates a shared state that is associated with the returned future object.
The further behavior of the second function depends on the policy argument as follows (if more than one of these conditions applies, the implementation may choose any of the corresponding policies):
  • If launch​::​async is set in policy, calls invoke(decay-copy(std​::​forward<F>(f)), decay-​copy(std​::​forward<Args>(args))...) ([func.require], [thread.thread.constr]) as if in a new thread of execution represented by a thread object with the calls to decay-copy being evaluated in the thread that called async. Any return value is stored as the result in the shared state. Any exception propagated from the execution of invoke(decay-copy(std​::​forward<F>(f)), decay-copy(std​::​forward​<Args>(args))...) is stored as the exceptional result in the shared state. The thread object is stored in the shared state and affects the behavior of any asynchronous return objects that reference that state.
  • If launch​::​deferred is set in policy, stores decay-copy(std​::​forward<F>(f)) and decay-​copy(std​::​forward<Args>(args))... in the shared state. These copies of f and args constitute a deferred function. Invocation of the deferred function evaluates invoke(std​::​move(g), std​::​move(xyz)) where g is the stored value of decay-copy(std​::​forward<F>(f)) and xyz is the stored copy of decay-copy(std​::​forward<Args>(args)).... Any return value is stored as the result in the shared state. Any exception propagated from the execution of the deferred function is stored as the exceptional result in the shared state. The shared state is not made ready until the function has completed. The first call to a non-timed waiting function ([futures.state]) on an asynchronous return object referring to this shared state invokes the deferred function in the thread that called the waiting function. Once evaluation of invoke(std​::​move(g), std​::​move(xyz)) begins, the function is no longer considered deferred.
    [Note
    : If this policy is specified together with other policies, such as when using a policy value of launch​::​async | launch​::​deferred, implementations should defer invocation or the selection of the policy when no more concurrency can be effectively exploited. — end note
    ]
  • If no value is set in the launch policy, or a value is set that is neither specified in this document nor by the implementation, the behavior is undefined.
Returns: An object of type future<invoke_­result_­t<decay_­t<F>, decay_­t<Args>...>> that refers to the shared state created by this call to async.
[Note
:
If a future obtained from async is moved outside the local scope, other code that uses the future should be aware that the future's destructor can block for the shared state to become ready.
— end note
]
Synchronization: Regardless of the provided policy argument,
  • the invocation of async synchronizes with the invocation of f.
    [Note
    : This statement applies even when the corresponding future object is moved to another thread. — end note
    ]
    ; and
  • the completion of the function f is sequenced before ([intro.multithread]) the shared state is made ready.
    [Note
    : f might not be called at all, so its completion might never happen. — end note
    ]
If the implementation chooses the launch​::​async policy,
  • a call to a waiting function on an asynchronous return object that shares the shared state created by this async call shall block until the associated thread has completed, as if joined, or else time out ([thread.thread.member]);
  • the associated thread completion synchronizes with the return from the first function that successfully detects the ready status of the shared state or with the return from the last function that releases the shared state, whichever happens first.
Throws: system_­error if policy == launch​::​async and the implementation is unable to start a new thread, or std​::​bad_­alloc if memory for the internal data structures could not be allocated.
Error conditions:
  • resource_­unavailable_­try_­again — if policy == launch​::​async and the system is unable to start a new thread.
[Example
:
int work1(int value);
int work2(int value);
int work(int value) {
  auto handle = std::async([=]{ return work2(value); });
  int tmp = work1(value);
  return tmp + handle.get();    // #1
}
[Note
:
Line #1 might not result in concurrency because the async call uses the default policy, which may use launch​::​deferred, in which case the lambda might not be invoked until the get() call; in that case, work1 and work2 are called on the same thread and there is no concurrency.
— end note
]
— end example
]