shared - i need it to be useful

Manu turkeyman at gmail.com
Sun Oct 21 23:49:13 UTC 2018


On Sun, Oct 21, 2018 at 11:31 AM Manu <turkeyman at gmail.com> wrote:
>
> On Sun., 21 Oct. 2018, 2:55 am Walter Bright via Digitalmars-d,
> <digitalmars-d at puremagic.com> wrote:
> >
> > On 10/20/2018 11:24 AM, Manu wrote:
> > > This is an unfair dismissal.
> >
> > It has nothing at all to do with fairness. It is about what the type system
> > guarantees in @safe code. To repeat, the current type system guarantees in @safe
> > code that T* and shared(T)* do not point to the same memory location.
> >
> > Does your proposal maintain that or not? It's a binary question.
>
> By the definition Nick pulled from Wikipedia and posted for you a few
> posts back, yes, my proposal satisfies Wikipedia's definition of no
> aliasing. I understand that property is critical, and I have carefully
> designed for it.
>
> > > I'm not sure you've understood the proposal.
> > > This is the reason for the implicit conversion. It provides safe
> > > transition.
> >
> > I don't see any way to make an implicit T* to shared(T)* safe, or vice versa.
> > The T* code can create more aliases that the conversion doesn't know about, and
> > the shared(T)* code can hand out aliases to other threads. So it all falls to
> > pieces.
>
> T* can't make additional T* aliases on other threads; there can only
> be one thread with T*.
> shared(T)* can not make a T*.
> shared(T)* has no read or write access, so it's not an alias of T* by
> Wikipedia's definition.
>
> Only threadsafe functions can do anything to T.
> The leap of faith is; some @trusted utility functions at the bottom of
> the shared stack makes a promise that it is threadsafe, and must
> deliver that promise.
> I don't think this is unreasonable; this is the nature of @trusted
> functions, they make a promise, and they must keep it.
> If the trusted function does not lie, then the chain of trust holds
> upwards through the stack.
>
> The are very few such trusted functions in practise. Like, similar to
> the number of digits you have.
>
> > Using a 'scope' qualifier won't work, because 'scope' isn't transitive,
> > while shared is, i.e. U** and shared(U*)*.
>
> I don't think I depend on scope in any way.
> That was an earlier revision of thinking in an older thread.
>
> >  > I'm not sure how to clarify it, what can I give you?
> >
> > Write a piece of code that does such an implicit conversion that you argue is
> > @safe. Make the code as small as possible. Your example:
> >
> >  > int* a;
> >  > shared(int)* b = a;
> >
> > This is not safe.
> >
> > ---- Manu's Proposal ---
> > @safe:
> > int i;
> > int* a = &i;
> > StartNewThread(a); // Compiles! Coder has no idea!
> >
> > ... in the new thread ...
> > void StartOfNewThread(shared(int)* b) {
> >
> >      ... we have two threads accessing 'i',
> >      one thinks it is shared, the other unshared,
> >      and StartOfNewThread() has no idea and anyone
> >      writing code for StartOfNewThread() has no way
> >      to know anything is wrong ...
> >
> >      lockedIncrement(b);  // Data Race!
> > }
>
> This program doesn't compile. You receive an error because it is not safe.
> The function is `lockedIncrement(int*)`. It can't receive a shared
> argument; the function is not threadsafe by my definition.
> You have written a program that produces the expected error that
> alerts you that you have tried to do un- at safe and make a race.
>
> Stanislav produced this same program, and I responded with the correct
> program a few posts back.
> I'll repeat it here; the @safe program to model this interaction is:
>
> @safe:
>
> // function is NOT threadsafe by my definition, can not be called on
> shared arguments
> void atomicIncrement(int*);
>
> struct Atomic(T)
> {
>   // encapsulare the unsafe data so it's inaccessible by any unsafe means
>   private T val;
>
>   // perform the unsafe cast in a trusted function
>   // we are able to assure a valid calling context by encapsulating
> the data above
>   void opUnary(string op : "++")() shared @trusted {
> atomicIncrement(cast(T*)&val); }
> }
>
> Atomic!int i;
> Atomic!int* a = &i;
> StartNewThread(a); // Compiles, of course!
> ++i; // no race
>
> ... in the new thread ...
> void StartOfNewThread(shared(Atomic!int)* b) {
>   ... we have two threads accessing 'i', one has thread-local access,
> this one has a restricted shared access.
>   here, we have a shared instance, so we can only access `b` via
> threadsafe functions.
>   as such, we can manipulate `b` without fear.
>   ++i; // no race!
> }
>
>
> > Your proposal means that the person writing the lockedIncrement(), which is a
> > perfectly reasonable thing to do, simply cannot write it in a way that has a
> > @safe interface
>
> Correct, the rules of my proposal apply to lockedIncrement(). They
> apply to `shared` generally.
> lockedIncrement() is not a threadsafe function. You can't call it on a
> shared instance, because `int`s API (ie, all intrinsic operations) are
> not threadsafe.
> lockedIncrement() can't promise threadsafe access to `shared(int)*`,
> so the argument is not shared.
>
> Your program made the correct compile error about doing unsafety, but
> the location of the compile error is different under my proposal;
> complexity is worn by the shared library author, rather than every
> calling user ever.
> I think my proposal places the complexity in the right location.
> `shared` is intrinsically dangerous; it's not reasonable to ask every
> user that ever calls a shared API to write unsafe code when when
> calling. That's just plain bad design.
>
> > because the person writing the lockedIncrement() library
> > function has no way to know that the data it receives is actually unshared data.
>
> The author of `shared` tooling must assure a valid context such that
> its threadsafety promises are true. Atomic(T) does that with a private
> member.
> The @safe way to interact with atomics is to use the Atomic utility
> type I showed above.
> That is one such @trusted tool that I talk about as being "at the
> bottom of the stack".
> It is probably joined by a mutex/semaphore, and some
> containers/queues. That is probably all the things, and other things
> would be @safe compositions of those tools.
>
> > I.e. @trusted code is obliged to proved a safe interface. Your proposal makes
> > that impossible because the compiler would allow unshared data to be implicitly
> > typed as shared.
>
> What? No.
> Please, try and understand my proposal...


Did you read this email? It seems you didn't read this email... Please read it.


More information about the Digitalmars-d mailing list