[cpp-threads] Visibility question

Boehm, Hans hans.boehm at hp.com
Wed Aug 2 00:37:50 BST 2006


We agree that there is clearly no race in the fence() case.

But I think that's a fundamentally different question, since in my view
fence() itself introduces synchronization edges between consecutively
executed (in some total order) fence() operations.  I think it has to.
Otherwise consider:

Thread 1:
x.store<raw>(1);
fence();
r2 = y.load<raw>();

Thread 2:
y.store<raw>(1);
fence();
r2 = x.load<raw>();

Undesirable outcome: r1 = r2 = 0

There is nothing to prevent this unless something introduces a
synchronization edge between threads to make a store appear "in between"
the initialization and the raw load.

(I suspect there are interesting issues as to what fence() really means
and how it interacts with atomics.  But I think the weakest
semi-plausible interpretation is that fences behave as though they are
both load and store operations on some locations shared by all fences.)

The fetch_add operations on the other hand operate on distinct
locations, and hence introduce no inter-thread happens-before edge.

Nonetheless, I'll take this as a vote against calling this a race?

Hans



> -----Original Message-----
> From: cpp-threads-bounces at decadentplace.org.uk 
> [mailto:cpp-threads-bounces at decadentplace.org.uk] On Behalf 
> Of Peter Dimov
> Sent: Tuesday, August 01, 2006 4:11 PM
> To: C++ threads standardisation
> Subject: Re: [cpp-threads] Visibility question
> 
> Boehm, Hans wrote:
> > Clark brought up the following interesting question that I couldn't 
> > immediately answer.  I think I now have the answer, but I thought I 
> > would post it here to make sure we agree.
> >
> > The question is whether "raw", i.e. unordered, atomic operations 
> > should contribute to the happens-before relation.
> >
> > As a particular example of what I mean, consider (x,y,z atomic, w 
> > ordinary, everything initially zero):
> >
> > Thread 1:
> > w = 1; // ordinary store
> > y.fetch_add<ordered>(1); // ordered atomic; orders 
> everything within 
> > thread x.store<raw>(1);
> >
> > Thread 2:
> > r2 = x.load<raw>();
> > z.fetch_add<ordered>(1); // ordered atomic; orders 
> everything within 
> > thread; different variable if (r2) r1 = w; // ordinary load
> >
> > Is there a data race on w?
> 
> I believe that the answer should be "no", in the above and in 
> the following:
> 
> T1:
> w = 1;
> fence();
> x.store<raw>(1);
> 
> T2:
> r2 = x.load<raw>();
> fence();
> if (r2) r1 = w; // ordinary load
> 
> which is, in turn, a different (potentially less efficient) 
> reformulation of
> 
> T1:
> w = 1;
> x.store<release>(1);
> 
> T2:
> r2 = x.load<acquire>();
> if (r2) r1 = w; // ordinary load
> 
> and this is plain as day. :-)
> 
> > Thinking about this example a bit more. I'm not 100% 
> comfortable with 
> > either answer.  With the synchronization edge, it's clear 
> that ordered 
> > atomic updates are not equivalent to the nonatomic version, even if 
> > they operate on thread-private data.  That's ugly, though 
> probably not 
> > a showstopper for C++.
> 
> Since an <ordered> atomic is a superset of fence(), it seems 
> reasonable (to
> me) for it to not be equivalent to an ordinary op even when 
> operating on private data.
> 
> 
> --
> cpp-threads mailing list
> cpp-threads at decadentplace.org.uk
> http://www.decadentplace.org.uk/cgi-bin/mailman/listinfo/cpp-threads
> 



More information about the cpp-threads mailing list