On 08/12/2016 02:29 PM, H. S. Teoh via Digitalmars-d wrote:
On Fri, Aug 12, 2016 at 02:21:04PM -0400, Andrei Alexandrescu via Digitalmars-d 
wrote:
On 08/12/2016 02:01 PM, H. S. Teoh via Digitalmars-d wrote:
On Fri, Aug 12, 2016 at 02:04:53PM -0400, Andrei Alexandrescu via Digitalmars-d 
wrote:
On 08/12/2016 01:21 PM, Steven Schveighoffer wrote:
On 8/12/16 1:04 PM, Jonathan M Davis via Digitalmars-d wrote:

Honestly, I don't think that shared is broken.

Yes. It is broken.

shared int x;
++x; // error, must use atomicOp.
x = x + 1; // OK(!)

How is this broken and how should it behave? -- Andrei

?!

Isn't it obvious that assigning to a shared int must require
atomicOp or a cast? `x = x + 1;` clearly has a race condition
otherwise.

It is not to me, and it does not seem like a low-level race condition
to me (whereas ++x is).

The problem is that the line between "low-level" and "high-level" is
unclear and arbitrary.

I'd need to think a bit before agreeing or disagreeing, but it's a plausible take. In this case fortunately, the matters can be distinguished.

Doesn't ++x lower to x = x + 1 on some CPUs
anyway (or vice versa, if the optimizer decides to translate it the
other way)?

This doesn't matter. The question is what is explicit and what is implicit in the computation.

++expr

is a RMW ("read-modify-write") expression, equivalent to:

((ref x) => { return x = cast(typeof(x)) (x + 1); })(expr)

In contrast,

expr1 = expr2 + 1

is an expression consisting of distinct read and write, both under the control of the code written by the programmer. As I explained, distinguishing in the general case when expr1 and expr2 refer to the same memory location is not doable statically. So it stands to reason that the compiler generates the code for one read and one write because that is literally what it has been asked to do.

It may actually be the case that one wants to do x = x + 1 and exercise a benign high-level race.

Why should the user have to worry about such details?

What details are you referring to, and how would compiler technology help with those?

Wouldn't that make shared kinda useless to begin with?

It doesn't seem that way.

Currently the compiler must ensure that an atomic read and an atomic
write are generated for x. Other than that, it is the responsibility
of the user.  The use of "shared" does not automatically relieve the
user from certain responsibilities.

I agree that it would be nice to have stronger protection against
higher-level bugs, but those are outside the charter of "shared".
Consider:

x = *p + 1;

How would the compiler reject the right uses but not the case when p
== &x?
[...]

The compiler should reject it (without the appropriate casts) if p has a
shared type, and the aliasing situation with x is unknown / unclear.

I think you are not right here.



Andrei

Reply via email to