On 08/12/2016 08:04 PM, Andrei Alexandrescu wrote:
On 08/12/2016 01:21 PM, Steven Schveighoffer wrote:
[...]
shared int x;
++x; // error, must use atomicOp.
x = x + 1; // OK(!)
How is this broken and how should it behave? -- Andrei
I may be responsible for some confusion here, including my own.
Disclaimer: I'm far from being an expert on thread-safety. I may have
misunderstandings about fundamentals.
Recently, I made a thread titled "implicit conversions to/from shared":
http://forum.dlang.org/post/nlth0p$1l7g$1...@digitalmars.com
Before starting that thread, and before messing with atomicOp, I had
assumed that D enforces atomic reads/writes on shared types. Then I
noticed that I can read/write shared types that are too large for atomic
ops.
Example:
----
alias T = ubyte[1000];
enum T a = 1;
enum T b = 2;
shared T x = a;
import core.thread: Thread;
void write()
{
bool flip = false;
foreach (i; 0 .. 1_000_000)
{
if (flip) x = a; else x = b; // non-atomic write
flip = !flip;
}
}
void main()
{
auto t = new Thread(&write);
t.start();
foreach (i; 0 .. 1_000_000)
{
T r = x; // non-atomic read
assert(r == a || r == b); // fails
}
t.join();
}
----
I tested a bunch of stuff, and I remember having a test case that failed
with a primitive type like int, but I can't find or recreate it now, and
a little googling suggests that reading/writing should be atomic for
primitive types (on X86 with various restrictions). So I probably just
had an error in that test.
But the above test case stands, and it also fails with -m32 and ulong:
----
alias T = ulong;
enum T a = 0x01_01_01_01__01_01_01_01;
enum T b = 0x02_02_02_02__02_02_02_02;
/* rest as above */
----
So, `shared int x; x = x + 1;` is ok, as far as I see now. But with
other types, unsafe reads/writes are generated.