What functions can be called on a shared struct that's implicitly castable to immutable?

Simen Kjærås simen.kjaras at gmail.com
Tue Nov 5 09:37:10 PST 2013


On 04.11.2013 20:52, deadalnix wrote:
> On Monday, 4 November 2013 at 09:34:52 UTC, Simen Kjærås wrote:
>> On 04.11.2013 09:46, deadalnix wrote:
>>> You are trying to solve a non problem here. s is not shared in the first
>>> place, so you can start by fixing it here.
>>>
>>> Now, if S has some indirection, then it make sense to mark it as shared,
>>> but then the trick you propose won't work.
>>
>> I believe we're talking past each other here. Perhaps a better example:
>>
>> module foo;
>>
>> struct S {
>>    immutable(int)[] arr;
>>    void fuzz() const pure {
>>    }
>> }
>>
>> void bar(S s) {
>>    s.fuzz();
>> }
>>
>> void main() {
>>    shared S* s = new shared S;
>>    bar(*s);   // a
>>    s.fuzz(); // b
>> }
>>
>>
>> This example demonstrates the exact same behavior as before, and again
>> I'm not doing a lot of anything special to get line a to compile.
>>
>> Now, I'm still of the opinion that the 'trick' I propose works -
>> making a copy of s is cheap (as that's what the spec says about
>> copying structs), s is implicitly castable to immutable, and the
>> function to be called is guaranteed not to change s in any way. From
>> all that, it seems safe to call fuzz on a shared S.
>
> Any read of arr in fuzz won't be sequentially consistent, so it would be
> incorrect to let you call fuzz on a shared object.

Thank you, that's what I wanted to hear. I don't agree though.

Sequential consistence means reads and writes on one processor happen in 
the order they're written, possibly interleaved with reads and writes 
from other processors. I cannot see how this promise is broken by taking 
a copy on which you only do reads.

Example (CPU 1 is running fuzz, CPU 2 is randomly mutating memory, or 
doing something worthwhile):

   | CPU 1        | CPU 2
==============================
1 | read(arr)    | write(arr)
2 | read(arr[0]) | read(arr)
3 | read(arr[1]) | write(arr[0])

Now, one sequentially consistent ordering of these operations would be:

   | CPU 1        | CPU 2
==============================
1 | read(arr)    |
2 | read(arr[0]) |
3 | read(arr[1]) |
4 |              | write(arr)
5 |              | read(arr)
6 |              | write(arr[0])

Which is exactly what happens in the case of taking a copy.

An equally valid ordering would of course be this:

   | CPU 1        | CPU 2
==============================
1 | read(arr)    |
2 |              | write(arr)
3 |              | read(arr)
4 |              | write(arr[0])
5 | read(arr[0]) |
6 | read(arr[1]) |

In which case the results would potentially be different. However, the 
fact that other sequentially consistent orderings exist does not mean 
that the first is wrong.


Can you please show an example of how copying does violate sequential 
consistency?

--
   Simen


More information about the Digitalmars-d mailing list