Subject: Re: [boost] Lightweight futures
From: Niall Douglas (s_sourceforge_at_[hidden])
Date: 2015-07-01 20:46:06
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
> 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
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.
-- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/