Jeremy

<future>

std::promise (C++11)

template< class R > class promise Base template. (c++11)
template< class R > class promise<R&> Non-void specialization, used to communicate objects between threds. (c++11)
template<> class promise void specialization, used to communicate stateless events. (c++11)

The class template std::promise provides a facility to store a value or an exception that is later aquired asynchronously via a std::future object created by the std::promise object. Note that the std::promise object is meant to be used only once.

Each promise is associated with a shared state, which contains some state information and a result which may be not yet evauated, evalated to a value (possibly void) or evaluated to an exception. A promise may do three things with the shared state:

- make ready: the promise stores the result or the exception in the shared state. Marks the state ready and unblocks any thread waiting on a future associated with the shared state.

- release: the promise gives up its reference to the shared state. If this was the last such reference, the shared stateis destoryed. Unless this was a shared state created by `std::async` which is not yet ready, this operation does not block.

- abandon: the promise stores the exception of type `std::future_error` with error code `std::future_errc::broken_promise`, makes the shared state ready, and then releases it.

The promise is the “push” end of the promise-future communication channel: the operation that stores a value in the shared state synchronizes-with (as defined in std::future::get). Concurrent access to the same shared state may conflict otherwise: for example multiple callers of std::shared_future::get must either all be read-only or provide external synchronization.

Member functions

Member functions Definition
(constructor) constructs the promise object
(destructor) destructs the promise object
operator= assigns the shared state
swap swaps two promise objects
get_future returns a future associated with the promised result
set_value sets the result to specific value
set_value_at_thread_exit sets the result to specific value while delivering the notification only at thread exit
set_exception sets the result to indicate an exception
set_exception_at_thread_exit sets the result to indicate an exception while delivering the notification only at thread exit

Non-member functions

std::swap (c++11) specializes the std::swap algorithm

Helper classes

std::uses_allocator (c++11) specializes the std::uses_allocator type trait

Example

#include <chrono>
#include <future>
#include <iostream>
#include <numeric>
#include <thread>
#include <vector>

void accumulate(
  std::vector<int>::iterator first,
  std::vector<int>::iterator last,
  std::promise<int> accumulate_promise
) {
  int sum = std::accumulate(first, last, 0);
  accumulate_promise.set_value(sum); // Notify future
}

void do_work(std::promise<void> barrier) {
  std::this_thread::sleep_for(std::chrono:seconds(1));
  barrier.set_value();
}

int main() {
  // Demonstrate using promise<int> to transmit a result between threads.
  std::vector<int> numbers = {1, 2, 3, 4, 5, 6};
  std::promise<int> accumulate_promise;
  std::future<int> accumulate_future = accumulate_promise.get_future();
  std::thread work_thread(accumulate, numbers.begin(), numbers.end(), std::move(accumulate_promise));

  // future::get() will wait until the future has a avalid result and retreives it.
  // Calling wait() before get() is not needed
  // accumuate_future.wait(); // wait for result
  std::cout << "result=" << accumulate_future.get() << "\n";
  work_thread.join(); // wait for thread completion

  // Demonstrate using promise<void> o signal state between threads.
  std::promise<void> barrier;
  std::future<void> barrier_future = barrier.getfuture();
  std::thread new_work_thread(do_work, std::move(barrier));
  barrier_future.wait();
  new_work_thread.join();
}

std::packaged_task (C++11)

// since C++11
template< class >
class packaged_task;

// since C++11
template< class R, class ...ArgTypes >
class packaged_task<R(ArgTypes...)>;

The class template std::packaged_task wraps any Callable target (function, lambda expression, bind expression, or another function object) so that it can be invoked asynchronously. Its return value or exception thrown is stored in a shared state which can be accessed through std::future objects.

Just like std::function, std::packaged_task is a polymorphic, allocator-aware container: the stored callable target may be allocated on heap or with a provided allocator. (until C++17)

Member functions

