[dmd-concurrency] draft 7
Andrei Alexandrescu
andrei at erdani.com
Mon Feb 1 22:24:11 PST 2010
Fawzi Mohamed wrote:
>> I suggest we resume and start with describing what was unclear, then
>> specifying the steps we need to take to clarify things better in the
>> draft. Thanks!
>
> p 26-27
> "All operations on _balance are now protected by acquiring _guard. It
> may seem
> there is no need to protect balance with _guard because a double can be
> read atomi-
> cally, but protection must be there for subtle reasons that shall come
> forth later. In brief,
> due to today’s aggressive optimizing compilers and relaxed memory
> models, all access
> to shared data must entail some handshake between threads; no handshake
> means a
> thread could call obj.balance repeatedly and always get a cached copy
> form some pro-
> cessor’s cache that never, ever gets updated, in spite of other threads’
> frenetic use of
> deposit and withdraw. ( This is one of the ways in which modern
> multithreading defies
> intuition and confuses programmers versed in classic multithreading.)"
>
> From the previous discussion it should be clear that as written it is
> wrong.
I'm afraid it's your assessment of the situation that is wrong or at
least in conflict with the texts that I'm perusing.
There are two entities that must be kept in the know with regard to
proper synchronization: the compiler and the underlying machine. The
text chose to conflate the two (at least for the time being) in order to
simplify the discussion.
> Indeed it is better to have a barrier there (if the update would not be
> atomic, in general case) but there is no need for it in this specific
> case, as access is atomic.
I'm not sure. Clearly the compiler must be kept informed that no code
motion and no enregistering should happen. So no contest there.
The problem is whether a barrier should also be inserted. According to
the four Posix guarantees in Butenhof's book "Programming with Posix
Threads" page 89, memory visibility by a thread of whatever another
thread just did is only ensured if you insert a barrier. (Acquiring and
releasing a mutex both issue barriers.) Butenhof actually goes to
lengths to insist on it: "This is not only to protect against multiple
writes---even when a thread only reads data it must use a mutex to
ensure that it sees the most recent value of the data written while the
mutex was locked." Later he does mention only a barrier is really
needed, but he never says _nothing_ is needed.
At the end of the visibility section he says "If you want to write
portable Pthreads code, you will always guarantee correct memory
visibility by using the Pthreads memory visibility rules instead of
relying on any assumptions regarding the hardware or compiler behavior."
If you or I don't know of any machine that would keep on reading a datum
from its own cache instead of consulting the main memory, we're making
an assumption. The right thing to do is to keep the compiler _AND_ the
machine in the loop by using appropriate fencing.
> It is wrong that a cache of a processor might be never updated, it will
> be updated at some point, and a barrier (in general) will not force the
> update to be performed immediately.
According to my reading of Butenhof's text, there is nothing stopping a
machine from delaying reads indefinitely if you don't insert a barrier.
BTW I am not under the misconception that a barrier is a flush
operation. In fact Butenhof's book, which I use for reference, warns
exactly about that in large font on page 93.
If you have references to back up your viewpoint please let me know.
> A problem that can come up is that the variable is promoted to a
> register in a loop and is not updated from the memory.
Yes.
> To avoid this one can use volatile.
Volatile is not in D anymore, and also at that point in the text the
entire notion is not introduced and not worth introducing.
> This issue is a priory disconnected
> with the presence of barriers (well one would hope that a correct
> compiler disables "upgrading to registers" for variables in a locked
> section crossing the boundary of it, but a priory it doesn't have to be
> like that, and especially using just memory barriers, I would not trust
> current compilers to always do the correct thing without a "volatile".
Agreed. There must be a special construct understood by the compiler to
(a) prevent reordering and enregistering and (b) insert the appropriate
barrier instruction. One without the other is useless.
Andrei
More information about the dmd-concurrency
mailing list