shared - i need it to be useful

Manu turkeyman at gmail.com
Sat Oct 20 06:48:41 UTC 2018


On Fri, Oct 19, 2018 at 9:45 AM Steven Schveighoffer via Digitalmars-d
<digitalmars-d at puremagic.com> wrote:
>
> On 10/18/18 9:09 PM, Manu wrote:
> > On Thu, Oct 18, 2018 at 5:30 PM Timon Gehr via Digitalmars-d
> > <digitalmars-d at puremagic.com> wrote:
> >>
> >> On 18.10.18 23:34, Erik van Velzen wrote:
> >>> If you have an object which can be used in both a thread-safe and a
> >>> thread-unsafe way that's a bug or code smell.
> >>
> >> Then why do you not just make all members shared? Because with Manu's
> >> proposal, as soon as you have a shared method, all members effectively
> >> become shared.
> >
> > No they don't, only facets that overlap with the shared method.
> > I tried to present an example before:
> >
> > struct Threadsafe
> > {
> >    int x;
> >    Atomic!int y;
> >    void foo() shared { ++y; } // <- shared interaction only affects 'y'
> >    void bar() { ++x; ++y; } // <- not threadsafe, but does not violate
> > foo's commitment; only interaction with 'y' has any commitment
> > associated with it
> >    void unrelated() { ++x; } // <- no responsibilities are transposed
> > here, you can continue to do whatever you like throughout the class
> > where 'y' is not concerned
> > }
> >
> > In practise, and in my direct experience, classes tend to have exactly
> > one 'y', and either zero (pure utility), or many such 'x' members.
> > Threadsafe API interacts with 'y', and the rest is just normal
> > thread-local methods which interact with all members thread-locally,
> > and may also interact with 'y' while not violating any threadsafety
> > commitments.
>
> I promised I wouldn't respond, I'm going to break that (obviously).
>
> But that's because after reading this description I ACTUALLY understand
> what you are looking for.
>
> I'm going to write a fuller post later, but I can't right now. But the
> critical thing here is, you want a system where you can divvy up a type
> into pieces you share and pieces you don't. But then you *don't* want to
> have to share only the shared pieces. You want to share the whole thing
> and be sure that it can't access your unshared pieces.
>
> This critical requirement makes things a bit more interesting. For the
> record, the most difficult thing to reaching this understanding was that
> whenever I proposed anything, your answer was something like 'I just
> can't work with that', and when I asked why, you said 'because it's
> useless', etc. Fully explaining this point is very key to understanding
> your thinking.
>
> To be continued...

I'm glad that there's movement here... but I'm still not 100%
convinced you understood me; perhaps getting close though.
I only say that because your description above has a whole lot more
words and complexity than is required to express my proposal. If you
perceive that complexity in structural terms, then I am still not
clearly understood.

> "divvy up a type into pieces"

This is an odd mental model of what I'm saying, and I can't sympathise
with those words, but if they work for you, and we agree on the
semantics, then sure...
If you write an object with some const methods and some non-const
methods, then take a const instance of the object... you can only call
the const methods. Have you 'divvied up the type' into a const portion
and a non-const portion? If the answer is yes, then I can accept your
description.

I would talk in terms of restriction:
An object has 4 functions, 2 are mutable, 2 are const... you apply
const to the type and you are *restricted* to only calling the 2 const
functions.
An object has 4 functions, 2 are unsahred, 2 are shared... you apply
shared to the type and you are *restricted* to only calling the 2
shared (threadsafe) functions.

I haven't 'broken the type up', I'm just restricting what you can do
to it from within a particular context. In the const context, you
can't mutate it. In the shared context, you can't do un-threadsafe to
it, and the guarantee of that is embedded in the rules:
1. shared data can not be read or written
2. shared methods must be threadsafe
  a. this may require that they be @trusted at the low-level, like the
methods of `Atomic(T)`
  b. no other method may violate the shared method's promise,
otherwise it does not actually deliver its promise
    i. I have extensive experience with this and it's just not a
problem in practise, but compiler technology to assist would be
welcome!
    ii. This does NOT mean the not-shared methods are somehow shared;
they just need to be careful when interacting with some (usually
small) subset of members

This design implies strong encapsulation, but that's a naturally
occurring tendency implementing anything that's threadsafe.
As a helpful best-practise to assure that non-shared methods don't
undermine a shared method's commitment; prefer to interact with
volatile members via accessors or properties that are themselves
shared (can be private if you like).
You will find that it's not actually hard to deliver on the object's commitment.

If you write tooling that is at the level one-up from the bottom of
the stack or higher, it should be unusual that you ever need to write
a @trusted method, in which case delivering on *your* shared methods
promise is implicit.


More information about the Digitalmars-d mailing list