Function Definition
(constructor) constructs the task object
(destructor) destructs the task object
operator= moves the task object
valid checks if the task object has a valid function
swap swaps two task objects
get_future returns a std::future associated with the promised result
operator() executes the function
make_ready_at_thread_exit executes the function ensuring that the result is ready only once the current thread exits
reset resets the state abandoning any stored results of previous executions

Non-member functions

Function Definition
swap specializes the std::swap algorithm

Example

#include <cmath>
#include <functional>
#include <future>
#include <iostream>
#include <thread>

int f(int x, int y) { return std::pow(x, y); }

void task_lambda(){
  std::packaged_task<int(int, int)> task([](int a, int b) {
    return std::pow(a, b);
  });
  std::future<int> result = task.get_future();

  task(2, 9);

  std::cout << "task_lambda:\t" << result.get() << "\n";

}

void task_bind(){
  std::packaged_task<int()> task(std::bind(f, 2, 11));
  std::future<int> result = task.get_future();

  task();

  std::cout << "task_bind:\t" << result.get() << "\n";
}

void task_thread() {
  std::packaged_task<int(int, int)> task(f);
  std::future<int> result = task.get_future();

  std::thread task_td(std::move(task), 2, 10);
  task_td.join();

  std::cout << "task_thread:\t" << result.get() << '\n';
}

int main() {
  task_lambda();
  task_bind();
  task_thread();
}

/**
 * Output
 * task_lambda:    512
   task_bind:      2048
   task_thread:    1024
 */

std::future (C++11)

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

The class template std::future provides a mechanism to access the result of asynchronous operations:

- An asynchronous operation (created via std::async, std::packaged_task, or std::promise) can provide a std::future object to the creator of that asynchronous operation.

- The creator of the asynchronous operation can then use a variety of methods to query, wait for, or extract a value from the std::future. These methods may block if the asynchronous operation has not yet provided a value.

- When the asynchronous operation is ready to send a result to the creator, it can do so by modifying shared state (e.g. std::promise::set_value) that is linked to the creator's std::future.

Note that std::future references shared state that is not shared with any other asynchronous return objects (as opposed to std::shared_future).

  • Member functions
Function Definition
(constructor) constructs the future object
(destructor) destructs the future object
operator= moves the future object
share transfers the shared state from *this to a shared_future and returns it
get returns the result
valid checks if the future has a shared state
wait waits for the result to beconme available
wait_for waits for the result, returns if it is not available for the specified timeout duration
wait_until waits for the result, returns if it is not available until specified time point has been reached
  • Examples
#include <future>
#include <iostream>
#include <thread>

int main() {
  // future from a packaged_task
  std::packaged_task<int()> task([] { return 7; }); // wrap the function
  std::future<int> f1 = task.get_future();          // get a future
  std::thread t(std::move(task));                   // launch on a thread

  // future from an async()
  std::future<int> f2 = std::async(std::launch::async, [] { return 8; });

  // future from a promise
  std::promise<int> p;
  std::future<int> f3 = p.get_future();
  std::thread([&p] { p.set_value_at_thread_exit(9); }).detach();

  std::cout << "Waiting..." << std::flush;
  f1.wait();
  f2.wait();
  f3.wait();
  std::cout << "Done!\nResults are: " << f1.get() << ' ' << f2.get() << ' '
            << f3.get() << "\n";
  t.join();
}

/**
 * Output
 * Waiting...Done!
   Results are: 7 8 9
 */
```

- Example with exceptions

```cpp
#include <future>
#include <iostream>
#include <thread>

int main() {
  std::promise<int> p;
  std::future<int> f = p.get_future();

  std::thread t ([&p] {
    try {
      throw std::runtime_error("Example");
    } catch(...) {
      try {
        p.set_exception(std::current_exception());
      } catch (...) {} // set_exception() may throw too
    }
  });

  try {
    std::cout << f.get();
  } catch (const std::exception& e) {
    std:: cout << "exception from the thread: " << e.what() << "\n";
  }

  t.join();
}

// exception from the thread: Example

std::async

