shared - i need it to be useful

Manu turkeyman at gmail.com
Thu Oct 18 02:26:23 UTC 2018


On Wed, Oct 17, 2018 at 6:50 PM Steven Schveighoffer via Digitalmars-d
<digitalmars-d at puremagic.com> wrote:
>
> On 10/17/18 6:37 PM, Manu wrote:
> > On Wed, Oct 17, 2018 at 12:35 PM Steven Schveighoffer via
> > Digitalmars-d <digitalmars-d at puremagic.com> wrote:
> >>
> >> 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?
> >
> > And that shared(int)* provides no access. No other thread with that
> > pointer can do anything with it.
>
> So then it's a misnomer -- it's not really shared, because I can't do
> anything with it.

**EXACTLY**.. this is the key to the entire design that allows for the
implicit promotion.

> >>> 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?
> >
> > You can call shared methods. They promise threadsafety.
> > That's a small subset of the program, but that's natural; only a very
> > small subset of the program is safe to be called from a shared
> > context.
>
> All I can see is that a shared method promises to be callable on shared
> or unshared data. In essence, it promises nothing.

How is that nothing? It promises threadsafety, such that it can be
called on shared data. Normally, you can do NOTHING with shared data
short of unsafe operations.
It is always safe to call a threadsafe function on an unshared thing,
so it should be possible.
`shared` opts into a strong commitment to threadsafety that allows for
a shared thing to interact with it, otherwise, no interaction is
possible.

I don't understand how you can claim it promises nothing; this is an
extremely strong promise!

> It's the programmer who must implement the thread safety, and there
> really is no help at all from the compiler for this.

I hope we can work on this moving forwards. There are probably some
things we can do... but even without compiler support, in reality,
there are probably only a small number of core pieces of tooling, but
most authors of a shared thing will just aggregate core tools and call
through to their API's.
Most shared code will use tools written by experts, and implement
nothing fancy or magical themselves.

> At some level,
> there will be either casts, or intrinsics, both of which are unsafe
> without knowing all the context of the object.

This will tend to be in the core tooling, written by an expert.
The whole point of my design is to remove friction from the USERS of
those tools, such that they can create aggregate functionality safely
without doing anything unsafe.

> In any case, it's simply
> a false guarantee of thread safety, which might as well be a convention
> of "any function which starts with TS_ is supposed to be thread safe".

What makes it a false guarantee? It allows you to have confidence in
the stack built on top of some core tools.
The whole point here is to minimise the number of people writing code
like that. I'm trying to make shared generally useful so that people
don't have to engage with it at a low-level.

If you see an unsafe case in a shared function (beyond the core
tooling), then you should immediately be suspicious, and this is a
deliberate part of my goal here. The point here is to produce safer
and more reliable code by defining access rules that lead to confident
threadsafety.

What you describe is what we have now.

> shared in the current form promises one thing and one thing only -- data
> marked as shared is actually sharable between threads, and data not
> marked as shared is actually not shared between threads.

Ummm. The current form might *say* that, but you still have full
unregulated access to all members, and it's completely unsafe, with no
attempt to mitigate that.
This is a new definition. Take it for what it's worth. The old
definition is mostly worthless.
I'm trying to make something that's interesting and useful.

> This new regime you are proposing does nothing extra or new, except break that guarantee.

There are heaps of advantages:
1. If I have a shared thing, I know I can't ruin its state by
manipulating it arbitrarily like I can now
2. I am now able to describe threadsafe objects
3. I relieve users from mental strain of trying to understand how to
correctly and safely interact with shared API's, because they can have
confidence that a shared thing can only perform threadsafe activity
4. I'm trying to give shared a simple, meaningful and *useful*
definition, that's easy to understand and communicate. I believe the
only reason people are having trouble, because they are taking it in
contrast, rather than as it is.

> >> 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.
> >
> > No. No casting! This is antiquated workflow.. I'm not trying to take
> > it away from you, but it's not an interesting model for the future.
> > `shared` can model more than just that.
> > You can call threadsafe methods. Shared methods explicitly dictate how
> > the system works, and in a very clear and obvious/intuitive way.
> >
> > The implicit cast makes using threadsafe objects more convenient when
> > you only have one, which is extremely common.
>
> The implicit cast means that you have to look at more than just your
> method. You have to look at the entire module, and figure out all the
> interactions, to see if the thread safe method actually is thread safe.
> That's programming by convention, and fully trusting the programmer.

I don't understand... how can the outer context affect the
threadsafety of a properly encapsulated thing?
Back to this trivial example:

struct Atomic(T)
{
  void opUnary(string op : "++")() shared { atomicInc(cast(T*)&val); }
  private T val;
}

This is properly encapsulated... the promise is firm.
If some threadsafe machinery is sufficiently complex that it has a
wide roam and you can't reason about whether the method is actually
threadsafe, then you have no business writing threadsafe machinery;
use a library.
This design, nor the current design, nor any other design can possibly help you.

> I don't think this thread is going anywhere, so I'll just have to wait
> and see if someone else can explain it better. I'm a firm no on implicit
> casting from mutable to shared.

You need to take it for an intellectual spin. Show me how it's corrupt
rather than just presenting discomfort with the idea in theory.
You're addicted to some concepts that you've carried around for a long
time. There is no value in requiring casts, they're just a funky
smell, and force the user to perform potentially unsafe manual
conversions, or interactions that they don't understand.

Implicit conversion is for allowing/encouraging what is safe. Calling
threadsafe functions is safe. You should be able to call threadsafe
functions.


More information about the Digitalmars-d mailing list