32 Thread support library [thread]

32.9 Futures [futures]

32.9.1 Overview [futures.overview]

[futures] describes components that a C++ program can use to retrieve in one thread the result (value or exception) from a function that has run in the same thread or another thread.
Note
:
These components are not restricted to multi-threaded programs but can be useful in single-threaded programs as well.
— end note
 ]

32.9.2 Header <future> synopsis [future.syn]

namespace std {
  enum class future_errc {
    broken_promise = implementation-defined,
    future_already_retrieved = implementation-defined,
    promise_already_satisfied = implementation-defined,
    no_state = implementation-defined
  };

  enum class launch : unspecified {
    async = unspecified,
    deferred = unspecified,
    implementation-defined
  };

  enum class future_status {
    ready,
    timeout,
    deferred
  };

  template<> struct is_error_code_enum<future_errc> : public true_type { };
  error_code make_error_code(future_errc e) noexcept;
  error_condition make_error_condition(future_errc e) noexcept;

  const error_category& future_category() noexcept;

  class future_error;

  template<class R> class promise;
  template<class R> class promise<R&>;
  template<> class promise<void>;

  template<class R>
    void swap(promise<R>& x, promise<R>& y) noexcept;

  template<class R, class Alloc>
    struct uses_allocator<promise<R>, Alloc>;

  template<class R> class future;
  template<class R> class future<R&>;
  template<> class future<void>;

  template<class R> class shared_future;
  template<class R> class shared_future<R&>;
  template<> class shared_future<void>;

  template<class> class packaged_task;  // not defined
  template<class R, class... ArgTypes>
    class packaged_task<R(ArgTypes...)>;

  template<class R, class... ArgTypes>
    void swap(packaged_task<R(ArgTypes...)>&, packaged_task<R(ArgTypes...)>&) noexcept;

  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);
}
The enum type launch is a bitmask type with elements launch​::​async and launch​::​deferred.
Note
:
Implementations can provide bitmasks to specify restrictions on task interaction by functions launched by async() applicable to a corresponding subset of available launch policies.
Implementations can extend the behavior of the first overload of async() by adding their extensions to the launch policy under the “as if” rule.
— end note
 ]
The enum values of future_­errc are distinct and not zero.

32.9.3 Error handling [futures.errors]

const error_category& future_category() noexcept;
Returns: A reference to an object of a type derived from class error_­category.
The object's default_­error_­condition and equivalent virtual functions shall behave as specified for the class error_­category.
The object's name virtual function returns a pointer to the string "future".
error_code make_error_code(future_errc e) noexcept;
Returns: error_­code(static_­cast<int>(e), future_­category()).
error_condition make_error_condition(future_errc e) noexcept;
Returns: error_­condition(static_­cast<int>(e), future_­category()).

32.9.4 Class future_­error [futures.future.error]

namespace std {
  class future_error : public logic_error {
  public:
    explicit future_error(future_errc e);

    const error_code& code() const noexcept;
    const char*       what() const noexcept;

  private:
    error_code ec_;             // exposition only
  };
}
explicit future_error(future_errc e);
Effects: Initializes ec_­ with make_­error_­code(e).
const error_code& code() const noexcept;
Returns: ec_­.
const char* what() const noexcept;
Returns: An ntbs incorporating code().message().

32.9.5 Shared state [futures.state]

Many of the classes introduced in subclause [futures] use some state to communicate results.
This shared state consists of some state information and some (possibly not yet evaluated) result, which can be a (possibly void) value or an exception.
Note
:
Futures, promises, and tasks defined in this clause reference such shared state.
— end note
 ]
Note
:
The result can be any kind of object including a function to compute that result, as used by async when policy is launch​::​deferred.
— end note
 ]
An asynchronous return object is an object that reads results from a shared state.
A waiting function of an asynchronous return object is one that potentially blocks to wait for the shared state to be made ready.
If a waiting function can return before the state is made ready because of a timeout ([thread.req.lockable]), then it is a timed waiting function, otherwise it is a non-timed waiting function.
An asynchronous provider is an object that provides a result to a shared state.
The result of a shared state is set by respective functions on the asynchronous provider.
Note
:
Such as promises or tasks.
— end note
 ]
