On 2 Jul 2015 at 11:39, Gavin Lambert wrote:
On 1/07/2015 22:29, Niall Douglas wrote:
What you are missing is that we assume that whoever is calling get_future() must issue some form of memory synchronisation to transmit the newly constructed future to another thread. That synchronises the changed _needs_locks to other threads. I suppose it is possible that someone could send the future to one thread, and have a different thread do a state read and in theory the state reading thread doesn't see any new state.
The most common case is to transport the promise to another thread (sometimes via packaged_task) and to return the future on the calling thread.
I suppose designs that pass the future to another thread instead are possible but they seem a lot weirder and more convoluted.
Now locks are always on, and as both promise's and future's move constructors are acquire release points, transporting either works. I've documented the exact deviations from the STL in the docs, copying and pasting from those: Known deviations from the ISO C++ standard specification: * No memory allocation is done, so if your code overrides the STL allocator for promise-future it will be ignored. * T must implement either or both the copy or move constructor, else it will static_assert. * T cannot be error_type nor exception_type, else it will static_assert. set_value_at_thread_exit() and set_exception_at_thread_exit() are not implemented, nor probably ever will be. * promise's and future's move constructor and move assignment are guaranteed noexcept in the standard. This promise's and future's move constructor and assignment is noexcept only if type T's move constructor is noexcept. * Only the APIs marked "SYNC POINT" in both promise and future synchronise memory. Calling APIs not marked "SYNC POINT" can return stale information, so don't write code which has a problem with that (specifically, do NOT have multiple threads examining a future for state concurrently unless they are exclusively using SYNC POINT APIs to synchronise memory between them). When might this be a problem in real world code? For example, valid() which is not a SYNC POINT API may return true when it is in fact false. If your code uses a synchronisation mechanism which is not a SYNC POINT API - most usually, this is "synchronised by time/sleep" - and then executes code which depends on valid() being correct as it would always be with STL future promise as valid() there synchronises memory, your code will be racy. The simplest solution is to call any SYNC POINT API before examining valid(), or issue a memory fence (std::atomic_thread_fence), or best of all refactor your code to not use synchronised by time/sleep in the first place. The thread sanitiser tsan reports any use of time to synchronise as a failure which is the correct thing to do - just don't do it in your code. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/