On 22.10.18 14:39, Aliak wrote:
On Monday, 22 October 2018 at 10:26:14 UTC, Timon Gehr wrote:
---
module borked;
void atomicIncrement(int* p)@system{
import core.atomic;
atomicOp!("+=",int,int)(*cast(shared(int)*)p,1);
}
struct Atomic(T){
private T val;
void opUnary(string op : "++")() shared @trusted {
atomicIncrement(cast(T*)&val);
}
}
void main()@safe{
auto a=new Atomic!int;
import std.concurrency;
spawn((shared(Atomic!int)* a){ ++*a; }, a);
++a.val; // race
}
---
Oh no! The author of the @trusted function (i.e. you) did not deliver
on the promise they made!
hi, if you change the private val in Atomic to be “private shared T
val”, is the situation the same?
It's a bit different, because then there is no implicit unshared->shared
conversion happening, and this discussion is only about that. However,
without further restrictions, you can probably construct cases where a
@safe function in one module escapes a private shared(T)* member to
somewhere else that expects a different synchronization strategy.
Therefore, even if we agree that unshared->shared conversion cannot be
implicit in @safe code, the 'shared' design is not complete, but it
would be a good first step to agree that this cannot happen, such that
we can then move on to harder issues.
E.g. probably it would be good to have something like @trusted data that
cannot be manipulated from @safe code, such that @trusted functions can
rely on some invariants.