shared - i need it to be useful

Manu turkeyman at gmail.com
Wed Oct 17 18:46:18 UTC 2018


On Wed, Oct 17, 2018 at 10:30 AM Steven Schveighoffer via
Digitalmars-d <digitalmars-d at puremagic.com> wrote:
>
> On 10/17/18 12:27 PM, Nicholas Wilson wrote:
> > On Wednesday, 17 October 2018 at 15:51:04 UTC, Steven Schveighoffer wrote:
> >> On 10/17/18 9:58 AM, Nicholas Wilson wrote:
> >>> On Wednesday, 17 October 2018 at 13:25:28 UTC, Steven Schveighoffer
> >>> wrote:
> >>>> It's identical to the top one. You now have a new unshared reference
> >>>> to shared data. This is done WITHOUT any agreed-upon synchronization.
> >>>
> >>> It isn't, you typo'd it (I originally missed it too).
> >>>> int *p3 = cast(int*)p2;
> >>>
> >>> vs
> >>>
> >>>> int *p3 = p;
> >>
> >> It wasn't a typo.
> >
> > The first example assigns p2, the second assigns p (which is thread
> > local) _not_ p2 (which is shared), I'm confused.
> >
>
> Here they are again:
>
> int *p;
> shared int *p2 = p;
> int *p3 = cast(int*)p2;
>
> int *p;
> shared int *p2 = p;
> int *p3 = p;
>
>
> I'll put some asserts in that show they accomplish the same thing:
>
> assert(p3 is p2);
> assert(p3 is p);
> assert(p2 is p);
>
> 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.
There's only one owning thread, and you can't violate that without unsafe casts.

> 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.

> That's why shared/unshared is more akin to mutable/immutable than
> mutable/const.

Only if you misrepresent my suggestion.

> 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.
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);
  }
}

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.

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.


More information about the Digitalmars-d mailing list