On Thursday, 7 August 2014 at 16:44:37 UTC, Andrei Alexandrescu wrote:
Yah. The basic intent behind "shared" is to disallow unwitting racy code, and allow users to define robust shared abstractions.

Consider this code at top level:

struct S {
  private long a, b, c;
  ...
}
shared S g_theS;

All threads will "see" the same g_theS. The intent of "shared" is to disallow racy/wrong use of g_theS across threads. However, assigning g_theS is by default allowed and does something definitely racy.

So I think we should do the following:

* if S.sizeof <= size_t.sizeof, generate atomic assignment automatically for shared values.

* if S.sizeof == 2 * size_t.sizeof, generate special 128-bit atomic assignment for shared values on 64-bit systems, and 64-bit atomic assignment on 32-bit systems.

* In all other cases, reject code during compilation and require an opAssign for shared values.

This will break code. It may even break correct code (benign races or code that uses external locking). On the other hand, it seems like the right thing to do by the intended semantics of "shared".

Even with that change, you'll still be able to do:

S otherS = ...;
g_theS.a = otherS.a;
g_theS.b = otherS.b;
g_theS.c = otherS.c;

Right? This violates the same invariants as:

g_theS = otherS;

Can you explain the practical benefits of enforcing atomicity of shared objects only at the individual statement level? I must be missing something.

Reply via email to