The means of setting the result of a shared state is specified in the description of those classes and functions that create such a state object.
When an asynchronous return object or an asynchronous provider is said to release its shared state, it means:
  • if the return object or provider holds the last reference to its shared state, the shared state is destroyed; and
  • the return object or provider gives up its reference to its shared state; and
  • these actions will not block for the shared state to become ready, except that it may block if all of the following are true: the shared state was created by a call to std​::​async, the shared state is not yet ready, and this was the last reference to the shared state.
When an asynchronous provider is said to make its shared state ready, it means:
  • first, the provider marks its shared state as ready; and
  • second, the provider unblocks any execution agents waiting for its shared state to become ready.
When an asynchronous provider is said to abandon its shared state, it means:
  • first, if that state is not ready, the provider
    • stores an exception object of type future_­error with an error condition of broken_­promise within its shared state; and then
    • makes its shared state ready;
  • second, the provider releases its shared state.
A shared state is ready only if it holds a value or an exception ready for retrieval.
Waiting for a shared state to become ready may invoke code to compute the result on the waiting thread if so specified in the description of the class or function that creates the state object.
Calls to functions that successfully set the stored result of a shared state synchronize with calls to functions successfully detecting the ready state resulting from that setting.
The storage of the result (whether normal or exceptional) into the shared state synchronizes with the successful return from a call to a waiting function on the shared state.
Some functions (e.g., promise​::​set_­value_­at_­thread_­exit) delay making the shared state ready until the calling thread exits.
The destruction of each of that thread's objects with thread storage duration is sequenced before making that shared state ready.
Access to the result of the same shared state may conflict.
Note
:
This explicitly specifies that the result of the shared state is visible in the objects that reference this state in the sense of data race avoidance ([res.on.data.races]).
For example, concurrent accesses through references returned by shared_­future​::​get() ([futures.shared.future]) must either use read-only operations or provide additional synchronization.
— end note
 ]

32.9.6 Class template promise [futures.promise]

namespace std {
  template<class R>
  class promise {
  public:
    promise();
    template<class Allocator>
      promise(allocator_arg_t, const Allocator& a);
    promise(promise&& rhs) noexcept;
    promise(const promise&) = delete;
    ~promise();

    // assignment
    promise& operator=(promise&& rhs) noexcept;
    promise& operator=(const promise&) = delete;
    void swap(promise& other) noexcept;

    // retrieving the result
    future<R> get_future();

    // setting the result
    void set_value(see below);
    void set_exception(exception_ptr p);

    // setting the result with deferred notification
    void set_value_at_thread_exit(see below);
    void set_exception_at_thread_exit(exception_ptr p);
  };

  template<class R>
    void swap(promise<R>& x, promise<R>& y) noexcept;

  template<class R, class Alloc>
    struct uses_allocator<promise<R>, Alloc>;
}
The implementation provides the template promise and two specializations, promise<R&> and promise<void>.
These differ only in the argument type of the member functions set_­value and set_­value_­at_­thread_­exit, as set out in their descriptions, below.
The set_­value, set_­exception, set_­value_­at_­thread_­exit, and set_­exception_­at_­thread_­exit member functions behave as though they acquire a single mutex associated with the promise object while updating the promise object.
template<class R, class Alloc> struct uses_allocator<promise<R>, Alloc> : true_type { };
Preconditions: Alloc meets the Cpp17Allocator requirements (Table 36).
promise(); template<class Allocator> promise(allocator_arg_t, const Allocator& a);
Effects: Creates a shared state.
The second constructor uses the allocator a to allocate memory for the shared state.
promise(promise&& rhs) noexcept;
Effects: Transfers ownership of the shared state of rhs (if any) to the newly-constructed object.
Postconditions: rhs has no shared state.
~promise();
Effects: Abandons any shared state ([futures.state]).
promise& operator=(promise&& rhs) noexcept;
Effects: Abandons any shared state ([futures.state]) and then as if promise(std​::​move(rhs)).swap(*this).
Returns: *this.
void swap(promise& other) noexcept;
Effects: Exchanges the shared state of *this and other.
Postconditions: *this has the shared state (if any) that other had prior to the call to swap.
other has the shared state (if any) that *this had prior to the call to swap.
future<R> get_future();
Returns: A future<R> object with the same shared state as *this.
Synchronization: Calls to this function do not introduce data races ([intro.multithread]) with calls to set_­value, set_­exception, set_­value_­at_­thread_­exit, or set_­exception_­at_­thread_­exit.
Note
:
Such calls need not synchronize with each other.
— end note
 ]
