A breach of immutability due to memory implicit conversions to immutable without synchronisation, maybe??

Steven Schveighoffer schveiguy at gmail.com
Sun Nov 11 20:47:16 UTC 2018


On 11/11/18 2:21 PM, John Colvin wrote:
> Take a look at this (I think?) supposed-to-be-thread-safe code according 
> to the rules of D:
> 
> import std.stdio;
> import core.thread;
> import core.atomic;
> 
> int* foo() pure
> {
>      auto ret = new int;
>      *ret = 3;  // MODIFY
>      return ret;
> }
> 
> shared immutable(int)* g;
> 
> void bar()
> {
>      immutable(int)* d = null;
>      while (d is null)
>          d = g.atomicLoad;
>      assert(*d == 3); // READ
>      assert(*d == 3); // READ AGAIN
> }
> 
> void main()
> {
>      auto t = new Thread(&bar).start();
>      immutable(int)* a = foo();
>      g.atomicStore(a);
>      t.join;
> }
> 
> What stops the CPU executing this such that MODIFY happens between READ 
> and READ AGAIN ?
> 
> To aid in the thought experiment, imagine if we replace `*ret = 3` with 
> `*ret = longComputation()`? It might help your reasoning about 
> re-ordering if you consider `foo` inlined.

Hm... I wouldn't expect the compiler would allow reordering across a 
return boundary. Even if the inlining occurs. Implicit conversion of 
pure return values depends on that.

I don't know all the rules of reordering, but this would definitely 
cause races if it was allowed to reorder the storage of data in *d, and 
the storage of d into g.

> 
> Is implicit conversion to the shared part of immutable actually safe, or 
> does it secretly hide data races?
> 
> Another way of putting this:
> Is `ret` secretly shared due to implicit conversion to immutable?

If it is, we need to make it so it's not.

-Steve


More information about the Digitalmars-d mailing list