On Thursday, 18 October 2018 at 11:35:21 UTC, Simen Kjærås wrote:
On Thursday, 18 October 2018 at 10:08:48 UTC, Stanislav Blinov wrote:
Manu,

how is it that you can't see what *your own* proposal means??? Implicit casting from mutable to shared means that everything is shared by default! Precisely the opposite of what D proclaims.

Well, sorta. But that's not a problem, because you can't do anything that's not threadsafe to something that's shared.

Yes you can. You silently agree to another function's assumption that you pass shared data, while actually passing thread-local data and keeping treating it as thread-local. I.e. you silently agree to a race.

You also essentially forbid defining *any* functions that take `shared T*` argument(s). You keep asking for concrete "holes". Don't you see what the previous "atomicInc" example implies???

I certainly don't. Please do elucidate.

What the hell? I do in the very next paragraph. Do people read sentence by sentence and assume context does not exist or what?

If *any* free function `foo(shared T* bar)`, per your definition, is not threadsafe, then no other function with shared argument(s) can be threadsafe at all. So how do you call functions on shared data then? You keep saying "methods, methods..."

struct Other { /* ... */ }

struct S {
    void foo(shared Other*) shared;
}

Per your rules, there would be *nothing* in the language to prevent calling S.foo with an unshared Other.

That's true. And you can't do anything to it, so that's fine.

Yes you can do "anything" to it. If you couldn't, you wouldn't be able to implement `shared` at all. Forbidding reads and writes isn't enough to guarantee that you "can't do anything with it". *Unless* you forbid implicit conversion from mutable to shared. Then, and only then, your statement can hold.

So the only way to make your proposal work would be to forbid all functions from taking `shared T*` or `ref shared T` argument.

No. Please read this thread again. From the beginning, every word.

Are you kidding me? Maybe it's *you* who should do that?..

Actually, don't do that, because Manu's proposal is simple and elegant:

1. the rule must be applied that shared object can not be read or written

No objection there, I fully support that. I even stated multiple times how it can be extended and why.

2. attributing a method shared is a statement and a promise that the method is threadsafe

No objection here either.

The rest just follows naturally.

Nothing follows naturally. The proposal doesn't talk at all about the fact that you can't have "methods" on primitives, that you can't distinguish between shared and unshared data if that proposal is realized, that you absolutely destroy D's TLS-by-default treatment...

There's actually one more thing: The one and only thing you can do (without unsafe casting) with a shared object, is call shared methods and free functions on it.

Functions that you must not be allowed to write per this same proposal. How quaint.

To sum up, things you implied but never specified in your proposal:

1. Primitive types can't be explicitly `shared`.

Sure they can, they just can't present a thread-safe interface, so you can't do anything with a shared(int).

Ergo... you can't have functions taking pointers to shared primitives. Ergo, `shared <primitive type>` becomes a useless language construct.

2. Free functions taking `shared` arguments are not allowed.

Yes, they are. They would be using other shared methods or free functions on the shared argument, and would thus be thread-safe. If defined in the same module as the type on which they operate, they would have access to the internal state of the object, and would have to be written in such a way as to not violate the thread-safety of other methods and free functions that operate on it.

This contradicts (1). Either you can have functions taking shared T* arguments, thus creating threadsafe interface for them, or you can't. If, per (1) as you say, you can't


3. Only `shared` methods can implement threadsafe operations on `shared` data (which contradicts (2) already) <- this one you did specify.

Non-shared methods are perfectly free to be thread-safe (and they should be, in the sense that they shouldn't interfere with shared methods). A better way to state this is that only shared methods may be called on a shared object. A shared object may also be passed to a function taking a shared parameter.


4. Every variable is implicitly shared, whether intended so or not.

Well, yes, in the same sense that every variable is also implicitly const, whether intended so or not.

I sort of expected that answer. No, nothing is implicitly const. When you pass a reference to a function taking const, *you keep mutable reference*, the function agrees to that, and it's only "promise" is to not modify data through the reference you gave it. But *you still keep mutable reference*. Just as you would keep *unshared mutable* reference if implicit conversion from mutable to shared existed.

Reply via email to