Throws: future_­error if *this has no shared state or if get_­future has already been called on a promise with the same shared state as *this.
Error conditions:
  • future_­already_­retrieved if get_­future has already been called on a promise with the same shared state as *this.
  • no_­state if *this has no shared state.
void promise::set_value(const R& r); void promise::set_value(R&& r); void promise<R&>::set_value(R& r); void promise<void>::set_value();
Effects: Atomically stores the value r in the shared state and makes that state ready ([futures.state]).
Throws:
  • future_­error if its shared state already has a stored value or exception, or
  • for the first version, any exception thrown by the constructor selected to copy an object of R, or
  • for the second version, any exception thrown by the constructor selected to move an object of R.
Error conditions:
  • promise_­already_­satisfied if its shared state already has a stored value or exception.
  • no_­state if *this has no shared state.
void set_exception(exception_ptr p);
Preconditions: p is not null.
Effects: Atomically stores the exception pointer p in the shared state and makes that state ready ([futures.state]).
Throws: future_­error if its shared state already has a stored value or exception.
Error conditions:
  • promise_­already_­satisfied if its shared state already has a stored value or exception.
  • no_­state if *this has no shared state.
void promise::set_value_at_thread_exit(const R& r); void promise::set_value_at_thread_exit(R&& r); void promise<R&>::set_value_at_thread_exit(R& r); void promise<void>::set_value_at_thread_exit();
Effects: Stores the value r in the shared state without making that state ready immediately.
Schedules that state to be made ready when the current thread exits, after all objects of thread storage duration associated with the current thread have been destroyed.
Throws:
  • future_­error if its shared state already has a stored value or exception, or
  • for the first version, any exception thrown by the constructor selected to copy an object of R, or
  • for the second version, any exception thrown by the constructor selected to move an object of R.
Error conditions:
  • promise_­already_­satisfied if its shared state already has a stored value or exception.
  • no_­state if *this has no shared state.
void set_exception_at_thread_exit(exception_ptr p);
Preconditions: p is not null.
Effects: Stores the exception pointer p in the shared state without making that state ready immediately.
Schedules that state to be made ready when the current thread exits, after all objects of thread storage duration associated with the current thread have been destroyed.
Throws: future_­error if an error condition occurs.
Error conditions:
  • promise_­already_­satisfied if its shared state already has a stored value or exception.
  • no_­state if *this has no shared state.
template<class R> void swap(promise<R>& x, promise<R>& y) noexcept;
Effects: As if by x.swap(y).

32.9.7 Class template future [futures.unique.future]

The class template future defines a type for asynchronous return objects which do not share their shared state with other asynchronous return objects.
A default-constructed future object has no shared state.
A future object with shared state can be created by functions on asynchronous providers or by the move constructor and shares its shared state with the original asynchronous provider.
The result (value or exception) of a future object can be set by calling a respective function on an object that shares the same shared state.
Note
:
Member functions of future do not synchronize with themselves or with member functions of shared_­future.
— end note
 ]
The effect of calling any member function other than the destructor, the move-assignment operator, share, or valid on a future object for which valid() == false is undefined.
Note
:
It is valid to move from a future object for which valid() == false.
— end note
 ]
Note
:
Implementations should detect this case and throw an object of type future_­error with an error condition of future_­errc​::​no_­state.
— end note
 ]
namespace std {
  template<class R>
  class future {
  public:
    future() noexcept;
    future(future&&) noexcept;
    future(const future&) = delete;
    ~future();
    future& operator=(const future&) = delete;
    future& operator=(future&&) noexcept;
    shared_future<R> share() noexcept;

    // retrieving the value
    see below get();

    // functions to check state
    bool valid() const noexcept;

    void wait() const;
    template<class Rep, class Period>
      future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
    template<class Clock, class Duration>
      future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
  };
}
The implementation provides the template future and two specializations, future<R&> and future<void>.
These differ only in the return type and return value of the member function get, as set out in its description, below.
future() noexcept;
Effects: The object does not refer to a shared state.
Postconditions: valid() == false.
future(future&& rhs) noexcept;
Effects: Move constructs a future object that refers to the shared state that was originally referred to by rhs (if any).
Postconditions:
  • valid() returns the same value as rhs.valid() prior to the constructor invocation.
  • rhs.valid() == false.
