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

John Colvin john.loughran.colvin at gmail.com
Mon Nov 12 09:11:04 UTC 2018


On Sunday, 11 November 2018 at 20:47:16 UTC, Steven Schveighoffer 
wrote:
> 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.

The compiler can definitely re-order over return boundaries in 
general, but perhaps there is some special logic to prevent 
mistakes in these cases?

> Wait, wouldn't the atomicStore create a barrier, such that all 
> the code before it must execute?
> 
> -Steve

Not a full memory barrier on most (all relevant?) platforms.


More information about the Digitalmars-d mailing list