Dimitri Vorona
Dimitri Vorona

Reputation: 490

Calling `std::shared_future<T>::wait` from multiple threads

cppreference is explicit about calling std::shared_future<T>::wait from multiple threads:

Calling wait on the same std::shared_future from multiple threads is not safe; the intended use is for each thread that waits on the same shared state to have a copy of a std::shared_future.

But I can't find a basis for this assertion. There is nothing in the standard marking wait as some kind of special case. While the standard says that the methods on a single shared_future instance are not synchronized, you don't need synchronization as long only const methods are being called:

[17.6.5.9/3] A C++ standard library function shall not directly or indirectly modify objects (1.10) accessible by threads other than the current thread unless the objects are accessed directly or indirectly via the function’s non-const arguments, including this.

There are contradicting answers to be found on SO. Does anyone have an authoritative source on this which explains how calling a const method on a from stdlib could lead to a race condition?

Upvotes: 13

Views: 702

Answers (2)

Stewart Becker
Stewart Becker

Reputation: 317

The C++11 standard contained a caveat on the shared state of futures and shared_futures:

[futures.state/10] Accesses to the same shared state conflict (1.10)

Explicit exceptions to the usual rule of const library functions being thread-safe are allowed, as section 17.6.5.9 begins:

[res.on.data.race/1] ... Every standard library function shall meet each requirement unless otherwise specified. ... (emphasis mine)

However, C++14 changed the rule about shared state to:

[futures.state/11] Access to the result of the same shared state may conflict (1.10). [ 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 avoid- ance (17.6.5.9). For example, concurrent accesses through references returned by shared_future::get() (30.6.7) must either use read-only operations or provide additional synchronization. — end note ]

I suspect the warning on cppreference.com was added under C++11, and never updated to reflect the wording in the later standards.

Since neither shared_future::wait() nor future::wait() refer to the shared state's result, both should now be data-race free.

Upvotes: 1

Vincent X
Vincent X

Reputation: 372

Calling wait on a shared_future instance from multiple threads should be thread-safe since it is a const method. As your quote from the standard suggests, the standard library guarantees that const means thread-safe. However, what is not safe is calling non-const methods such as operator=, similar to shared_ptr. This may be one reason to always hold a copy of shared_future in each thread to prevent accidentally introducing data races.

For additional context, there once existed an atomic_future, which was essentially an atomic version of shared_future that could be safely shared among multiple threads. According to N2997:

The need for an atomic_future arose from the fact that the move constructor and both assigments made the shared_future much more error prone to use a single instance from multiple threads. But such use is required when more than one thread scans a global list of futures for results provided by worker threads.

However, atomic_future was removed from C++11 in N3194 due to its unsatisfactory interface.

Upvotes: 0

Related Questions