shared - i need it to be useful

Stanislav Blinov stanislav.blinov at gmail.com
Thu Oct 18 12:15:07 UTC 2018


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.


More information about the Digitalmars-d mailing list