~future();
Effects:
future& operator=(future&& rhs) noexcept;
Effects:
Postconditions:
  • valid() returns the same value as rhs.valid() prior to the assignment.
  • rhs.valid() == false.
shared_future<R> share() noexcept;
Returns: shared_­future<R>(std​::​move(*this)).
Postconditions: valid() == false.
R future::get(); R& future<R&>::get(); void future<void>::get();
Note
:
As described above, the template and its two required specializations differ only in the return type and return value of the member function get.
— end note
 ]
Effects:
  • wait()s until the shared state is ready, then retrieves the value stored in the shared state;
  • releases any shared state ([futures.state]).
Returns:
  • future​::​get() returns the value v stored in the object's shared state as std​::​move(v).
  • future<R&>​::​get() returns the reference stored as value in the object's shared state.
  • future<void>​::​get() returns nothing.
Throws: The stored exception, if an exception was stored in the shared state.
Postconditions: valid() == false.
bool valid() const noexcept;
Returns: true only if *this refers to a shared state.
void wait() const;
Effects: Blocks until the shared state is ready.
template<class Rep, class Period> future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
Effects: None if the shared state contains a deferred function ([futures.async]), otherwise blocks until the shared state is ready or until the relative timeout ([thread.req.timing]) specified by rel_­time has expired.
Returns:
  • future_­status​::​deferred if the shared state contains a deferred function.
  • future_­status​::​ready if the shared state is ready.
  • future_­status​::​timeout if the function is returning because the relative timeout ([thread.req.timing]) specified by rel_­time has expired.
Throws: timeout-related exceptions ([thread.req.timing]).
template<class Clock, class Duration> future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
Effects: None if the shared state contains a deferred function ([futures.async]), otherwise blocks until the shared state is ready or until the absolute timeout ([thread.req.timing]) specified by abs_­time has expired.
Returns:
  • future_­status​::​deferred if the shared state contains a deferred function.
  • future_­status​::​ready if the shared state is ready.
  • future_­status​::​timeout if the function is returning because the absolute timeout ([thread.req.timing]) specified by abs_­time has expired.
Throws: timeout-related exceptions ([thread.req.timing]).

32.9.8 Class template shared_­future [futures.shared.future]

The class template shared_­future defines a type for asynchronous return objects which may share their shared state with other asynchronous return objects.
A default-constructed shared_­future object has no shared state.
A shared_­future object with shared state can be created by conversion from a future object and shares its shared state with the original asynchronous provider of the shared state.
The result (value or exception) of a shared_­future object can be set by calling a respective function on an object that shares the same shared state.
Note
:
Member functions of shared_­future do not synchronize with themselves, but they synchronize with the shared state.
— end note
 ]
The effect of calling any member function other than the destructor, the move-assignment operator, the copy-assignment operator, or valid() on a shared_­future object for which valid() == false is undefined.
Note
:
It is valid to copy or move from a shared_­future object for which valid() is false.
— end note
 ]
Note
:
Implementations should detect this case and throw an object of type future_­error with an error condition of future_­errc​::​no_­state.
— end note
 ]
namespace std {
  template<class R>
  class shared_future {
  public:
    shared_future() noexcept;
    shared_future(const shared_future& rhs) noexcept;
    shared_future(future<R>&&) noexcept;
    shared_future(shared_future&& rhs) noexcept;
    ~shared_future();
    shared_future& operator=(const shared_future& rhs) noexcept;
    shared_future& operator=(shared_future&& rhs) noexcept;

    // retrieving the value
    see below get() const;

    // functions to check state
    bool valid() const noexcept;

