shared - i need it to be useful

Steven Schveighoffer schveiguy at gmail.com
Wed Oct 17 19:31:55 UTC 2018


On 10/17/18 2:46 PM, Manu wrote:
> On Wed, Oct 17, 2018 at 10:30 AM Steven Schveighoffer via

>> What the example demonstrates is that while you are trying to disallow
>> implicit casting of a shared pointer to an unshared pointer, you have
>> inadvertently allowed it by leaving behind an unshared pointer that is
>> the same thing.
> 
> This doesn't make sense... you're showing a thread-local program.
> The thread owning the unshared pointer is entitled to the unshared
> pointer. It can make as many copies at it likes. They are all
> thread-local.

It's assumed that shared int pointer can be passed to another thread, 
right? Do I have to write a full program to demonstrate?

> There's only one owning thread, and you can't violate that without unsafe casts.

The what is the point of shared? Like why would you share data that 
NOBODY CAN USE?

At SOME POINT, shared data needs to be readable and writable. Any 
correct system is going to dictate how that works. It's a good start to 
make shared data unusable unless you cast. But then to make it 
implicitly castable from unshared defeats the whole purpose.

>> In order for a datum to be
>> safely shared, it must be accessed with synchronization or atomics by
>> ALL parties.
> 
> ** Absolutely **
> 
>> If you have one party that can simply change it without
>> those, you will get races.
> 
> *** THIS IS NOT WHAT I'M PROPOSING ***
> 
> I've explained it a few times now, but people aren't reading what I
> actually write, and just assume based on what shared already does that
> they know what I'm suggesting.
> You need to eject all presumptions from your mind, take the rules I
> offer as verbatim, and do thought experiments from there.

What seems to be a mystery here is how one is to actually manipulate 
shared data. If it's not usable as shared data, how does one use it?

> 
>> That's why shared/unshared is more akin to mutable/immutable than
>> mutable/const.
> 
> Only if you misrepresent my suggestion.

It's not misrepresentation, I'm trying to fill in the holes with the 
only logical possibilities I can think of.

> 
>> It's true that only one thread will have thread-local access. It's not
>> valid any more than having one mutable alias to immutable data.
> 
> And this is why the immutable analogy is invalid. It's like const.
> shared offers restricted access (like const), not a different class of
> thing.

No, not at all. Somehow one must manipulate shared data. If shared data 
cannot be read or written, there is no reason to share it.

So LOGICALLY, we have to assume, yes there actually IS a way to 
manipulate shared data through these very carefully constructed and 
guarded things.

> There is one thread with thread-local access, and many threads with
> shared access.
> 
> If a shared (threadsafe) method can be defeated by threadlocal access,
> then it's **not threadsafe**, and the program is invalid.
> 
> struct NotThreadsafe
> {
>    int x;
>    void local()
>    {
>      ++x; // <- invalidates the method below, you violate the other
> function's `shared` promise
>    }
>    void notThreadsafe() shared
>    {
>      atomicIncrement(&x);
>    }
> }

So the above program is invalid. Is it compilable with your added 
allowance of implicit casting to shared? If it's not compilable, why 
not? If it is compilable, how in the hell does your proposal help 
anything? I get the exact behavior today without any changes (except 
today, I need to explicitly cast, which puts the onus on me).

> 
> struct Atomic(T)
> {
>    void opUnary(string op : "++")() shared { atomicIncrement(&val); }
>    private T val;
> }
> struct Threadsafe
> {
>    Atomic!int x;
>    void local()
>    {
>      ++x;
>    }
>    void threadsafe() shared
>    {
>      ++x;
>    }
> }
> 
> Naturally, local() is redundant, and it's perfectly fine for a
> thread-local to call threadsafe() via implicit conversion.

In this case, yes. But that's not because of anything the compiler can 
prove.

How does Atomic work? I thought shared data was not usable? I'm being 
pedantic because every time I say "well at some point you must be able 
to modify things", you explode.

Complete the sentence: "In order to read or write shared data, you have 
to ..."

> 
> Here's another one, where only a subset of the object is modeled to be
> threadsafe (this is particularly interesting to me):
> 
> struct Threadsafe
> {
>    int x;
>    Atomic!int y;
> 
>    void notThreadsafe()
>    {
>      ++x;
>      ++y;
>    }
>    void threadsafe() shared
>    {
>      ++y;
>    }
> }
> 
> In these examples, the thread-local function *does not* undermine the
> threadsafety of threadsafe(), it MUST NOT undermine the threadsafety
> of threadsafe(), or else threadsafe() **IS NOT THREADSAFE**.
> In the second example, you can see how it's possible and useful to do
> thread-local work without invalidating the objects threadsafety
> commitments.
> 
> 
> I've said this a bunch of times, there are 2 rules:
> 1. shared inhibits read and write access to members
> 2. `shared` methods must be threadsafe
> 
>>From there, shared becomes interesting and useful.
> 

Given rule 1, how does Atomic!int actually work, if it can't read or 
write shared members?
For rule 2, how does the compiler actually prove this?

Any programming by convention, we can do today. We can implement 
Atomic!int with the current compiler, using unsafe casts inside @trusted 
blocks.

-Steve


More information about the Digitalmars-d mailing list