On Monday, 15 October 2018 at 21:51:43 UTC, Manu wrote:

If a shared method is incompatible with an unshared method, your class is broken.

What?!? So... my unshared methods should also perform all that's necessary for `shared` methods?

Explicit casting doesn't magically implement thread-safety, it
basically just guarantees failure.

It doesn't indeed. It does, however, at least help prevent silent bugs. Via that same guaranteed failure. That failure is about all the help we can get from the compiler anyway.

What I suggest are rules that lead to proper behaviour with respect to writing a thread-safe API.
You can write bad code with any feature in any number of ways.

Yup. For example, passing an int* to a function expecting shared int*.

I see it this way:
If your object has shared methods, then it is distinctly and
*deliberately* involved in thread-safety. You have deliberately
opted-in to writing a thread-safe object, and you must deliver on your promise.

The un-shared API of an object that supports `shared` are not exempt from the thread-safety commitment, they are simply the subset of the API that may not be called from a shared context.

And therefore they lack any synchronization. So I don't see how they *can* be "compatible" with `shared` methods.

If your shared method is incompatible with other methods, your class is broken, and you violate your promise.

Nope.

class BigCounter {

this() { /* don't even need the mutex if I'm not sharing this */ }

    this(Mutex m = null) shared {
        this.m = m ? m : new Mutex;
    }

    void increment() { value += 1; }
void increment() shared { synchronized(m) *value.assumeUnshared += 1; }

private:
    Mutex m;
    BigInt value;
}

They're not "compatible" in any shape or form. Or would you have the unshared ctor also create the mutex and unshared increment also take the lock? What's the point of having them then? Better disallow mixed implementations altogether (which is actually not that bad of an idea).

Nobody writes methods of an object such that they don't work with each other... methods are part of a deliberately crafted and packaged entity. If you write a shared object, you do so deliberately, and you buy responsibility of making sure your objects API is thread-safe.
If your object is not thread-safe, don't write shared methods.

Ahem... Okay...

import std.concurrency;
import core.atomic;

void thread(shared int* x) {
    (*x).atomicOp!"+="(1);
}

shared int c;

void main() {
    int x;
    auto tid = spawn(&thread, &x); // "just" a typo
}

You're saying that's ok, it should "just" compile. It shouldn't. It should produce an error and a mild electric discharge into the developer's chair.

Reply via email to