On Tue, Oct 16, 2018 at 11:30 AM Steven Schveighoffer via
Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> On 10/16/18 2:10 PM, Manu wrote:
> > On Tue, Oct 16, 2018 at 6:35 AM Steven Schveighoffer via Digitalmars-d
> > <digitalmars-d@puremagic.com> wrote:
> >>
> >> On 10/16/18 9:25 AM, Steven Schveighoffer wrote:
> >>> On 10/15/18 2:46 PM, Manu wrote:
> >>
> >>>>>  From there, it opens up another critical opportunity; T* -> shared(T)*
> >>>> promotion.
> >>>> Const would be useless without T* -> const(T)* promotion. Shared
> >>>> suffers a similar problem.
> >>>> If you write a lock-free queue for instance, and all the methods are
> >>>> `shared` (ie, threadsafe), then under the current rules, you can't
> >>>> interact with the object when it's not shared, and that's fairly
> >>>> useless.
> >>>>
> >>
> >> Oh, I didn't see this part. Completely agree with Timon on this, no
> >> implicit conversions should be allowed.
> >
> > Why?
>
> int x;
>
> shared int *p = &x; // allow implicit conversion, currently error
>
> passToOtherThread(p);
>
> useHeavily(&x);

What does this mean? It can't do anything... that's the whole point here.
I think I'm struggling here with people bringing presumptions to the
thread. You need to assume the rules I define in the OP for the
experiment to work.

> How is this safe?

Because useHeavily() can't read or write to x.

> Thread1 is using x without locking, while the other
> thread has to lock. In order for synchronization to work, both sides
> have to agree on a synchronization technique and abide by it.

Only the owning thread can access x, the shared instance can't access x at all.

If some code somewhere decides it wants to cast-away shared, then it
needs to determine ownership via some other means. It needs to be
confident that the original owner yielded ownership, and any API that
leads to this behaviour would need to be designed in such a way to
encourage correct behaviour.
That's *exactly* how it is now... I haven't changed anything from this
perspective.

> >> If you want to have a lock-free implementation of something, you can
> >> abstract the assignments and reads behind the proper mechanisms anyway,
> >> and still avoid locking (casting is not locking).
> >
> > Sorry, I don't understand what you're saying. Can you clarify?
> >
>
> I'd still mark a lock-free implementation shared, and all its methods
> shared. shared does not mean you have to lock, just cast away shared.

Shared *should* mean that the function is threadsafe, and you are safe
to call it from a shared instance.
If a function is not shared, then you MUST cast away shared, and that
implies that you need to use external means to create a context where
you have thread-local ownership of the instance (usually with a
mutex).

You shouldn't need to cast-away shared to make a safe function call.
By casting away shared, you also gain access to all the non-threadsafe
methods and members.
Casting shared away to call a shared method makes a safe access into a
potentially unsafe access.

It's unacceptable to case-away shared. It should be as unacceptable as
casting const away. The only situation where it's okay is where you're
externally verifying thread-locality by external means, and that's
subject to your broader systemic design.

> A lock-free container still has to do some special things to make sure it
> avoids races, and having an "unusable" state aids in enforcing this.

Can you explain how my proposal doesn't model this very neatly?

Reply via email to