11

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?

Richard Critten
  • 2,138
  • 3
  • 13
  • 16
Dimitri Vorona
  • 450
  • 3
  • 13
  • 1
    I would like to add to this question: What about the usual std::future::wait? It is also const, so it should be thread-safe. But std::future is apparently less thread safe than std::shared_future, so the answer for std::shared_future will also have ramifications on std::future. – gexicide Mar 23 '21 at 14:56
  • 1
    [Relevant/possible dupe](https://stackoverflow.com/a/32333444/7582247) and [futures.shared.future/32.9.8.2](https://eel.is/c++draft/futures#shared.future-2) "_Member functions of `shared_­future` do not synchronize with themselves, but they synchronize with the shared state_" – Ted Lyngmo Mar 23 '21 at 15:12
  • @LanguageLawyer shared_future doesn't have a set method, only promise does. – gexicide Mar 23 '21 at 15:17
  • @gexicide indeed :/. Ignoring special member functions, `shared_future` only has `const` methods. – Language Lawyer Mar 23 '21 at 15:19
  • Exactly! This is why the cited sentence doesn't seem to make much sense. An object with only const methods should always behave thread-safely. – gexicide Mar 23 '21 at 15:19
  • @gexicide logical constness and bitwise one are two different thing, const is not a guarantee https://stackoverflow.com/questions/3830367/difference-between-logical-and-physical-const-ness – Alessandro Teruzzi Mar 23 '21 at 15:23
  • `const` is a guarantee for the standard library, see the quoted passage (17.6.5.9/3). `const` operations are expected not to cause race conditions with other `const` operations there. – Dimitri Vorona Mar 23 '21 at 15:25
  • @AlessandroTeruzzi https://timsong-cpp.github.io/cppwp/n4861/res.on.data.races#3 – Language Lawyer Mar 23 '21 at 15:25
  • Looking at gcc-4.6.2 implementation, shared_future wait is calling _M_state->wait() where _M_state is shared_ptr<_State_base>. _State_base wait is a non const function that aquire a mutex. https://gcc.gnu.org/onlinedocs/gcc-4.6.2/libstdc++/api/a00888_source.html – Alessandro Teruzzi Mar 23 '21 at 16:38
  • @AlessandroTeruzzi The methods of the shared state are explicitly defined as synchronized by the standard. – Dimitri Vorona Mar 23 '21 at 17:17
  • @DimitriVorona which is precisely the issue here, you have multiple threads waiting till a pointer is not null, the method is syncronized, so they are executed one after another. Let's assume the first thread to complete owns the only copy of the shared_future (the others have a pointer to it). What happen if the std::shared_future goes out of scope before the other methods complete? – Alessandro Teruzzi Mar 23 '21 at 17:59
  • @AlessandroTeruzzi if the only shared_future goes out of scope, then on which objects would the other threads call those methods in the first place? – gexicide Mar 23 '21 at 18:15
  • @gexicide the method was called before the object went out of scope and the other threads are wait for the mutex to be released. – Alessandro Teruzzi Mar 23 '21 at 18:25
  • Obviously, the usual lifetime rules apply, but this has nothing to do with thread-safety. Just assume that the lifetime of the threads is shorter than the lifetime of the shared `shared_future`. – Dimitri Vorona Mar 23 '21 at 18:31
  • 1
    to nitpick here cppreference doesn't says it is not "thread-safe" but just is not "safe" and using a copy per thread prevent exactly the scenario I have described above. – Alessandro Teruzzi Mar 23 '21 at 18:40

0 Answers0