    void wait() const;
    template<class Rep, class Period>
      future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
    template<class Clock, class Duration>
      future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
  };
}
The implementation provides the template shared_­future and two specializations, shared_­future<R&> and shared_­future<void>.
These differ only in the return type and return value of the member function get, as set out in its description, below.
shared_future() noexcept;
Effects: The object does not refer to a shared state.
Postconditions: valid() == false.
shared_future(const shared_future& rhs) noexcept;
Effects: The object refers to the same shared state as rhs (if any).
Postconditions: valid() returns the same value as rhs.valid().
shared_future(future<R>&& rhs) noexcept; shared_future(shared_future&& rhs) noexcept;
Effects: Move constructs a shared_­future object that refers to the shared state that was originally referred to by rhs (if any).
Postconditions:
  • valid() returns the same value as rhs.valid() returned prior to the constructor invocation.
  • rhs.valid() == false.
~shared_future();
Effects:
shared_future& operator=(shared_future&& rhs) noexcept;
Effects:
Postconditions:
  • valid() returns the same value as rhs.valid() returned prior to the assignment.
  • rhs.valid() == false.
shared_future& operator=(const shared_future& rhs) noexcept;
Effects:
  • Releases any shared state ([futures.state]);
  • assigns the contents of rhs to *this.
    Note
    : As a result, *this refers to the same shared state as rhs (if any). — end note
     ]
Postconditions: valid() == rhs.valid().
const R& shared_future::get() const; R& shared_future<R&>::get() const; void shared_future<void>::get() const;
Note
:
As described above, the template and its two required specializations differ only in the return type and return value of the member function get.
— end note
 ]
Note
:
Access to a value object stored in the shared state is unsynchronized, so programmers should apply only those operations on R that do not introduce a data race ([intro.multithread]).
— end note
 ]
Effects: wait()s until the shared state is ready, then retrieves the value stored in the shared state.
Returns:
  • shared_­future​::​get() returns a const reference to the value stored in the object's shared state.
    Note
    : Access through that reference after the shared state has been destroyed produces undefined behavior; this can be avoided by not storing the reference in any storage with a greater lifetime than the shared_­future object that returned the reference. — end note
     ]
  • shared_­future<R&>​::​get() returns the reference stored as value in the object's shared state.
  • shared_­future<void>​::​get() returns nothing.
Throws: The stored exception, if an exception was stored in the shared state.
bool valid() const noexcept;
Returns: true only if *this refers to a shared state.
void wait() const;
Effects: Blocks until the shared state is ready.
template<class Rep, class Period> future_status wait_for(const chrono::duration<Rep, Period>& rel_time) const;
Effects: None if the shared state contains a deferred function ([futures.async]), otherwise blocks until the shared state is ready or until the relative timeout ([thread.req.timing]) specified by rel_­time has expired.
Returns:
  • future_­status​::​deferred if the shared state contains a deferred function.
  • future_­status​::​ready if the shared state is ready.
  • future_­status​::​timeout if the function is returning because the relative timeout ([thread.req.timing]) specified by rel_­time has expired.
Throws: timeout-related exceptions ([thread.req.timing]).
template<class Clock, class Duration> future_status wait_until(const chrono::time_point<Clock, Duration>& abs_time) const;
Effects: None if the shared state contains a deferred function ([futures.async]), otherwise blocks until the shared state is ready or until the absolute timeout ([thread.req.timing]) specified by abs_­time has expired.
Returns:
  • future_­status​::​deferred if the shared state contains a deferred function.
  • future_­status​::​ready if the shared state is ready.
  • future_­status​::​timeout if the function is returning because the absolute timeout ([thread.req.timing]) specified by abs_­time has expired.
Throws: timeout-related exceptions ([thread.req.timing]).

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
 ]

32.9.10 Class template packaged_­task [futures.task]

The class template packaged_­task defines a type for wrapping a function or callable object so that the return value of the function or callable object is stored in a future when it is invoked.
When the packaged_­task object is invoked, its stored task is invoked and the result (whether normal or exceptional) stored in the shared state.
Any futures that share the shared state will then be able to access the stored result.
namespace std {
  template<class> class packaged_task;  // not defined

  template<class R, class... ArgTypes>
  class packaged_task<R(ArgTypes...)> {
  public:
    // construction and destruction
    packaged_task() noexcept;
    template<class F>
      explicit packaged_task(F&& f);
    ~packaged_task();

    // no copy
    packaged_task(const packaged_task&) = delete;
    packaged_task& operator=(const packaged_task&) = delete;

    // move support
    packaged_task(packaged_task&& rhs) noexcept;
    packaged_task& operator=(packaged_task&& rhs) noexcept;
    void swap(packaged_task& other) noexcept;

