What Is Std::Promise

What is std::promise?

In the words of [futures.state] a std::future is an asynchronous return object ("an object that reads results from a shared state") and a std::promise is an asynchronous provider ("an object that provides a result to a shared state") i.e. a promise is the thing that you set a result on, so that you can get it from the associated future.

The asynchronous provider is what initially creates the shared state that a future refers to. std::promise is one type of asynchronous provider, std::packaged_task is another, and the internal detail of std::async is another. Each of those can create a shared state and give you a std::future that shares that state, and can make the state ready.

std::async is a higher-level convenience utility that gives you an asynchronous result object and internally takes care of creating the asynchronous provider and making the shared state ready when the task completes. You could emulate it with a std::packaged_task (or std::bind and a std::promise) and a std::thread but it's safer and easier to use std::async.

std::promise is a bit lower-level, for when you want to pass an asynchronous result to the future, but the code that makes the result ready cannot be wrapped up in a single function suitable for passing to std::async. For example, you might have an array of several promises and associated futures and have a single thread which does several calculations and sets a result on each promise. async would only allow you to return a single result, to return several you would need to call async several times, which might waste resources.

Need help understanding how std::promise and std::future work in this case

Primise/future isn't a value that changes, it is a value that is delivered later.

If you want a value that changes, you can stich together one out of a mutex, condition variable and a bool. However, reasoning about a bool that changes more than once in a multithreaded environment will break most people's brains in my experience.

Better options are things like queues of data guarded by the above, and try pop or try pop all methods that get data off the queue if it is there.

template<class T>
struct threadsafe_queue{
std::optional<T> try_pop();
std::optional<T> wait_and_pop();
bool is_aborted() const;
std::deque<T> pop_all();
void push(T);
void abort();

private:
std::condition_variable cv;
mutable std::mutex m;
std::deque<T> queue;
bool isAborted=false;

std::unique_lock<std::mutex> lock() const;
};

write the above, then send the user input via the queue. The main thread does a try pop and processes if it gets any.

std::optional<T> try_pop(){
auto l=lock();
if (is_aborted()||queue.empty()) return {};
auto r=queue.front();
queue.pop_front();
return r;
}
std::optional<T> wait_and_pop(){
auto l=lock();
cv.wait(l, [&]{return is_aborted()||!queue.empty();});
if (is_aborted()) return {};
auto r=queue.front();
queue.pop_front();
return r;
}
void push(T t){
auto l=lock();
if (is_aborted()) return;
queue.push_back(std::move(t));
cv.notify_one();
}
void abort(){
auto l=lock();
isAborted=true;
cv.notify_all();
}

there are some sample implementations.

Does future in c++ corresponding to promise in javascript?


  1. Yes.
  2. std::future<T> stands for a future result of T, i.e. the object will at some point in the future hold a T. std::promise<T> is an object promising to provide a T at some point in the future.

Which language got the naming right is debatable.

C++ what are std::shared_future and std::promise

std::shared_future is when you need to have multiple valid copies of a std::future, and possibly multiple consumers of said std::future.

You can move a std::future into a std::shared_future.

Consider std::future:std::shared_future to be like std::unique_ptr:std::shared_ptr.


std::promise is one way to generate a std::future.

template<class T>
std::future<std::decay_t<T>> make_ready_future( T&& t ) {
std::promise<std::decay_t<T>> pr;
auto r = pr.get_future();
pr.set_value(std::forward<T>(t));
return r;
}

this is a toy example of std::promise, where I take a T and I generate an already-ready std::future<T> from it.

If you have code that could require threading off, or might already be calculated, you can return a std::future<T> from it. If it isn't calculated, you return the result of a std::async( ... ) call. If it is already calculated, you return a make_ready_future( calculated value ).

A slightly more complex situation:

template<class T>
struct promise_pipe {
std::future<T> out;
std::function<void(T)> in;
promise_pipe() {
auto pr = std::make_shared<std::promise<T>>();
out = pr->get_future();
in = [pr](T t)mutable{
if (pr) pr->set_value(std::forward<T>(t));
pr = {};
};
}
};

Here I have written a promise_pipe.

A promise_pipe<int> has two fields -- a std::future<int> out and a std::function<void(int)> in.

The out future will be ready if and only if you call in with an int.

So you can have a function that returns a std::future<Bitmap>. You have some network code that is expecting the bitmap to be streamed. So you create a promise_pipe<Bitmap>, return the std::future<Bitmap> and store the in in a "todo" list when the bitmap arrives.

When the bitmap has fully arrived, you just do in(Bitmap) and whomever is waiting on that future will be notified.


promises are objects designed to allow programmers to generate their own futures, not just use ones created by the std library.

Now a packaged_task is often easier to work with than a std::promise, and serves much the same purpose as the promise_pipe above. It is move-only, so you can end up having to wrap it into a std::shared_ptr and a std::function anyhow (or a custom move-only std::function)

Passing std::promise object to a function via direct function call

When you compile this code, even if don't use std::thread explicitly, you still have to add -pthread command line option, because internally std::promise and std::future depend on the pthread library.

Without -pthread on my machine I get:

terminate called after throwing an instance of 'std::system_error'
what(): Unknown error -1

With -pthread:

20*10 = 200

My doubt is if std::promise using std::thread then it should throw some compilation or linkage error right?

Very good question. See my answer here.

C++ cannot call set_value for promise move-captured in a lambda?

By default lambda stores all its captured values (non-references) as const values, you can't modify them. But lambda supports keyword mutable, you can add it like this:

[/*...*/](/*...*/) mutable { /*...*/ }

This will allow inside body of a lambda to modify all its values.

If for some reason you can't use mutable, then you can use other work-around:

[/*...*/, p = std::make_shared<ClassName>(std::move(p)), /* ... */](/*...*/) {/*...*/}

In other words wrap your moved value into std::shared_ptr, you can also use std::unique_ptr if you like.

Wrapping into shared pointer solves the problem, because shared pointer (unique also) allows to modify its underlying object value even if pointer itself is const.

Don't forget inside the body of a lambda to dereference p as a pointer, in other words if you used p.SomeMethod(), now you have to use p->SomeMethod() (with -> operator).

Lifetime of promise and set_value_at_thread_exit

Why does your code generate problems? Let's start with ansewer to 'when _at_thread_exit writes to shared state of std::future and std::promise?'. It happens after destruction of all thread local variables. Your lambda is called within the thread and after its scope is left, the promise is already destroyed. But what happens when thread calling your lambda has some thread-local variables? Well, the writing will occur after destruction of the std::promise object. Actually, the rest is really undefined in standard. It seems that passing data to shared state could be done after destruction of std::promise but information is not really there.

Simplest solution is of course this:

std::promise<int> promise;
auto future = promise.get_future();

const auto task = [](std::promise<int>& promise) {
try {
promise.set_value_at_thread_exit(int_generator_that_can_throw());
} catch (...) {
promise.set_exception_at_thread_exit(std::current_exception());
}
};

std::thread thread(task, std::ref(promise));
// use future
thread.join();


Related Topics



Leave a reply



Submit