A few questions about safe concurrent programming assumptions

Nicholas Smith nmsmith65 at gmail.com
Wed Oct 2 23:34:00 PDT 2013


Hi there,

I have a few questions about what is safe to assume when writing 
concurrent code in D with data sharing (as opposed to message 
passing).

After doing a fair amount of reading, I'm still slightly hazy 
about what shared does and doesn't guarantee. Here is the only 
assumption I understand to be valid:

* Reads and writes to shared variables with a size equal to or 
less than the word size of the machine are atomic and are visible 
to all other threads immediately.

Now, in Andrei's D book he also states that sequential 
consistency is guaranteed for operations on shared data, however 
I understand that this is not currently implemented by (any?) 
compiler and perhaps never will be, what with the discussions 
about making changes to the semantics of shared and all.

So then this code is not safe, assuming the methods are executed 
on different threads:

shared int x = 0;
shared bool complete = false;

void foo()
{
     x = 7; // atomic
     complete = true; // atomic
}

void bar()
{
     while (!complete) {}

     writeln(x); // undefined output (0 or 7)
}

But then I understand the core.atomic module incorporates the 
necessary memory barriers to make this work, so we can replace 
foo() by:

void foo()
{
     x.atomicStore(7);
     complete.atomicStore(true);
}

and achieve the intended behaviour (maybe only one of those two 
modifications were actually needed here?). Or possibly:

void foo()
{
     x = 7; // atomic
     atomicFence(); // from core.atomic again
     complete = true; // atomic
}

Do these modifications achieve the desired result? I know there 
are other ways to go about this. I think one (bad) way is putting 
a mutex around every read/write (a mutex also acts as a memory 
barrier, right?), and I suppose in this case, message passing! 
(But let's pretend the data sharing is more complex)

My other question about shared is: what does the shared qualifier 
mean when applied to a class definition? e.g.

shared class Cat {...}

Does it just force all references to Cat instances to be shared 
by default and make all methods of Cat shared? Andrei uses it in 
his book when talking about lock-free programming with cas, 
however he seems to make the assumption (well, explicitly states) 
that sequential consistency is assured within the methods of such 
a class. I'm quite confused about what it actually means, 
especially since shared apparently does not currently use memory 
barriers.


More information about the Digitalmars-d-learn mailing list