    bool valid() const noexcept;

    // result retrieval
    future<R> get_future();

    // execution
    void operator()(ArgTypes... );
    void make_ready_at_thread_exit(ArgTypes...);

    void reset();
  };

  template<class R, class... ArgTypes>
    void swap(packaged_task<R(ArgTypes...)>& x, packaged_task<R(ArgTypes...)>& y) noexcept;
}

32.9.10.1 Member functions [futures.task.members]

packaged_task() noexcept;
Effects: The object has no shared state and no stored task.
template<class F> packaged_task(F&& f);
Constraints: remove_­cvref_­t<F> is not the same type as packaged_­task<R(ArgTypes...)>.
Mandates: is_­invocable_­r_­v<R, F&, ArgTypes...> is true.
Preconditions: Invoking a copy of f behaves the same as invoking f.
Effects: Constructs a new packaged_­task object with a shared state and initializes the object's stored task with std​::​forward<F>(f).
Throws: Any exceptions thrown by the copy or move constructor of f, or bad_­alloc if memory for the internal data structures could not be allocated.
packaged_task(packaged_task&& rhs) noexcept;
Effects: Transfers ownership of rhs's shared state to *this, leaving rhs with no shared state.
Moves the stored task from rhs to *this.
Postconditions: rhs has no shared state.
packaged_task& operator=(packaged_task&& rhs) noexcept;
Effects:
~packaged_task();
Effects: Abandons any shared state ([futures.state]).
void swap(packaged_task& other) noexcept;
Effects: Exchanges the shared states and stored tasks of *this and other.
Postconditions: *this has the same shared state and stored task (if any) as other prior to the call to swap.
other has the same shared state and stored task (if any) as *this prior to the call to swap.
bool valid() const noexcept;
Returns: true only if *this has a shared state.
future<R> get_future();
Returns: A future object that shares the same shared state as *this.
Synchronization: Calls to this function do not introduce data races ([intro.multithread]) with calls to operator() or make_­ready_­at_­thread_­exit.
Note
:
Such calls need not synchronize with each other.
— end note
 ]
Throws: A future_­error object if an error occurs.
Error conditions:
  • future_­already_­retrieved if get_­future has already been called on a packaged_­task object with the same shared state as *this.
  • no_­state if *this has no shared state.
void operator()(ArgTypes... args);
Effects: As if by INVOKE<R>(f, t, t, , t) ([func.require]), where f is the stored task of *this and t, t, , t are the values in args....
If the task returns normally, the return value is stored as the asynchronous result in the shared state of *this, otherwise the exception thrown by the task is stored.
The shared state of *this is made ready, and any threads blocked in a function waiting for the shared state of *this to become ready are unblocked.
Throws: A future_­error exception object if there is no shared state or the stored task has already been invoked.
Error conditions:
  • promise_­already_­satisfied if the stored task has already been invoked.
  • no_­state if *this has no shared state.
void make_ready_at_thread_exit(ArgTypes... args);
Effects: As if by INVOKE<R>(f, t, t, , t) ([func.require]), where f is the stored task and t, t, , t are the values in args....
If the task returns normally, the return value is stored as the asynchronous result in the shared state of *this, otherwise the exception thrown by the task is stored.
In either case, this is done without making that state ready ([futures.state]) immediately.
Schedules the shared state to be made ready when the current thread exits, after all objects of thread storage duration associated with the current thread have been destroyed.
Throws: future_­error if an error condition occurs.
Error conditions:
  • promise_­already_­satisfied if the stored task has already been invoked.
  • no_­state if *this has no shared state.
void reset();
Effects: As if *this = packaged_­task(std​::​move(f)), where f is the task stored in *this.
Note
:
This constructs a new shared state for *this.
The old state is abandoned ([futures.state]).
— end note
 ]
Throws:
  • bad_­alloc if memory for the new shared state could not be allocated.
  • any exception thrown by the move constructor of the task stored in the shared state.
  • future_­error with an error condition of no_­state if *this has no shared state.

32.9.10.2 Globals [futures.task.nonmembers]

template<class R, class... ArgTypes> void swap(packaged_task<R(ArgTypes...)>& x, packaged_task<R(ArgTypes...)>& y) noexcept;
Effects: As if by x.swap(y).