On Sunday, 21 October 2018 at 09:58:18 UTC, Walter Bright wrote:
On 10/20/2018 11:08 AM, Nicholas Wilson wrote:
You can if no-one else writes to it, which is the whole point of Manu's proposal. Perhaps it should be const shared instead of shared but still.

There is no purpose whatsoever to data that can be neither read nor written. Shared data is only useful if, at some point, it is read/written, presumably by casting it to unshared in @trusted code. As soon as that is done, you've got a data race with the other existing unshared aliases.

No, because every part of the public interface has to work together to ensure thread-safety.

This code is invalid (but compiles) under MP:

module A;
struct S {
    private int n;
    void foo() @safe {
        n--; // Not thread-safe
    }
    void bar() shared @trusted {
        atomicOp!"++"(n.assumeUnshared);
    }
}

module B;
import A;
void passToOtherThread(shared(S)*); // Calls S.bar()

void main() {
    S* s = new S();
    passToOtherThread(s);
    s.foo();
}

The reason: foo() breaks bar()s promise of thread-safety. This means that S does not provide a thread-safe interface. It would be nice if the compiler could statically notice that, but I don't see how that'd work.

Now, for a thread-safe version:

module A;
struct S {
    int n;
    void foo() @trusted {
        atomicOp!"--"(n); // Thread-safe
    }
    void bar() shared @trusted {
        atomicOp!"++"(n.assumeUnshared);
    }
}

module B;
import A;
void passToOtherThread(shared(S)*); // Calls S.bar()

void main() {
    S* s = new S();
    passToOtherThread(s);
    s.foo();
}

In this case, passToOtherThread is free to call S.bar as often as it may feel like, since atomic operations are used in every possible access to S.n. This is true even though one thread has unshared access and other threads have shared access.

--
  Simen

Reply via email to