Reputation: 434
The C++11 standard says:
30.6.6 Class template future
(3) "The effect of calling any member function other than the destructor, the move-assignment operator, or valid on a future object for which
valid() == false
is undefined."
So, does it mean that the following code might encounter undefined behaviour?
void wait_for_future(std::future<void> & f)
{
if (f.valid()) {
// what if another thread meanwhile calls get() on f (which invalidates f)?
f.wait();
}
else {
return;
}
}
Q1: Is this really a possible undefined behaviour?
Q2: Is there any standard compliant way to avoid the possible undefined behaviour?
Note that the standard has an interesting note [also in 30.6.6 (3)]:
"[Note: Implementations are encouraged to detect this case and throw an object of type future_error with an error condition of
future_errc::no_state
. —endnote]"
Q3: Is it ok if I just rely on the standard's note and just use f.wait()
without checking f
's validity?
void wait_for_future(std::future<void> & f)
{
try {
f.wait();
}
catch (std::future_error const & err) {
return;
}
}
As it turned out, the real problem with my example was not directly due to parallel modifications (a single modifying get
was called from a single thread, the other thread called valid
and wait
which shall be safe).
The real problem was that the std::future
object's get
function was accessed from a different thread, which is not the intended use case! The std::future
object shall only be used from a single thread!
The only other thread that is involved is the thread that sets the shared state: via return from the function passed to std::async
or calling set_value
on the related std::promise
object, etc.
More: even wait
ing on an std::future
object from another thread is not intended behaviour (due to the very same UB as in my example#1). We shall use std::shared_future
for this use case, having each thread its own copy of an std::shared_future
object. Note that all these are not through the same shared std::future
object, but through separate (related) objects!
Bottom line: These objects shall not be shared between threads. Use a separate (related) object in each thread.
Upvotes: 17
Views: 6413
Reputation: 34538
A normal std::future
is not threadsafe by itself. So yes it is UB, if you call modifying functions from multiple threads on a single std::future
as you have a potential race condition. Though, calling wait
from multiple threads is ok as it's const/non-modifying.
However, if you really need to access the return value of a std::future
from multiple threads you can first call std::future::share
on the future to get a std::shared_future
which you can copy to each thread and then each thread can call get
. Note that it's important that each thread has its own std::shared_future
object.
You only need to check valid if it is somehow possible that your future might be invalid which is not the case for the normal usecases(std::async
etc.) and proper usage(e.g.: not callig get
twice).
Upvotes: 17
Reputation: 254461
Futures allow you to store the state from one thread and retrieve it from another. They don't provide any further thread safety.
Is this really a possible undefined behaviour?
If you have two threads trying to get the future's state without synchronisation, yes. I've no idea why you might do that though.
Is there any standard compliant way to avoid the possible undefined behaviour?
Only try to get the state from one thread; or, if you genuinely need to share it between threads, use a mutex or other synchronisation.
Is it ok if I just rely on the standard's note
If you known that the only implementations you need to support follow that recommendation, yes. But there should be no need.
and just use
f.wait()
without checkingf
's validity?
If you're not doing any weird shenanigans with multiple threads accessing the future, then you can just assume that it's valid until you've retrieved the state (or moved it to another future).
Upvotes: 7