// since C++11
template< class F, class... Args >
std::future</* see below */> async( F&& f, Args&&... args );

// since C++11
template< class F, class... Args >
std::future</* see below */> async( std::launch policy,
                                    F&& f, Args&&... args );

The function template std::async runs the function f asynchronously (potentially in a separate thread which might be a part of a thread pool) and returns a std::future that will eventually hold the result of that function call.

- Behaves as if (2) is called with policy being std::launch::async | std::launch::deferred.

- Calls a function f with arguments args according to a specific launch policy policy (see below).

The return type of std::async is std::future, where V is:

// until C++17
typename std::result_of<typename std::decay<F>::type(
                        typename std::decay<Args::type...)>::type.

// since C++17
std::invole_result_t<std::Decay_t<F>, std::decay_t<Args>...>.

If any of the following conditions is satisfied, the program is ill-formed: (until C++20)

- F is not MoveConstructible.

- Any type in Args is not MoveConstructible.

- INVOKE(decay-copy(std::forward<F>(f)),
   decay-copy(std::forward<Args>(args))...) is not a valid expression.

If any of the following is false, the program is ill-formed: (since C++20)

- std::is_constructible_v<std::decay_t<F>, F>

- (std::is_constructible_v<std::decay_t<Args>, Args> && ...)

- std::is_invocable_v<std::decay_t<F>, std::decay_t<Args>...>

The call to std::async synchronizes with the call to f, and the completion of f is sequenced before making the shared state ready.

  • Parameters

    • f: Callable object to call

    • args: parameters to pass to f

    • policy: bitmask value, where individual bits control the allowed methods of execution

  • Return value

std::future referring to the shared state created by this call to std::async.

  • Deferred invocation

If the deferred flag is set (i.e. (policy & std::launch::deferred) != 0), then std::async stores

decay-copy(std::forward(f)) and decay-copy(std::forward(args))… in the shared state. (until C++23)

auto(std::forward(f)) and auto(std::forward(args))… in the shared state. (since C++23)

Lazy evaluation is performed:

- The first call to a non-timed wait function on the std::future that std::async returned to the caller will evaluate `INVOKE(std::move(g), std::move(xyz))` in the thread that called the waiting function (which does not have to be the thread that originally called std::async), where

`g` is the stored value of `decay-copy(std::forward<F>(f))` and (until C++23)

`xyz` is the stored copy of `decay-copy(std::forward<Args>(args))...` (until C++23)

`g` is the stored value of `auto(std::forward<F>(f))` and (since C++23)
`xyz` is the stored copy of `auto(std::forward<Args>(args))...`. (since C++23)

- The result or exception is placed in the shared state associated with the returned `std::future` and only then it is made ready. All further accesses to the same `std::future` will return the result immediately.
  • Exceptions

    • std::bad_alloc, if the memory for the internal data structures cannot be allocated, or

    • std::system_error with error condition std::errc::resource_unavailable_try_again, if policy == std::launch::async and the implementation is unable to start a new thread.

    If policy is std::launch::async | std::launch::deferred or has additional bits set, it will fall back to deferred invocation or the implementation-defined policies in this case.

  • Example

#include <algorithm>
#include <future>
#include <iostream>
#include <mutex>
#include <numeric>
#include <string>
#include <vector>

std::mutex m;

struct X {
  void foo(int i, const std::string &str) {
    std::lock_guard<std::mutex> lk(m);
    std::cout << str << ' ' << i << "\n";
  }

  void bar(const std::string &str) {
    std::lock_guard<std::mutex> lk(m);
    std::cout << str << "\n";
  }

  int operator()(int i) {
    std::lock_guard<std::mutex> lk(m);
    std::cout << i << "\n";
    return i + 10;
  }
};

template <typename RandomIt> int parallel_sum(RandomIt beg, RandomIt end) {
  auto len = end - beg;
  if (len < 1000)
    return std::accumulate(beg, end, 0);

  RandomIt mid = beg + len / 2;
  auto handle =
      std::async(std::launch::async, parallel_sum<RandomIt>, mid, end);
  int sum = parallel_sum(beg, mid);
  return sum + handle.get();
}

