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