I'm a bit confused about the requirements in terms of thread-safety placed on std::promise::set_value()
.
The standard says:
Effects: Atomically stores the value r in the shared state and makes that state ready
However, it also says that promise::set_value()
can only be used to set a value once. If it is called multiple times, a std::future_error
is thrown. So you can only set the value of a promise once.
And indeed, just about every tutorial, online code sample, or actual use case for std::promise
involves a communication channel between 2 threads, where one thread calls std::future::get()
, and the other thread calls std::promise::set_value()
.
I've never seen a use case where multiple threads might call std::promise::set_value()
, and even if they did, all but one would cause a std::future_error
exception to be thrown.
So why does the standard mandate that calls to std::promise::set_value()
are atomic? What is the use case for calling std::promise::set_value()
from multiple threads concurrently?
EDIT:
Since the top-voted answer here is not really answering my question, I assume what I'm asking is unclear. So, to clarify: I'm aware of what futures and promises are for and how they work. My question is why, specifically, does the standard insist that std::promise::set_value()
must be atomic? This is a more subtle question than "why must there not be a race between calls to promise::set_value()
and calls to future::get()
"?
In fact, many of the answers here (incorrectly) respond that the reason is because if std::promise::set_value()
wasn't atomic, then std::future::get()
could potentially cause a race condition. But this is not true.
The only requirement to avoid a race condition is that std::promise::set_value()
must have a happens-before relationship with std::future::get()
- in other words, it must be guaranteed that when std::future::wait()
returns, std::promise::set_value()
has completed.
This is completely orthogonal to std::promise::set_value()
itself being atomic or not. In a typical implementation using condition variables, std::future::get()/wait()
would wait on a condition variable. Then, std::promise::set_value()
could non-atomically perform any arbitrarily complex computation to set the actual value. Then it would notify the shared condition variable, (implying a memory fence with release semantics), and std::future::get()
would wake up and safely read the result.
So, std::promise::set_value()
itself does not need to be atomic to avoid a race condition here - it simply needs to satisfy a happens-before relationship with std::future::get()
.
So again, my question is: why does the C++ standard insist that std::promise::set_value()
must actually be an atomic operation, as if a call to std::promise::set_value()
was performed entirely under a mutex lock? I see no reason why this requirement should exist, unless there is some reason or use case for multiple threads calling std::promise::set_value()
concurrently. And I can't think of such a use-case, hence this question.
If it was not an atomic store, then two threads could simultaneously call promise::set_value
, which does the following:
By making this sequence atomic, the first thread to execute (1) gets all the way through to (3), and any other thread calling promise::set_value
at the same time will fail at (1) and raise a future_error
with promise_already_satisfied
.
Without the atomicity, two threads could potentially store their value, and then one would successfully mark the state ready, and the other would raise an exception, i.e. the same result, except that it might be the value from the thread that saw an exception that got through.
In many cases that might not matter which thread 'wins', but when it does matter, without the atomicity guarantee you would need to wrap another mutex around the promise::set_value
call. Other approaches such as compare-and-exchange wouldn't work because you can't check the future (unless it's a shared_future
) to see if your value won or not.
When it doesn't matter which thread 'wins', you could give each thread its own future, and use std::experimental::when_any
to collect the first result that happened to become available.
Edit after some historical research:
Although the above (two threads using the same promise object) doesn't seem like a good use-case, it was certainly envisaged by one of the contemporary papers of the introduction of future
to C++: N2744. This paper proposed a couple of use-cases which had such conflicting threads calling set_value
, and I'll quote them here:
Second, consider use cases where two or more asynchronous operations are performed in parallel and "compete" to satisfy the promise. Some examples include:
- A sequence of network operations (e.g. request a web page) is performed in conjunction with a wait on a timer.
- A value may be retrieved from multiple servers. For redundancy, all servers are tried but only the first value obtained is needed.
In both examples, the first asynchronous operation to complete is the one that satisfies the promise. Since either operation may complete second, the code for both must be written to expect that calls to
set_value()
may fail.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With