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.