int main() {
  std::vector<int> v(10000, 1);
  std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << "\n";

  X x;

  // Calls (&x)->foo(42, "Hello") with default policy:
  // may print "Hello 42" concurrently or defer execution
  auto a1 = std::async(&X::foo, &x, 42, "Hello");
  // Calls x.bar("world!") with deferred policy
  // prints "world!" when a2.get() or a2.wait() is called
  auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!");
  // Calls X()(43); with async policy
  // prints "43" concurrently
  auto a3 = std::async(std::launch::async, X(), 43);
  a2.wait();
  std::cout << a3.get() << "\n";
} // if a1 is not done at this point, destructor of a1 prints "Hello 42" here

/**
 * Possible outout
 *
 * The sum is10000
   Hello 42
   world!
   43
   53
 */

std::shared_future

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

The class template std::shared_future provides a mechanism to access the result of asynchronous operations, similar to std::future, except that multiple threads are allowed to wait for the same shared state. Unlike std::future, which is only moveable (so only one instance can refer to any particular asynchronous result), std::shared_future is copyable and multiple shared future objects may refer to the same shared state.

Access to the same shared state from multiple threads is safe if each thread does it through its own copy of a shared_future object.

  • Member functions
Function Definition
(constructor) construsts the future object
(destructor) destructs the future object
operator= assigns the contents
get returns the reuslt
valid checks if the future has a shred state
wait waits for the result to become available
wait_for waits for the result, returns if it is not available for the specified timeout duration
wait_until wait for the result, returns if it is not available until specified time point has been reached
  • Example
#include <chrono>
#include <future>
#include <iostream>

int main() {
  std::promise<void> ready_promise, t1_ready_promise, t2_ready_promise;
  std::shared_future<void> ready_future(ready_promise.get_future());

  std::chrono::time_point<std::chrono::high_resolution_clock> start;

  auto fun1 = [&, ready_future]() -> std::chrono::duration<double, std::milli> {
    t1_ready_promise.set_value();
    ready_future.wait(); // waits for the signal from main()
    return std::chrono::high_resolution_clock::now() - start;
  };

  auto fun2 = [&, ready_future]() -> std::chrono::duration<double, std::milli> {
    t2_ready_promise.set_value();
    ready_future.wait(); // waits for the signal from main()
    return std::chrono::high_resolution_clock::now() - start;
  };

  auto fut1 = t1_ready_promise.get_future();
  auto fut2 = t2_ready_promise.get_future();

  auto result1 = std::async(std::launch::async, fun1);
  auto result2 = std::async(std::launch::async, fun2);

  // wait for the threads to become ready
  fut1.wait();
  fut2.wait();

  // the threads are ready, start the clock
  start = std::chrono::high_resolution_clock::now();

  // signal the threads to go
  ready_promise.set_value();

  std::cout << "Thread 1 received the signal " << result1.get().count()
            << " ms after start\n"
            << "Thread 2 received the signal " << result2.get().count()
            << " ms after start\n";
}

/**
 * Output data
 * Thread 1 received the signal 0.013674 ms after start
   Thread 2 received the signal 0.012266 ms after start
 */

std::future_error

class future_error; // since C++11
  • Member functions
Function Definition
(constructor) creates a std::future_error object
operator= replaces the std::future_error object
code returns the error code
what returns the explanatory string specific to the error code
  • Example
#include <future>
#include <iostream>

int main()
{
    std::future<int> empty;
    try
    {
        int n = empty.get(); // The behavior is undefined, but
                             // some implementations throw std::future_error
    }
    catch (const std::future_error& e)
    {
        std::cout << "Caught a future_error with code \"" << e.code()
                  << "\"\nMessage: \"" << e.what() << "\"\n";
    }
}

/**
 * Caught a future_error with code "future:3"
   Message: "No associated state"
 */

Reference

1.cppreference