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.

Reply via email to