[cpp-threads] Sequential Consistency redux
Alan Stern
stern at rowland.harvard.edu
Fri Nov 18 17:08:27 GMT 2011
On Fri, 18 Nov 2011, Mark Batty wrote:
> Hello Nick,
>
> > Are the facilities in 30 [Thread] required to be sequentially consistent
> > or not as far as ordinary code goes?
>
> We have proved this is true (in the absence of forging pointers to
> atomic locations) for our formal version of the memory model, which is
> faithful to the intent of the standard. The standard makes the claim
> of SC behavior for programs that don't use weaker-than-seq_cst atomics
> in n2391-1.10p21.
>
> > Orthogonally to that, does the same apply to relaxed atomics in the code
> > sections protected by mutexes?
>
> If you protect relaxed atomic reads and writes as if they were
> non-atomics, so that there would be no data races, then you should get
> back sequential consistency. This is not simply stated in the
> standard, and is a theorem requiring argument over the model. I do
> think it's true though.
>
> > Can both threads 3 and 4 print 1 and 0, thus indicating that
> > mutex-locked code is not sequentially consistent? If not, where is that
> > stated?
>
> 10-10 should not be visible. This hinges on the total order of mutex
> actions at a single location described in n2391-30.4.1.2p5.
My earlier response was wrong; Mark is correct. Perhaps the easiest
way to explain this is in terms of the "happens before" relation.
> Consider the following example:
>
> In serial code:
> static mutex A, B;
> int a = 0, b = 0;
> The program then creates 4 threads.
> In thread 1:
> A.lock(); a = 1; A.unlock();
> In thread 2:
> B.lock(); b = 1; B.unlock();
> In thread 3:
> A.lock(); cout << a << endl; A.unlock();
> B.lock(); cout << b << endl; B.unlock();
> In thread 4:
> B.lock(); cout << b << endl; B.unlock();
> A.lock(); cout << a << endl; A.unlock();
>
> Can both threads 3 and 4 print 1 and 0, thus indicating that
> mutex-locked code is not sequentially consistent? If not, where is that
> stated?
If threads 3 and 4 both print 1 and 0, then we must have:
(thread 4) cout << a (prints 0) is sequenced before
(thread 4) A.unlock synchronizes with
(thread 1) A.lock is sequenced before
(thread 1) a = 1 is sequenced before
(thread 1) A.unlock synchronizes with
(thread 3) A.lock is sequenced before
(thread 3) cout << a (prints 1) is sequenced before
(thread 3) cout << b (prints 0) is sequenced before
(thread 3) B.unlock synchronizes with
(thread 2) B.lock is sequenced before
(thread 2) b = 1 is sequenced before
(thread 2) B.unlock synchronizes with
(thread 4) B.lock is sequenced before
(thread 4) cout << b (prints 1) is sequenced before
(thread 4) cout << a
Therefore thread 4's "cout << a" "happens before" itself, which
violates the requirement in 1.10p12 that the "happens before" relation
must not exhibit any cycles.
Alan Stern
More information about the cpp-threads
mailing list