Re: shared - i need it to be useful

2018-10-22 Thread Manu via Digitalmars-d
On Mon, Oct 22, 2018 at 6:00 AM Timon Gehr via Digitalmars-d
 wrote:
>
> On 22.10.18 12:26, Timon Gehr wrote:
> > ---
> > module borked;
> >
> > void atomicIncrement(int* p)@system{
> >  import core.atomic;
> >  atomicOp!("+=",int,int)(*cast(shared(int)*)p,1);
> > }
> >
> > struct Atomic(T){
> >  private T val;
> >  void opUnary(string op : "++")() shared @trusted {
> >  atomicIncrement(cast(T*));
> >  }
> > }
> > void main()@safe{
> >  Atomic!int i;
> >  auto a=&[i][0];// was: Atomic!int* a = 
> >  import std.concurrency;
> >  spawn((shared(Atomic!int)* a){ ++*a; }, a);
> >  ++i.val; // race
> > }
> > ---
>
> Obviously, this should have been:
>
> ---
> module borked;
>
> void atomicIncrement(int*p)@system{
>  import core.atomic;
>  atomicOp!"+="(*cast(shared(int)*)p,1);
> }
> struct Atomic(T){
>  private T val;
>  void opUnary(string op:"++")()shared @trusted{
>  atomicIncrement(cast(T*));
>  }
> }
> void main()@safe{
>  auto a=new Atomic!int;
>  import std.concurrency;
>  spawn((shared(Atomic!int)* a){ ++*a; }, a);
>  ++a.val; // race
> }
> ---
>
> (I was short on time and had to fix Manu's code because it was not
> actually compilable.)

Nitpick; atomicOp does not receive a shared arg under my proposal,
it's not a threadsafe function by definition as discussed a few times.


Re: shared - i need it to be useful

2018-10-22 Thread Manu via Digitalmars-d
On Mon, Oct 22, 2018 at 4:50 AM Stanislav Blinov via Digitalmars-d
 wrote:
>
> On Monday, 22 October 2018 at 00:22:19 UTC, Manu wrote:
>
> > No no, they're repeated, not scattered, because I seem to have
> > to keep repeating it over and over, because nobody is reading
> > the text, or perhaps imaging there is a lot more text than
> > there is.
> > ...
> > You mean like every post in opposition which disregards the
> > rules and baselessly asserts it's a terrible idea? :/
> > ...
> > I responded to your faulty program directly with the correct
> > program, and you haven't acknowledged it. Did you see it?
>
> Right back at you.
>
> Quote:
>
> I think this is a typical sort of construction:
>
> struct ThreadsafeQueue(T)
> {
>void QueueItem(T*) shared;
>T* UnqueueItem() shared;
> }
>
> struct SpecialWorkList
> {
>struct Job { ... }
>
>void MakeJob(int x, float y, string z) shared  // <- any thread
> may
> produce a job
>{
>  Job* job = new Job; // <- this is thread-local
>  PopulateJob(job, x, y, z); // <- preparation of a job might be
> complex, and worthy of the SpecialWorkList implementation
>
>  jobList.QueueItem(job);  // <- QueueItem encapsulates
> thread-safety, no need for blunt casts
>}
>
>void Flush() // <- not shared, thread-local consumer
>{
>  Job* job;
>  while (job = jobList.UnqueueItem()) // <- it's obviously safe
> for
> a thread-local to call UnqueueItem even though the implementation
> is
> threadsafe
>  {
>// thread-local dispatch of work...
>// perhaps rendering, perhaps deferred destruction, perhaps
> deferred resource creation... whatever!
>  }
>}
>
>void GetSpecialSystemState() // <- this has NOTHING to do with
> the
> threadsafe part of SpecialWorkList
>{
>  return os.functionThatChecksSystemState();
>}
>
>// there may be any number of utility functions that don't
> interact
> with jobList.
>
> private:
>void PopulateJob(ref Job job, ...)
>{
>  // expensive function; not thread-safe, and doesn't have any
> interaction with threading.
>}
>
>ThreadsafeQueue!Job jobList;
> }
>
>
> This isn't an amazing example, but it's typical of a thing that's
> mostly thread-local, and only a small controlled part of it's
> functionality is thread-safe.
> The thread-local method Flush() also deals with thread-safety
> internally... because it flushes a thread-safe queue.
>
> All thread-safety concerns are composed by a utility object, so
> there's no need for locks, magic, or casts here.
>
> EndQuote;
>
> The above:
> 1) Will not compile, not currently, not under your proposal
> (presumably you forgot in frustration to cast before calling
> PopulateJob?..)

I did correct that line (along with an apology) on my very next post;
it would probably be a member of Job... or any manner of other code.
That is the least interesting line in the program

> 2) Does not in any way demonstrate a practical @safe application
> of an implicit conversion. As I wrote in the original response to
> that code, with that particular code it seems more like you just
> need forwarding methods that call `shared` methods under the hood
> (i.e. MakeJob), and it'd be "nice" if you didn't have to write
> those and could just call `shared` MakeJob on an un-`shared`
> reference directly. But these are all assumptions without seeing
> the actual usage.
>
> Please just stop acting like everyone here is opposing *you*.

You're right, it's mostly you.

>  All
> you're doing is dismissing everyone with a "nuh-huh, you no
> understand, you bad". If it was just me, fine, it would mean I'm
> dumb and not worthy of this discussion. But this isn't the case,
> which means *you are not getting your point across*. And yet
> instead of trying to fix that, you're getting all snarky.

I mean, it's fair, but it's pretty bloody hypocritical coming from you!
I think it's fair to point out that your high-frequency, persistent,
and unwavering hostility from post #1 across all my recent threads (at
least, until I told you to GF) is the primary reason I'm frustrated
here.
You can own part responsibility for my emotion.


Re: shared - i need it to be useful

2018-10-22 Thread Manu via Digitalmars-d
On Mon, Oct 22, 2018 at 3:30 AM Timon Gehr via Digitalmars-d
 wrote:
>
> On 22.10.18 02:54, Manu wrote:
> > On Sun, Oct 21, 2018 at 5:40 PM Timon Gehr via Digitalmars-d
> >  wrote:
> >>
> >> On 21.10.18 21:04, Manu wrote:
> >>> On Sun, Oct 21, 2018 at 12:00 PM Timon Gehr via Digitalmars-d
> >>>  wrote:
> 
>  On 21.10.18 17:54, Nicholas Wilson wrote:
> >
> >> As soon as that is done, you've got a data race with the other
> >> existing unshared aliases.
> >
> > You're in @trusted code, that is the whole point. The onus is on the
> > programmer to make that correct, same with regular @safe/@trusted@system
> > code.
> 
>  Not all of the parties that participate in the data race are in @trusted
>  code. The point of @trusted is modularity: you manually check @trusted
>  code according to some set of restrictions and then you are sure that
>  there is no memory corruption.
> 
>  Note that you are not allowed to look at any of the @safe code while
>  checking your @trusted code. You will only see an opaque interface to
>  the @safe code that you call and all you know is that all the @safe code
>  type checks according to @safe rules. Note that there might be an
>  arbitrary number of @safe functions and methods that you do not see.
> 
>  Think about it this way: you first write all the @trusted and @system
>  code, and some evil guy who does not like you comes in after you looks
>  at your code and writes all the @safe code. If there is any memory
>  corruption, it will be your fault and you will face harsh consequences.
>  Now, design the @safe type checking rules. It won't be MP!
> 
>  Note that there may well be a good way to get the good properties of MP
>  without breaking the type system, but MP itself is not good because it
>  breaks @safe.
> >>>
> >>> Show me. Nobody has been able to show that yet. I'd really like to know 
> >>> this.
> >>>
> >>
> >> I just did,
> >
> > There's no code there... just a presumption that the person who wrote
> > the @trusted code did not deliver the promise they made.
> > ...
>
> Yes, because there is no way to write @trusted code that holds its
> promise while actually doing something interesting in multiple threads
> if @safe code can implicitly convert from unshared to shared.

How do my examples prior fail to hold their promise?
struct S
{
  private int x;
  void method() shared @trusted { /* safely manipulate x */ }
}

How can you not trust that function? How can a 3rd party invalidate
that functions promise? `x` is inaccessible.
S can be thread-local or shared as much as you like, and it's safe,
and I don't know how a 3rd party could @safely undermine that?

> >> but if you really need to, give me a non-trivial piece of> correct 
> >> multithreaded code that accesses some declared-unshared field
> >> from a shared method and I will show you how the evil guy would modify
> >> some @safe code in it and introduce race conditions. It needs to be your
> >> code, as otherwise you will just claim again that it is me who wrote bad
> >> @trusted code.
> >
> > You can pick on any of my prior code fragments. They've all been ignored so 
> > far.
> >
>
> I don't want "code fragments". Show me the real code.
>
> I manually browsed through posts now (thanks a lot) and found this
> implementation:
>
> struct Atomic(T){
>void opUnary(string op : "++")() shared { atomicIncrement(); }
>private T val;
> }
>
> This is @system code. There is no @safe or @trusted here, so I am
> ignoring it.
>
>
> Then I browsed some more, because I had nothing better to do, and I
> found this. I completed it so that it is actually compilable, except for
> the unsafe implicit conversion.
>
> Please read this code, and then carefully read the comments below it
> before you respond. I will totally ignore any of your answers that
> arrive in the next two hours.
>
> ---
> module borked;
>
> void atomicIncrement(int* p)@system{
>  import core.atomic;
>  atomicOp!("+=",int,int)(*cast(shared(int)*)p,1);
> }
>
> struct Atomic(T){
>  private T val;
>  void opUnary(string op : "++")() shared @trusted {
>  atomicIncrement(cast(T*));
>  }
> }
> void main()@safe{
>  Atomic!int i;
>  auto a=&[i][0];// was: Atomic!int* a = 
>  import std.concurrency;
>  spawn((shared(Atomic!int)* a){ ++*a; }, a);
>  ++i.val; // race
> }
> ---
>
>
> Oh no! The author of the @trusted function (i.e. you) did not deliver on
> the promise they made!
>
> Now, before you go and tell me that I am stupid because I wrote bad
> code, consider the following:
>
> - It is perfectly @safe to access private members from the same module.
>
> - You may not blame the my @safe main function for the problem. It is
> @safe, so it cannot be blamed for UB. Any UB is the result of a bad
> @trusted function, a compiler bug, or hardware failure.
>
> - The only @trusted function in this 

Re: shared - i need it to be useful

2018-10-22 Thread Simen Kjærås via Digitalmars-d

On Monday, 22 October 2018 at 14:31:28 UTC, Timon Gehr wrote:

On 22.10.18 16:09, Simen Kjærås wrote:

On Monday, 22 October 2018 at 13:40:39 UTC, Timon Gehr wrote:

module reborked;
import atomic;

void main()@safe{
    auto a=new Atomic!int;
    import std.concurrency;
    spawn((shared(Atomic!int)* a){ ++*a; }, a);
    ++a.tupleof[0];
}


Finally! Proof that MP is impossible. On the other hand, why 
the hell is that @safe? It breaks all sorts of guarantees 
about @safety. At a minimum, that should be un-@safe.


Filed in bugzilla: 
https://issues.dlang.org/show_bug.cgi?id=19326


--
   Simen


Even if this is changed (and it probably should be), it does 
not fix the case where the @safe function is in the same 
module. I don't think it is desirable to change the definition 
of @trusted such that you need to check the entire module if it 
contains a single @trusted function.


If I can break safety of some (previously correct) code by 
editing only @safe code, then that's a significant blow to 
@safe. I think we need a general way to protect data from being 
manipulated in @safe code in any way, same module or not.


What do you mean by 'previously correct'?

struct Array(T) {
@safe:
private int* ptr;
private int length;
@disable this();
this(int n) @trusted {
ptr = new int[n].ptr;
length = n;
foreach (ref e; ptr[0..length])
e = 123;
}
@trusted ref int get(int idx) {
assert(idx < length);
return ptr[idx];
}
}

unittest {
auto s = Array!int(1);
assert(s.get(0) == 123);
}

Is this correct code?

What if I add this:

@safe void bork(T)(ref Array!T s) {
s.length *= 2;
}

unittest {
auto s = Array!int(1);
bork(s);
assert(s.get(1) == 123); // Out of bounds!
}

--
  Simen


Re: shared - i need it to be useful

2018-10-22 Thread John Colvin via Digitalmars-d

On Monday, 22 October 2018 at 00:22:19 UTC, Manu wrote:
On Sun, Oct 21, 2018 at 2:35 PM Walter Bright via Digitalmars-d 
 wrote:


On 10/21/2018 2:08 PM, Walter Bright wrote:
> On 10/21/2018 12:20 PM, Nicholas Wilson wrote:
>> Yes, but the problem you describe is arises from implicit 
>> conversion in the other direction, which is not part of the 
>> proposal.

>
> It's Manu's example.

Then I don't know what the proposal is. Pieces of it appear to 
be scattered over numerous posts, mixed in with other text,


No no, they're repeated, not scattered, because I seem to have 
to keep repeating it over and over, because nobody is reading 
the text, or perhaps imaging there is a lot more text than 
there is.


I can go look at the original post - and I have - but while it 
may strictly speaking contain all the information I need, the 
amount of time and brain power it would take for me to comprehend 
the consequences is pretty large. I assume you have done a lot of 
that work already and could save everyone a lot of time by 
putting together a quick document that covers some of that, with 
good examples. I'm not going to read {1,2,3}00 messages full of 
irritated bidirectional miscommunication to try and understand 
this unless I really have to, and I assume others feel similarly.


Re: shared - i need it to be useful

2018-10-22 Thread Timon Gehr via Digitalmars-d

On 22.10.18 16:09, Simen Kjærås wrote:

On Monday, 22 October 2018 at 13:40:39 UTC, Timon Gehr wrote:

module reborked;
import atomic;

void main()@safe{
    auto a=new Atomic!int;
    import std.concurrency;
    spawn((shared(Atomic!int)* a){ ++*a; }, a);
    ++a.tupleof[0];
}


Finally! Proof that MP is impossible. On the other hand, why the hell is 
that @safe? It breaks all sorts of guarantees about @safety. At a 
minimum, that should be un-@safe.


Filed in bugzilla: https://issues.dlang.org/show_bug.cgi?id=19326

--
   Simen


Even if this is changed (and it probably should be), it does not fix the 
case where the @safe function is in the same module. I don't think it is 
desirable to change the definition of @trusted such that you need to 
check the entire module if it contains a single @trusted function.


If I can break safety of some (previously correct) code by editing only 
@safe code, then that's a significant blow to @safe. I think we need a 
general way to protect data from being manipulated in @safe code in any 
way, same module or not.


Re: shared - i need it to be useful

2018-10-22 Thread Atila Neves via Digitalmars-d

On Monday, 22 October 2018 at 00:22:19 UTC, Manu wrote:
On Sun, Oct 21, 2018 at 2:35 PM Walter Bright via Digitalmars-d 
 wrote:


Then I don't know what the proposal is. Pieces of it appear to 
be scattered over numerous posts, mixed in with other text,


No no, they're repeated, not scattered, because I seem to have 
to keep repeating it over and over, because nobody is reading 
the text, or perhaps imaging there is a lot more text than 
there is.


I've read every post on this thread and I also have the feeling 
that it's scattered. At the very least, I'm 90% confident I don't 
understand what it is you're proposing. Trust me, I'm trying.


I believe that you have a proposal which you believe results in 
@safe multithreaded code. I don't understand how what I've read 
so far would accomplish that. I'm conviced that shared data 
shouldn't be allowed to be written to, but I haven't yet been 
convinced of anything else.


I don't see how it's possible that implicit conversion from 
non-shared to shared can work at all. Yes, I know that in the 
proposal putting `shared` on anything makes it useless, but 
*somehow* that data gets to be used, even if it's by a @trusted 
function that casts away shared. At that point, nothing you do 
thread-safely to the shared data matters if you obtained the 
shared data from an implicit conversion. There may be many many 
aliases to it before it was converted, all of them able to write 
to that memory location believing it's not shared. And it would 
be @safe (but definitely not thread-safe) to do so! This has been 
explained a few times, by multiple people. I haven't seen anyone 
addressing this yet (it's possible it got lost in a sea of text).


I don't even understand why it is you want to cast anything to 
shared anyway - that'd always be a code smell if I saw it during 
code review. If it's shared, type it as such. Or better yet, if 
you can just use immutable.


I understand the frustration of not getting your point across. I 
would like to kindly point out that, if the replies have gotten 
to multiple dozen pages and several well-meaning people still 
don't get it, then the proposal probably isn't as simple as you 
believe it to be.





Re: shared - i need it to be useful

2018-10-22 Thread Simen Kjærås via Digitalmars-d

On Monday, 22 October 2018 at 13:40:39 UTC, Timon Gehr wrote:

module reborked;
import atomic;

void main()@safe{
auto a=new Atomic!int;
import std.concurrency;
spawn((shared(Atomic!int)* a){ ++*a; }, a);
++a.tupleof[0];
}


Finally! Proof that MP is impossible. On the other hand, why the 
hell is that @safe? It breaks all sorts of guarantees about 
@safety. At a minimum, that should be un-@safe.


Filed in bugzilla: https://issues.dlang.org/show_bug.cgi?id=19326

--
  Simen


Re: shared - i need it to be useful

2018-10-22 Thread Timon Gehr via Digitalmars-d

On 22.10.18 15:26, Simen Kjærås wrote:

Here's the correct version:

module atomic;

void atomicIncrement(int* p) @system {
     import core.atomic;
     atomicOp!("+=",int,int)(*cast(shared(int)*)p,1);
}

struct Atomic(T) {
     // Should probably mark this shared for extra safety,
     // but it's not strictly necessary
     private T val;
     void opUnary(string op : "++")() shared @trusted {
     atomicIncrement(cast(T*));
     }
}
-
module unborked;
import atomic;

void main() @safe {
     auto a = new Atomic!int;
     import std.concurrency;
     spawn((shared(Atomic!int)* a){ ++*a; }, a);
     //++i.val; // Cannot access private member
}

Once more, Joe Average Programmer should not be writing the @trusted 
code in Atomic!T.opUnary - he should be using libraries written by 
people who have studied the exact issues that make multithreading hard.


--
   Simen


module reborked;
import atomic;

void main()@safe{
auto a=new Atomic!int;
import std.concurrency;
spawn((shared(Atomic!int)* a){ ++*a; }, a);
++a.tupleof[0];
}


Re: shared - i need it to be useful

2018-10-22 Thread Timon Gehr via Digitalmars-d

On 22.10.18 03:01, Manu wrote:

On Sun, Oct 21, 2018 at 5:55 PM Timon Gehr via Digitalmars-d
 wrote:


On 22.10.18 02:45, Manu wrote:

On Sun, Oct 21, 2018 at 5:35 PM Timon Gehr via Digitalmars-d
 wrote:


On 21.10.18 20:46, Manu wrote:

Shared data is only useful if, at some point, it is read/written, presumably by
casting it to unshared in @trusted code. As soon as that is done, you've got a
data race with the other existing unshared aliases.

If such a race is possible, then the @trusted function is not
threadsafe, so it is not @trusted by definition.
You wrote a bad @trusted function, and you should feel bad.
...


I wonder where this "each piece of code is maintained by only one person
and furthermore this is the only person that will suffer if the code has
bugs" mentality comes from. It is very popular as well as obviously
nonsense.


The simplest way to guarantee that no unsafe access is possible is to
use encapsulation to assure no unregulated access exists.


This only works if untrusted programmers (i.e. programmers who are only
allowed to write/modify @safe code) are not allowed to change your
class. I.e. it does not work.


Have you ever cracked open std::map and 'fixed' it because you thought
it was bad?


(Also, yes, some people do that because std::map does not provide an 
interface to augment the binary search tree.)



Of course not. Same applies here. Nobody 'fixes' core.atomic.Atomic
without understanding what they're doing.



You are not proposing to let core.atomic.Atomic convert to shared
implicitly, you are proposing to do that for all classes.


You can always implicitly convert to shared.


Yes, exactly what I said.


Where did I ever say anything like that? I'm sure I've never said
this.


???

I said that you are proposing to allow implicit conversions to shared 
for all classes, not only core.atomic.Atomic, and the last time you said 
it was the previous sentence of the same post.



How do these transformations of what I've said keep happening?
...


You literally said that nobody changes core.atomic.Atomic. Anyway, even 
if I bought that @safe somehow should not be checked within druntime (I 
don't), bringing up this example does not make for a coherent argument 
why implicit conversion to shared should be allowed for all classes.



You seem to be stuck on the detail whether you can trust the @trusted
author though...


Again: the @safe author is the problem.


I don't follow. The @safe author is incapable of doing threadsafety
violation.


They are capable of doing so as soon as you provide them a @trusted 
function that treats data as shared that they can access as unshared.



They can only combine threadsafe functions.
They can certainly produce a program that doesn't work, and they are
capable of ordering issues, but that's not the same as data-race
related crash bugs.



Accessing private members of aggregates in the same module is @safe. 
tupleof is @safe too.


Re: shared - i need it to be useful

2018-10-22 Thread Simen Kjærås via Digitalmars-d

On Monday, 22 October 2018 at 10:26:14 UTC, Timon Gehr wrote:

module borked;

void atomicIncrement(int* p)@system{
import core.atomic;
atomicOp!("+=",int,int)(*cast(shared(int)*)p,1);
}

struct Atomic(T){
private T val;
void opUnary(string op : "++")() shared @trusted {
atomicIncrement(cast(T*));
}
}
void main()@safe{
Atomic!int i;
auto a=&[i][0];// was: Atomic!int* a = 
import std.concurrency;
spawn((shared(Atomic!int)* a){ ++*a; }, a);
++i.val; // race
}
---


Oh no! The author of the @trusted function (i.e. you) did not 
deliver on the promise they made!


Now, before you go and tell me that I am stupid because I wrote 
bad code, consider the following:


- It is perfectly @safe to access private members from the same 
module.


Yes, so you need to place the @trusted code in a separate module. 
The @trusted code will be very rare (Atomic!T, lockfree queue, 
mutex, semaphore...). This will be in the standard library, 
possibly some other library. You should not be writing your own 
implementations of these. You should not be writing @trusted code 
without very good reason. You should be using @safe functions all 
over your code if at all possible.



- You may not blame the my @safe main function for the problem. 
It is @safe, so it cannot be blamed for UB. Any UB is the 
result of a bad @trusted function, a compiler bug, or hardware 
failure.


No, we blame the fact you have not blocked off non-thread-safe 
access to Atomic!T.val. What you have made is this:


https://i.imgur.com/PnKMigl.jpg

The piece of code to blame is the @trusted function - you can't 
trust it, since non-thread-safe access to Atomic!T.val has not 
been blocked off. As long as anyone can extend its interface, 
Atomic!T can't be thread-safe.


I'll quote myself:


For clarity: the interface of a type is any method, function,
delegate or otherwise that may affect its internals. That
means any free function in the same module, and any
non-private members.


I've actually missed some possibilities there - member functions 
of other types in the same module must also count as part of the 
interface. Because of this wide net, modules that implement 
thread-safe types with shared methods should be short and sweet.




- The only @trusted function in this module was written by you.

You said that there is a third implementation somewhere. If 
that one actually works, I apologize and ask you to please 
paste it again in this subthread.


Here's the correct version:

module atomic;

void atomicIncrement(int* p) @system {
import core.atomic;
atomicOp!("+=",int,int)(*cast(shared(int)*)p,1);
}

struct Atomic(T) {
// Should probably mark this shared for extra safety,
// but it's not strictly necessary
private T val;
void opUnary(string op : "++")() shared @trusted {
atomicIncrement(cast(T*));
}
}
-
module unborked;
import atomic;

void main() @safe {
auto a = new Atomic!int;
import std.concurrency;
spawn((shared(Atomic!int)* a){ ++*a; }, a);
//++i.val; // Cannot access private member
}

Once more, Joe Average Programmer should not be writing the 
@trusted code in Atomic!T.opUnary - he should be using libraries 
written by people who have studied the exact issues that make 
multithreading hard.


--
  Simen


Re: shared - i need it to be useful

2018-10-22 Thread Timon Gehr via Digitalmars-d

On 22.10.18 14:39, Aliak wrote:

On Monday, 22 October 2018 at 10:26:14 UTC, Timon Gehr wrote:

---
module borked;

void atomicIncrement(int* p)@system{
    import core.atomic;
    atomicOp!("+=",int,int)(*cast(shared(int)*)p,1);
}

struct Atomic(T){
    private T val;
    void opUnary(string op : "++")() shared @trusted {
    atomicIncrement(cast(T*));
    }
}
void main()@safe{
    auto a=new Atomic!int;
    import std.concurrency;
    spawn((shared(Atomic!int)* a){ ++*a; }, a);
    ++a.val; // race
}
---


Oh no! The author of the @trusted function (i.e. you) did not deliver 
on the promise they made!


hi, if you change the private val in Atomic to be “private shared T 
val”, is the situation the same?


It's a bit different, because then there is no implicit unshared->shared 
conversion happening, and this discussion is only about that. However, 
without further restrictions, you can probably construct cases where a 
@safe function in one module escapes a private shared(T)* member to 
somewhere else that expects a different synchronization strategy.


Therefore, even if we agree that unshared->shared conversion cannot be 
implicit in @safe code, the 'shared' design is not complete, but it 
would be a good first step to agree that this cannot happen, such that 
we can then move on to harder issues.


E.g. probably it would be good to have something like @trusted data that 
cannot be manipulated from @safe code, such that @trusted functions can 
rely on some invariants.


Re: shared - i need it to be useful

2018-10-22 Thread Timon Gehr via Digitalmars-d

On 22.10.18 12:26, Timon Gehr wrote:

---
module borked;

void atomicIncrement(int* p)@system{
     import core.atomic;
     atomicOp!("+=",int,int)(*cast(shared(int)*)p,1);
}

struct Atomic(T){
     private T val;
     void opUnary(string op : "++")() shared @trusted {
     atomicIncrement(cast(T*));
     }
}
void main()@safe{
     Atomic!int i;
     auto a=&[i][0];// was: Atomic!int* a = 
     import std.concurrency;
     spawn((shared(Atomic!int)* a){ ++*a; }, a);
     ++i.val; // race
}
---


Obviously, this should have been:

---
module borked;

void atomicIncrement(int*p)@system{
import core.atomic;
atomicOp!"+="(*cast(shared(int)*)p,1);
}
struct Atomic(T){
private T val;
void opUnary(string op:"++")()shared @trusted{
atomicIncrement(cast(T*));
}
}
void main()@safe{
auto a=new Atomic!int;
import std.concurrency;
spawn((shared(Atomic!int)* a){ ++*a; }, a);
++a.val; // race
}
---

(I was short on time and had to fix Manu's code because it was not 
actually compilable.)


Re: shared - i need it to be useful

2018-10-22 Thread Aliak via Digitalmars-d

On Monday, 22 October 2018 at 10:26:14 UTC, Timon Gehr wrote:

---
module borked;

void atomicIncrement(int* p)@system{
import core.atomic;
atomicOp!("+=",int,int)(*cast(shared(int)*)p,1);
}

struct Atomic(T){
private T val;
void opUnary(string op : "++")() shared @trusted {
atomicIncrement(cast(T*));
}
}
void main()@safe{
Atomic!int i;
auto a=&[i][0];// was: Atomic!int* a = 
import std.concurrency;
spawn((shared(Atomic!int)* a){ ++*a; }, a);
++i.val; // race
}
---


Oh no! The author of the @trusted function (i.e. you) did not 
deliver on the promise they made!


hi, if you change the private val in Atomic to be “private shared 
T val”, is the situation the same?


Re: shared - i need it to be useful

2018-10-22 Thread Stanislav Blinov via Digitalmars-d

On Monday, 22 October 2018 at 00:22:19 UTC, Manu wrote:

No no, they're repeated, not scattered, because I seem to have 
to keep repeating it over and over, because nobody is reading 
the text, or perhaps imaging there is a lot more text than 
there is.

...
You mean like every post in opposition which disregards the 
rules and baselessly asserts it's a terrible idea? :/

...
I responded to your faulty program directly with the correct 
program, and you haven't acknowledged it. Did you see it?


Right back at you.

Quote:

I think this is a typical sort of construction:

struct ThreadsafeQueue(T)
{
  void QueueItem(T*) shared;
  T* UnqueueItem() shared;
}

struct SpecialWorkList
{
  struct Job { ... }

  void MakeJob(int x, float y, string z) shared  // <- any thread 
may

produce a job
  {
Job* job = new Job; // <- this is thread-local
PopulateJob(job, x, y, z); // <- preparation of a job might be
complex, and worthy of the SpecialWorkList implementation

jobList.QueueItem(job);  // <- QueueItem encapsulates
thread-safety, no need for blunt casts
  }

  void Flush() // <- not shared, thread-local consumer
  {
Job* job;
while (job = jobList.UnqueueItem()) // <- it's obviously safe 
for
a thread-local to call UnqueueItem even though the implementation 
is

threadsafe
{
  // thread-local dispatch of work...
  // perhaps rendering, perhaps deferred destruction, perhaps
deferred resource creation... whatever!
}
  }

  void GetSpecialSystemState() // <- this has NOTHING to do with 
the

threadsafe part of SpecialWorkList
  {
return os.functionThatChecksSystemState();
  }

  // there may be any number of utility functions that don't 
interact

with jobList.

private:
  void PopulateJob(ref Job job, ...)
  {
// expensive function; not thread-safe, and doesn't have any
interaction with threading.
  }

  ThreadsafeQueue!Job jobList;
}


This isn't an amazing example, but it's typical of a thing that's
mostly thread-local, and only a small controlled part of it's
functionality is thread-safe.
The thread-local method Flush() also deals with thread-safety
internally... because it flushes a thread-safe queue.

All thread-safety concerns are composed by a utility object, so 
there's no need for locks, magic, or casts here.


EndQuote;

The above:
1) Will not compile, not currently, not under your proposal 
(presumably you forgot in frustration to cast before calling 
PopulateJob?..)
2) Does not in any way demonstrate a practical @safe application 
of an implicit conversion. As I wrote in the original response to 
that code, with that particular code it seems more like you just 
need forwarding methods that call `shared` methods under the hood 
(i.e. MakeJob), and it'd be "nice" if you didn't have to write 
those and could just call `shared` MakeJob on an un-`shared` 
reference directly. But these are all assumptions without seeing 
the actual usage.


Please just stop acting like everyone here is opposing *you*. All 
you're doing is dismissing everyone with a "nuh-huh, you no 
understand, you bad". If it was just me, fine, it would mean I'm 
dumb and not worthy of this discussion. But this isn't the case, 
which means *you are not getting your point across*. And yet 
instead of trying to fix that, you're getting all snarky.


Re: shared - i need it to be useful

2018-10-22 Thread Timon Gehr via Digitalmars-d

On 22.10.18 02:54, Manu wrote:

On Sun, Oct 21, 2018 at 5:40 PM Timon Gehr via Digitalmars-d
 wrote:


On 21.10.18 21:04, Manu wrote:

On Sun, Oct 21, 2018 at 12:00 PM Timon Gehr via Digitalmars-d
 wrote:


On 21.10.18 17:54, Nicholas Wilson wrote:



As soon as that is done, you've got a data race with the other
existing unshared aliases.


You're in @trusted code, that is the whole point. The onus is on the
programmer to make that correct, same with regular @safe/@trusted@system
code.


Not all of the parties that participate in the data race are in @trusted
code. The point of @trusted is modularity: you manually check @trusted
code according to some set of restrictions and then you are sure that
there is no memory corruption.

Note that you are not allowed to look at any of the @safe code while
checking your @trusted code. You will only see an opaque interface to
the @safe code that you call and all you know is that all the @safe code
type checks according to @safe rules. Note that there might be an
arbitrary number of @safe functions and methods that you do not see.

Think about it this way: you first write all the @trusted and @system
code, and some evil guy who does not like you comes in after you looks
at your code and writes all the @safe code. If there is any memory
corruption, it will be your fault and you will face harsh consequences.
Now, design the @safe type checking rules. It won't be MP!

Note that there may well be a good way to get the good properties of MP
without breaking the type system, but MP itself is not good because it
breaks @safe.


Show me. Nobody has been able to show that yet. I'd really like to know this.



I just did,


There's no code there... just a presumption that the person who wrote
the @trusted code did not deliver the promise they made.
...


Yes, because there is no way to write @trusted code that holds its 
promise while actually doing something interesting in multiple threads 
if @safe code can implicitly convert from unshared to shared.



but if you really need to, give me a non-trivial piece of> correct 
multithreaded code that accesses some declared-unshared field
from a shared method and I will show you how the evil guy would modify
some @safe code in it and introduce race conditions. It needs to be your
code, as otherwise you will just claim again that it is me who wrote bad
@trusted code.


You can pick on any of my prior code fragments. They've all been ignored so far.



I don't want "code fragments". Show me the real code.

I manually browsed through posts now (thanks a lot) and found this 
implementation:


struct Atomic(T){
  void opUnary(string op : "++")() shared { atomicIncrement(); }
  private T val;
}

This is @system code. There is no @safe or @trusted here, so I am 
ignoring it.



Then I browsed some more, because I had nothing better to do, and I 
found this. I completed it so that it is actually compilable, except for 
the unsafe implicit conversion.


Please read this code, and then carefully read the comments below it 
before you respond. I will totally ignore any of your answers that 
arrive in the next two hours.


---
module borked;

void atomicIncrement(int* p)@system{
import core.atomic;
atomicOp!("+=",int,int)(*cast(shared(int)*)p,1);
}

struct Atomic(T){
private T val;
void opUnary(string op : "++")() shared @trusted {
atomicIncrement(cast(T*));
}
}
void main()@safe{
Atomic!int i;
auto a=&[i][0];// was: Atomic!int* a = 
import std.concurrency;
spawn((shared(Atomic!int)* a){ ++*a; }, a);
++i.val; // race
}
---


Oh no! The author of the @trusted function (i.e. you) did not deliver on 
the promise they made!


Now, before you go and tell me that I am stupid because I wrote bad 
code, consider the following:


- It is perfectly @safe to access private members from the same module.

- You may not blame the my @safe main function for the problem. It is 
@safe, so it cannot be blamed for UB. Any UB is the result of a bad 
@trusted function, a compiler bug, or hardware failure.


- The only @trusted function in this module was written by you.

You said that there is a third implementation somewhere. If that one 
actually works, I apologize and ask you to please paste it again in this 
subthread.




Re: shared - i need it to be useful

2018-10-22 Thread Manu via Digitalmars-d
On Mon, Oct 22, 2018 at 2:30 AM Walter Bright via Digitalmars-d
 wrote:
>
> On 10/22/2018 1:34 AM, Manu wrote:
> > I posted it, twice... 2 messages, back to back, and you're responding
> > to this one, and not that one. I'll post it again...
>
>
> Posting it over and over is illustrative of the failure of posting proposal
> documents to the n.g. instead of posting it as a DIP which can be referred to:
>
> 1. nobody knows which of your 70 messages are the ones with the proposal in it
>
> 2. with multiple posts of the proposal, nobody knows which one is the most
> up-to-date one

It hasn't changed. Not one single bit. I haven't changed a single
detail in this thread.

> Doing it this way does not work. Continuing to repost it is a waste of your
> time. Post it as a DIP and link to it.

And you STILL ignored my post >_<
Please look at the proper code that implements your broken example. I
know you've seen it now.


Re: shared - i need it to be useful

2018-10-22 Thread rikki cattermole via Digitalmars-d

On 22/10/2018 10:28 PM, Walter Bright wrote:

On 10/22/2018 1:34 AM, Manu wrote:

I posted it, twice... 2 messages, back to back, and you're responding
to this one, and not that one. I'll post it again...



Posting it over and over is illustrative of the failure of posting 
proposal documents to the n.g. instead of posting it as a DIP which can 
be referred to:


1. nobody knows which of your 70 messages are the ones with the proposal 
in it


2. with multiple posts of the proposal, nobody knows which one is the 
most up-to-date one


Doing it this way does not work. Continuing to repost it is a waste of 
your time. Post it as a DIP and link to it.


As I've said previously, it doesn't need to be a good DIP or anywhere 
near complete. It just needs code examples comparing current and 
proposed behavior with some text about semantics changes.


Re: shared - i need it to be useful

2018-10-22 Thread Walter Bright via Digitalmars-d

On 10/22/2018 1:34 AM, Manu wrote:

I posted it, twice... 2 messages, back to back, and you're responding
to this one, and not that one. I'll post it again...



Posting it over and over is illustrative of the failure of posting proposal 
documents to the n.g. instead of posting it as a DIP which can be referred to:


1. nobody knows which of your 70 messages are the ones with the proposal in it

2. with multiple posts of the proposal, nobody knows which one is the most 
up-to-date one


Doing it this way does not work. Continuing to repost it is a waste of your 
time. Post it as a DIP and link to it.




Re: shared - i need it to be useful

2018-10-22 Thread Walter Bright via Digitalmars-d

On 10/22/2018 1:42 AM, Manu wrote:

You removed whatever comment you're referring to.


If your newsreader cannot find the antecedent, you badly need to use a better 
one. Thunderbird handles this rather well, there's no reason to use an inferior one.


Or just click the <- button:

https://digitalmars.com/d/archives/digitalmars/D/shared_-_i_need_it_to_be_useful_320165.html#N320607



I don't understand any of Timon's posts in this thread at all, which
is unusual because he's usually pretty clear.


I suspect Timon is equally frustrated at not getting his point across.


Re: shared - i need it to be useful

2018-10-22 Thread Manu via Digitalmars-d
On Mon, Oct 22, 2018 at 12:55 AM Walter Bright via Digitalmars-d
 wrote:
>
> On 10/21/2018 11:58 AM, Timon Gehr wrote:
> > [...]
>
> Thank you, Timon, for a nice explanation of what I was trying to express.

You removed whatever comment you're referring to.
I don't understand any of Timon's posts in this thread at all, which
is unusual because he's usually pretty clear.


Re: shared - i need it to be useful

2018-10-22 Thread Manu via Digitalmars-d
Third time's the charm maybe?

- repeated, 3rd time 

On Sun., 21 Oct. 2018, 2:55 am Walter Bright via Digitalmars-d,
 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 = 
> 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-@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*)); }
}

Atomic!int i;
Atomic!int* a = 
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 

Re: shared - i need it to be useful

2018-10-22 Thread Manu via Digitalmars-d
On Mon, Oct 22, 2018 at 12:50 AM Walter Bright via Digitalmars-d
 wrote:
>
> On 10/21/2018 5:54 PM, Manu wrote:
> > Would you please respond to my messages, and specifically, respond to
> > the code that I presented to you in response to your broken example.
> > Or any of my earlier fragments throughout this thread. I've shared
> > quite a few, and so far, nobody has ever produced a criticism of any
> > of my fragments. They've just been skipped over.
>
> That's just the problem. You've posted 62 messages so far in this thread, and
> then there's all the ones Nicholas posted.

I sent it twice... again just a short while ago right before this
one... but you responded to this one and not that one O_o

> Trying to assemble the "earlier fragments throughout this thread" is not
> practical for readers, and the endless nature of this thread is ample evidence
> for it. The n.g. is a place to discuss a proposal, not the proposal itself.
>
> This change is definitely merits an actual proposal DIP, so that one is 
> assured
> of seeing the complete proposal, rationale, examples, etc., in one document, 
> as
> well as not being distracted by sidebars, thread drift, and mistakes. This
> document can evolve with corrections and clarifications from the discussion, 
> and
> anyone can get up to speed quickly by just reading the latest version of it.

Okay, but I still want you to respond to my corrections of your
program, which were in direct response to you... twice.

>  > But the one aimed directly at your own most recent sample program
>  > addresses your program directly.
>
> My most recent sample program was a direct criticism of one of your fragments,
> so please don't say "nobody has ever ...". I do understand your frustration at
> finding it hard to get your point across, but the problem at least for me is
> trying to mine it from nuggets scattered across 62 posts. Mine it, refine it,
> cast it into an ingot, then present it as a DIP.

I posted it, twice... 2 messages, back to back, and you're responding
to this one, and not that one. I'll post it again...


Re: shared - i need it to be useful

2018-10-22 Thread Walter Bright via Digitalmars-d

On 10/21/2018 11:58 AM, Timon Gehr wrote:

[...]


Thank you, Timon, for a nice explanation of what I was trying to express.


Re: shared - i need it to be useful

2018-10-22 Thread Walter Bright via Digitalmars-d

On 10/21/2018 5:54 PM, Manu wrote:

Would you please respond to my messages, and specifically, respond to
the code that I presented to you in response to your broken example.
Or any of my earlier fragments throughout this thread. I've shared
quite a few, and so far, nobody has ever produced a criticism of any
of my fragments. They've just been skipped over.


That's just the problem. You've posted 62 messages so far in this thread, and 
then there's all the ones Nicholas posted.


Trying to assemble the "earlier fragments throughout this thread" is not 
practical for readers, and the endless nature of this thread is ample evidence 
for it. The n.g. is a place to discuss a proposal, not the proposal itself.


This change is definitely merits an actual proposal DIP, so that one is assured 
of seeing the complete proposal, rationale, examples, etc., in one document, as 
well as not being distracted by sidebars, thread drift, and mistakes. This 
document can evolve with corrections and clarifications from the discussion, and 
anyone can get up to speed quickly by just reading the latest version of it.



> But the one aimed directly at your own most recent sample program
> addresses your program directly.

My most recent sample program was a direct criticism of one of your fragments, 
so please don't say "nobody has ever ...". I do understand your frustration at 
finding it hard to get your point across, but the problem at least for me is 
trying to mine it from nuggets scattered across 62 posts. Mine it, refine it, 
cast it into an ingot, then present it as a DIP.


Re: shared - i need it to be useful

2018-10-21 Thread Joakim via Digitalmars-d

On Monday, 22 October 2018 at 00:22:19 UTC, Manu wrote:
On Sun, Oct 21, 2018 at 2:35 PM Walter Bright via Digitalmars-d 
 wrote:


On 10/21/2018 2:08 PM, Walter Bright wrote:
> On 10/21/2018 12:20 PM, Nicholas Wilson wrote:
>> Yes, but the problem you describe is arises from implicit 
>> conversion in the other direction, which is not part of the 
>> proposal.

>
> It's Manu's example.

Then I don't know what the proposal is. Pieces of it appear to 
be scattered over numerous posts, mixed in with other text,


No no, they're repeated, not scattered, because I seem to have 
to keep repeating it over and over, because nobody is reading 
the text, or perhaps imaging there is a lot more text than 
there is.


I told you this is what happens with forum posts 4 days ago, yet 
you didn't listen:


https://forum.dlang.org/post/fokdcnzircoiuhrhz...@forum.dlang.org


opinions, and handwavy stuff.


You mean like every post in opposition which disregards the 
rules and baselessly asserts it's a terrible idea? :/



There's nothing to point to that is "the proposal".


You can go back to the OP, not a single detail is changed at 
any point, but I've repeated it a whole bunch of times 
(including in direct response to your last post) and the 
summary has become more concise, but not different.


1. Shared has no read or write access to data
2. Functions with shared arguments are threadsafe with respect 
to

those arguments
  a. This is a commitment that must be true in _absolute terms_ 
(there
exists discussion about the ways that neighbours must not 
undermine

this promise)
  b. There are numerous examples demonstrating how to configure 
this
(TL;DR: use encapsulation, and @trusted at the bottom of the 
stack)


If you can find a legitimate example where those rules don't 
hold, I

want to see it.
But every example so far has been based on a faulty premise 
where

those 2 simple rules were not actually applied.


Put it all together in a 2-3 page proposal elsewhere, so he 
doesn't have to hunt everything out in a blizzard of forum posts.


I responded to your faulty program directly with the correct 
program, and you haven't acknowledged it. Did you see it?


I suggest you and Manu write up a proper proposal. Something 
that is complete, has nothing else in it, has a rationale, 
illuminating examples, and explains why alternatives are 
inferior.


I have written this program a couple of times, including in 
direct

response to your last sample program.
You seem to have dismissed it... where is your response to that
program, or my last entire post?


For examples of how to do it:

https://github.com/dlang/DIPs/tree/master/DIPs

Trying to rewrite the semantics of shared is not a simple 
task, doing multithreading correctly is a minefield of "OOPS! 
I didn't think of that!" and if anything cries out for a DIP, 
your and Manu's proposal does.


Yes, I agree it's DIP worthy. But given the almost nothing but 
overt

hostility I've received here, why on earth would I waste my time
writing a DIP?
I put months into my other DIP which sits gathering dust... if 
this
thread inspired any confidence that it would be well-received I 
would
make the effort, but the critical reception we've seen here 
is... a

bit strange.
It's a 2-point proposal, the rules are **SO SIMPLE**, which is 
why I
love it. How it can be misunderstood is something I'm having 
trouble
understanding, and I don't know how to make it any clearer than 
I
already have; numerous times over, including in my last reply 
to you,

which you have ignored and dismissed it seems.

Please go back and read my response to your last program.


He did not say to write a full DIP, just a proposal, so he knows 
exactly what you mean, just as I said. It will require a DIP 
eventually, but he didn't ask you to write one now.


Re: shared - i need it to be useful

2018-10-21 Thread Neia Neutuladh via Digitalmars-d
On Sun, 21 Oct 2018 17:35:38 -0700, Manu wrote:

> On Sun, Oct 21, 2018 at 3:15 PM Neia Neutuladh via Digitalmars-d
>  wrote:
>> If we only used your proposal and only used @safe code, we wouldn't
>> have any data races, but that's only because we wouldn't have any
>> shared data. We'd have shared *variables*, but they would not contain
>> any data we could read or alter, and that's pretty much useless.
> 
> I've shown an implementation of Atomic(T) 3 times now... no response any
> time. Why is it being dismissed? Do I need to write it more times?
> This is a clear demonstration of how to build the foundation of the
> @safe threadsafe stack.

Yes, Atomic can be implemented here as @trusted code, and maybe using 
@safe compiler intrinsics for some situations. That's useful! But it's not 
*enough*.

It would require a lot of work to refit existing code to using only atomic 
operations, and it would end up looking rather clunky a lot of the time. 
If you're doing anything complex with a complex object graph, you're going 
to have a terrible time. You need to copy that object graph (atomically), 
which is going to be expensive and provides non-trivial restrictions on 
what you can store.

So I'm not keen on a world in which all multithreading is using atomic 
structs.

Unless, when you were talking about Atomic, you actually meant a wrapper 
containing some sort of lock, allowing you to submit arbitrary delegates 
to mutate the data within in a serialized way, similar to Atila Neves's 
fearless library. Which really stretches the definition of "atomic".

>> Currently, it helps because casting unshared to shared is not @safe,
>> because it makes it trivial to get multiple threads with unshared
>> references to the same data.
> 
> No no, that's a massive smell. That means anytime anyone wants to
> distribute something, they need to perform unsafe casts. That's not
> okay.

Casting thread-local to shared makes it easy to cause errors, and that's 
why it's a massive smell. Making it silent doesn't eliminate the smell.

> 100% of my SMP code works with my proposal, and something close to 0%
> works with shared as it is today. (assuming we desire @safe interaction,
> which we do, because threading is hard enough already!)

You want un-shared things to implicitly cast to shared. You don't want to 
have to allocate anything as shared, and you do want to pass absolutely 
anything to any thread.

You can write a @trusted assumeShared function, analogous to assumeUnique, 
to accomplish that. It would be slightly more awkward than what you're 
proposing, but it accomplishes one of your two goals: convert non-shared 
things to shared things in @safe code.

The other goal in your proposal is for some code that currently compiles 
not to. So you should be able to write the code you want, albeit with an 
increased risk of bugs. Or you could write a template that wraps a shared 
thing and forbids field access and assignment.

>> And that's when you're using shared as expected rather than doing
>> something weird.
> 
> No, I *expect* to use shared in @safe code, and not write any unsafe
> code ever. shared doesn't model a useful interaction now, not in any
> way.
> 
> Today, access to shared data members are unrestricted and completely
> unsafe

Yes, and that's bad and should be changed.

> passing data into something like a parallel-for requires unsafe
> casts.

Or allocating data as shared, which is the recommended way, because that 
makes absolutely certain that, from the start, no code has an un-shared 
copy of that data. No casts needed.


Re: shared - i need it to be useful

2018-10-21 Thread Nicholas Wilson via Digitalmars-d

On Monday, 22 October 2018 at 00:55:00 UTC, Timon Gehr wrote:

On 22.10.18 02:46, Nicholas Wilson wrote:

On Monday, 22 October 2018 at 00:38:33 UTC, Timon Gehr wrote:

I just did,


Link please?



https://forum.dlang.org/post/pqii8k$11u3$1...@digitalmars.com


That contains no code.

Not all of the parties that participate in the data race are in 
@trusted code.


There are two cases of @trusted code, bad and good. Bad trusted 
code can indeed be misused by @safe code to corrupt memory. Good 
trusted code cannot.


What part of the proposal breaks the type system? The implicit 
conversion to shared implies that the passed to function is @safe 
iff all the functions it calls are @safe only call function with 
that parameter to other shared @safe functions _OR_ they are 
@trusted.


The point of @trusted is modularity: you manually check 
@trusted code according to some set of restrictions and then 
you are sure that there is no memory corruption.


Yes. And?


Re: shared - i need it to be useful

2018-10-21 Thread Manu via Digitalmars-d
On Sun, Oct 21, 2018 at 5:55 PM Timon Gehr via Digitalmars-d
 wrote:
>
> On 22.10.18 02:45, Manu wrote:
> > On Sun, Oct 21, 2018 at 5:35 PM Timon Gehr via Digitalmars-d
> >  wrote:
> >>
> >> On 21.10.18 20:46, Manu wrote:
>  Shared data is only useful if, at some point, it is read/written, 
>  presumably by
>  casting it to unshared in @trusted code. As soon as that is done, you've 
>  got a
>  data race with the other existing unshared aliases.
> >>> If such a race is possible, then the @trusted function is not
> >>> threadsafe, so it is not @trusted by definition.
> >>> You wrote a bad @trusted function, and you should feel bad.
> >>> ...
> >>
> >> I wonder where this "each piece of code is maintained by only one person
> >> and furthermore this is the only person that will suffer if the code has
> >> bugs" mentality comes from. It is very popular as well as obviously
> >> nonsense.
> >>
> >>> The simplest way to guarantee that no unsafe access is possible is to
> >>> use encapsulation to assure no unregulated access exists.
> >>
> >> This only works if untrusted programmers (i.e. programmers who are only
> >> allowed to write/modify @safe code) are not allowed to change your
> >> class. I.e. it does not work.
> >
> > Have you ever cracked open std::map and 'fixed' it because you thought
> > it was bad?
> > Of course not. Same applies here. Nobody 'fixes' core.atomic.Atomic
> > without understanding what they're doing.
> >
>
> You are not proposing to let core.atomic.Atomic convert to shared
> implicitly, you are proposing to do that for all classes.

You can always implicitly convert to shared.
Where did I ever say anything like that? I'm sure I've never said
this. How do these transformations of what I've said keep happening?

> > You seem to be stuck on the detail whether you can trust the @trusted
> > author though...
>
> Again: the @safe author is the problem.

I don't follow. The @safe author is incapable of doing threadsafety
violation. They can only combine threadsafe functions.
They can certainly produce a program that doesn't work, and they are
capable of ordering issues, but that's not the same as data-race
related crash bugs.


Re: shared - i need it to be useful

2018-10-21 Thread Manu via Digitalmars-d
On Sun, Oct 21, 2018 at 5:40 PM Timon Gehr via Digitalmars-d
 wrote:
>
> On 21.10.18 21:04, Manu wrote:
> > On Sun, Oct 21, 2018 at 12:00 PM Timon Gehr via Digitalmars-d
> >  wrote:
> >>
> >> On 21.10.18 17:54, Nicholas Wilson wrote:
> >>>
>  As soon as that is done, you've got a data race with the other
>  existing unshared aliases.
> >>>
> >>> You're in @trusted code, that is the whole point. The onus is on the
> >>> programmer to make that correct, same with regular @safe/@trusted@system
> >>> code.
> >>
> >> Not all of the parties that participate in the data race are in @trusted
> >> code. The point of @trusted is modularity: you manually check @trusted
> >> code according to some set of restrictions and then you are sure that
> >> there is no memory corruption.
> >>
> >> Note that you are not allowed to look at any of the @safe code while
> >> checking your @trusted code. You will only see an opaque interface to
> >> the @safe code that you call and all you know is that all the @safe code
> >> type checks according to @safe rules. Note that there might be an
> >> arbitrary number of @safe functions and methods that you do not see.
> >>
> >> Think about it this way: you first write all the @trusted and @system
> >> code, and some evil guy who does not like you comes in after you looks
> >> at your code and writes all the @safe code. If there is any memory
> >> corruption, it will be your fault and you will face harsh consequences.
> >> Now, design the @safe type checking rules. It won't be MP!
> >>
> >> Note that there may well be a good way to get the good properties of MP
> >> without breaking the type system, but MP itself is not good because it
> >> breaks @safe.
> >
> > Show me. Nobody has been able to show that yet. I'd really like to know 
> > this.
> >
>
> I just did,

There's no code there... just a presumption that the person who wrote
the @trusted code did not deliver the promise they made.

> but if you really need to, give me a non-trivial piece of> correct 
> multithreaded code that accesses some declared-unshared field
> from a shared method and I will show you how the evil guy would modify
> some @safe code in it and introduce race conditions. It needs to be your
> code, as otherwise you will just claim again that it is me who wrote bad
> @trusted code.

You can pick on any of my prior code fragments. They've all been ignored so far.


Re: shared - i need it to be useful

2018-10-21 Thread Nicholas Wilson via Digitalmars-d

On Monday, 22 October 2018 at 00:46:04 UTC, Walter Bright wrote:
That's what I was referring to, and Manu's example. It doesn't 
work, as I pointed out.


I'm pretty sure it does, but please repeat it.

We will eventually. This started as a "please point out any 
problems with this" and has probably outlived that phase.


You'll need to address the issues raised here in the DIP.


That is a given. You would do well to heed it for your own DIPs.


Re: shared - i need it to be useful

2018-10-21 Thread Manu via Digitalmars-d
On Sun, Oct 21, 2018 at 5:50 PM Walter Bright via Digitalmars-d
 wrote:
>
> On 10/21/2018 4:12 PM, Nicholas Wilson wrote:
> > On Sunday, 21 October 2018 at 21:32:14 UTC, Walter Bright wrote:
> >> On 10/21/2018 2:08 PM, Walter Bright wrote:
> >>> On 10/21/2018 12:20 PM, Nicholas Wilson wrote:
>  Yes, but the problem you describe is arises from implicit conversion in 
>  the
>  other direction, which is not part of the proposal.
> >>>
> >>> It's Manu's example.
> >>
> >> Then I don't know what the proposal is. Pieces of it appear to be scattered
> >> over numerous posts, mixed in with other text, opinions, and handwavy 
> >> stuff.
> >> There's nothing to point to that is "the proposal".
> >
> > The proposal is:
> >
> > Implicit conversion _to_ shared, e.g. passing it to a thread entry point, 
> > and
> > not implicit conversion _from_ shared (just like implicit const 
> > conversions).
>
> That's what I was referring to, and Manu's example. It doesn't work, as I
> pointed out.
>
>
> >> I suggest you and Manu write up a proper proposal. Something that is 
> >> complete,
> >> has nothing else in it, has a rationale, illuminating examples, and 
> >> explains
> >> why alternatives are inferior.
> >
> > We will eventually. This started as a "please point out any problems with 
> > this"
> > and has probably outlived that phase.
>
> You'll need to address the issues raised here in the DIP.

Would you please respond to my messages, and specifically, respond to
the code that I presented to you in response to your broken example.
Or any of my earlier fragments throughout this thread. I've shared
quite a few, and so far, nobody has ever produced a criticism of any
of my fragments. They've just been skipped over.
But the one aimed directly at your own most recent sample program
addresses your program directly.


Re: shared - i need it to be useful

2018-10-21 Thread Timon Gehr via Digitalmars-d

On 22.10.18 02:46, Nicholas Wilson wrote:

On Monday, 22 October 2018 at 00:38:33 UTC, Timon Gehr wrote:

I just did,


Link please?



https://forum.dlang.org/post/pqii8k$11u3$1...@digitalmars.com


Re: shared - i need it to be useful

2018-10-21 Thread Timon Gehr via Digitalmars-d

On 22.10.18 02:45, Manu wrote:

On Sun, Oct 21, 2018 at 5:35 PM Timon Gehr via Digitalmars-d
 wrote:


On 21.10.18 20:46, Manu wrote:

Shared data is only useful if, at some point, it is read/written, presumably by
casting it to unshared in @trusted code. As soon as that is done, you've got a
data race with the other existing unshared aliases.

If such a race is possible, then the @trusted function is not
threadsafe, so it is not @trusted by definition.
You wrote a bad @trusted function, and you should feel bad.
...


I wonder where this "each piece of code is maintained by only one person
and furthermore this is the only person that will suffer if the code has
bugs" mentality comes from. It is very popular as well as obviously
nonsense.


The simplest way to guarantee that no unsafe access is possible is to
use encapsulation to assure no unregulated access exists.


This only works if untrusted programmers (i.e. programmers who are only
allowed to write/modify @safe code) are not allowed to change your
class. I.e. it does not work.


Have you ever cracked open std::map and 'fixed' it because you thought
it was bad?
Of course not. Same applies here. Nobody 'fixes' core.atomic.Atomic
without understanding what they're doing.



You are not proposing to let core.atomic.Atomic convert to shared 
implicitly, you are proposing to do that for all classes.



You seem to be stuck on the detail whether you can trust the @trusted
author though...


Again: the @safe author is the problem.



Re: shared - i need it to be useful

2018-10-21 Thread Walter Bright via Digitalmars-d

On 10/21/2018 4:12 PM, Nicholas Wilson wrote:

On Sunday, 21 October 2018 at 21:32:14 UTC, Walter Bright wrote:

On 10/21/2018 2:08 PM, Walter Bright wrote:

On 10/21/2018 12:20 PM, Nicholas Wilson wrote:
Yes, but the problem you describe is arises from implicit conversion in the 
other direction, which is not part of the proposal.


It's Manu's example.


Then I don't know what the proposal is. Pieces of it appear to be scattered 
over numerous posts, mixed in with other text, opinions, and handwavy stuff. 
There's nothing to point to that is "the proposal".


The proposal is:

Implicit conversion _to_ shared, e.g. passing it to a thread entry point, and 
not implicit conversion _from_ shared (just like implicit const conversions).


That's what I was referring to, and Manu's example. It doesn't work, as I 
pointed out.



I suggest you and Manu write up a proper proposal. Something that is complete, 
has nothing else in it, has a rationale, illuminating examples, and explains 
why alternatives are inferior.


We will eventually. This started as a "please point out any problems with this" 
and has probably outlived that phase.


You'll need to address the issues raised here in the DIP.


Re: shared - i need it to be useful

2018-10-21 Thread Nicholas Wilson via Digitalmars-d

On Monday, 22 October 2018 at 00:38:33 UTC, Timon Gehr wrote:

I just did,


Link please?



Re: shared - i need it to be useful

2018-10-21 Thread Manu via Digitalmars-d
On Sun, Oct 21, 2018 at 5:35 PM Timon Gehr via Digitalmars-d
 wrote:
>
> On 21.10.18 20:46, Manu wrote:
> >> Shared data is only useful if, at some point, it is read/written, 
> >> presumably by
> >> casting it to unshared in @trusted code. As soon as that is done, you've 
> >> got a
> >> data race with the other existing unshared aliases.
> > If such a race is possible, then the @trusted function is not
> > threadsafe, so it is not @trusted by definition.
> > You wrote a bad @trusted function, and you should feel bad.
> > ...
>
> I wonder where this "each piece of code is maintained by only one person
> and furthermore this is the only person that will suffer if the code has
> bugs" mentality comes from. It is very popular as well as obviously
> nonsense.
>
> > The simplest way to guarantee that no unsafe access is possible is to
> > use encapsulation to assure no unregulated access exists.
>
> This only works if untrusted programmers (i.e. programmers who are only
> allowed to write/modify @safe code) are not allowed to change your
> class. I.e. it does not work.

Have you ever cracked open std::map and 'fixed' it because you thought
it was bad?
Of course not. Same applies here. Nobody 'fixes' core.atomic.Atomic
without understanding what they're doing.

You seem to be stuck on the detail whether you can trust the @trusted
author though... that is a reasonable point of debate, but it's a
slightly separate topic. I am confident that the number of @trusted
functions required to found a useful stack are low, probably countable
with fingers, and always in a library.

If we can put aside that point of debate just for now; whether you
feel the @trusted author can be trusted, assuming that they can, does
the model work? Can you break the model as I have presented it?
If not; if the model is sound, then we can begin the discussion you're
alluding to and talk about opportunities to improve on static
guarantees for @trusted authors, or ways to communicate their
responsibility clearly, and patterns to assure success.


Re: shared - i need it to be useful

2018-10-21 Thread Nicholas Wilson via Digitalmars-d

On Monday, 22 October 2018 at 00:32:35 UTC, Timon Gehr wrote:
This only works if untrusted programmers (i.e. programmers who 
are only allowed to write/modify @safe code) are not allowed to 
change your class. I.e. it does not work.


This is the basis of the current @safe/@trusted/@system model.
Are you saying it is useless?


Re: shared - i need it to be useful

2018-10-21 Thread Timon Gehr via Digitalmars-d

On 21.10.18 21:04, Manu wrote:

On Sun, Oct 21, 2018 at 12:00 PM Timon Gehr via Digitalmars-d
 wrote:


On 21.10.18 17:54, Nicholas Wilson wrote:



As soon as that is done, you've got a data race with the other
existing unshared aliases.


You're in @trusted code, that is the whole point. The onus is on the
programmer to make that correct, same with regular @safe/@trusted@system
code.


Not all of the parties that participate in the data race are in @trusted
code. The point of @trusted is modularity: you manually check @trusted
code according to some set of restrictions and then you are sure that
there is no memory corruption.

Note that you are not allowed to look at any of the @safe code while
checking your @trusted code. You will only see an opaque interface to
the @safe code that you call and all you know is that all the @safe code
type checks according to @safe rules. Note that there might be an
arbitrary number of @safe functions and methods that you do not see.

Think about it this way: you first write all the @trusted and @system
code, and some evil guy who does not like you comes in after you looks
at your code and writes all the @safe code. If there is any memory
corruption, it will be your fault and you will face harsh consequences.
Now, design the @safe type checking rules. It won't be MP!

Note that there may well be a good way to get the good properties of MP
without breaking the type system, but MP itself is not good because it
breaks @safe.


Show me. Nobody has been able to show that yet. I'd really like to know this.



I just did, but if you really need to, give me a non-trivial piece of 
correct multithreaded code that accesses some declared-unshared field 
from a shared method and I will show you how the evil guy would modify 
some @safe code in it and introduce race conditions. It needs to be your 
code, as otherwise you will just claim again that it is me who wrote bad 
@trusted code.


Re: shared - i need it to be useful

2018-10-21 Thread Manu via Digitalmars-d
On Sun, Oct 21, 2018 at 3:15 PM Neia Neutuladh via Digitalmars-d
 wrote:
>
> On Sun, 21 Oct 2018 12:04:16 -0700, Manu wrote:
> > On Sun, Oct 21, 2018 at 12:00 PM Timon Gehr via Digitalmars-d
> >  wrote:
> >> Note that there may well be a good way to get the good properties of MP
> >> without breaking the type system, but MP itself is not good because it
> >> breaks @safe.
> >
> > Show me. Nobody has been able to show that yet. I'd really like to know
> > this.
>
> If we only used your proposal and only used @safe code, we wouldn't have
> any data races, but that's only because we wouldn't have any shared data.
> We'd have shared *variables*, but they would not contain any data we could
> read or alter, and that's pretty much useless.

I've shown an implementation of Atomic(T) 3 times now... no response
any time. Why is it being dismissed? Do I need to write it more times?
This is a clear demonstration of how to build the foundation of the
@safe threadsafe stack.

> To use your proposal, we need to cast data back from shared to unshared.
> When it's unshared, we need to make sure that exactly one thread has a
> reference to that data as unshared.

No, you just need to make sure that access is atomic or synchronised
in a proper way, and if you do cast away shared in some low-level
@trusted function, make sure that reference doesn't escape.
You can do it, I have faith.

> And @safe *should* help us with that.

Totally.

> Currently, it helps because casting unshared to shared is not @safe,
> because it makes it trivial to get multiple threads with unshared
> references to the same data.

No no, that's a massive smell. That means anytime anyone wants to
distribute something, they need to perform unsafe casts. That's not
okay.
Modeling shared-ness/unshared-ness is not *useful* in any way that I
have been able to identify. Modelling what it means to be threadsafe
is useful in every application I've ever written.
100% of my SMP code works with my proposal, and something close to 0%
works with shared as it is today. (assuming we desire @safe
interaction, which we do, because threading is hard enough already!)

> And that's when you're using shared as
> expected rather than doing something weird.

No, I *expect* to use shared in @safe code, and not write any unsafe
code ever. shared doesn't model a useful interaction now, not in any
way.

Today, access to shared data members are unrestricted and completely
unsafe, passing data into something like a parallel-for requires
unsafe casts.


Re: shared - i need it to be useful

2018-10-21 Thread Timon Gehr via Digitalmars-d

On 21.10.18 20:46, Manu wrote:

Shared data is only useful if, at some point, it is read/written, presumably by
casting it to unshared in @trusted code. As soon as that is done, you've got a
data race with the other existing unshared aliases.

If such a race is possible, then the @trusted function is not
threadsafe, so it is not @trusted by definition.
You wrote a bad @trusted function, and you should feel bad.
...


I wonder where this "each piece of code is maintained by only one person 
and furthermore this is the only person that will suffer if the code has 
bugs" mentality comes from. It is very popular as well as obviously 
nonsense.



The simplest way to guarantee that no unsafe access is possible is to
use encapsulation to assure no unregulated access exists.


This only works if untrusted programmers (i.e. programmers who are only 
allowed to write/modify @safe code) are not allowed to change your 
class. I.e. it does not work.


Re: shared - i need it to be useful

2018-10-21 Thread Manu via Digitalmars-d
On Sun, Oct 21, 2018 at 2:35 PM Walter Bright via Digitalmars-d
 wrote:
>
> On 10/21/2018 2:08 PM, Walter Bright wrote:
> > On 10/21/2018 12:20 PM, Nicholas Wilson wrote:
> >> Yes, but the problem you describe is arises from implicit conversion in the
> >> other direction, which is not part of the proposal.
> >
> > It's Manu's example.
>
> Then I don't know what the proposal is. Pieces of it appear to be scattered 
> over
> numerous posts, mixed in with other text,

No no, they're repeated, not scattered, because I seem to have to keep
repeating it over and over, because nobody is reading the text, or
perhaps imaging there is a lot more text than there is.

> opinions, and handwavy stuff.

You mean like every post in opposition which disregards the rules and
baselessly asserts it's a terrible idea? :/

> There's nothing to point to that is "the proposal".

You can go back to the OP, not a single detail is changed at any
point, but I've repeated it a whole bunch of times (including in
direct response to your last post) and the summary has become more
concise, but not different.

1. Shared has no read or write access to data
2. Functions with shared arguments are threadsafe with respect to
those arguments
  a. This is a commitment that must be true in _absolute terms_ (there
exists discussion about the ways that neighbours must not undermine
this promise)
  b. There are numerous examples demonstrating how to configure this
(TL;DR: use encapsulation, and @trusted at the bottom of the stack)

If you can find a legitimate example where those rules don't hold, I
want to see it.
But every example so far has been based on a faulty premise where
those 2 simple rules were not actually applied.

I responded to your faulty program directly with the correct program,
and you haven't acknowledged it. Did you see it?

> I suggest you and Manu write up a proper proposal. Something that is complete,
> has nothing else in it, has a rationale, illuminating examples, and explains 
> why
> alternatives are inferior.

I have written this program a couple of times, including in direct
response to your last sample program.
You seem to have dismissed it... where is your response to that
program, or my last entire post?

> For examples of how to do it:
>
> https://github.com/dlang/DIPs/tree/master/DIPs
>
> Trying to rewrite the semantics of shared is not a simple task, doing
> multithreading correctly is a minefield of "OOPS! I didn't think of that!" and
> if anything cries out for a DIP, your and Manu's proposal does.

Yes, I agree it's DIP worthy. But given the almost nothing but overt
hostility I've received here, why on earth would I waste my time
writing a DIP?
I put months into my other DIP which sits gathering dust... if this
thread inspired any confidence that it would be well-received I would
make the effort, but the critical reception we've seen here is... a
bit strange.
It's a 2-point proposal, the rules are **SO SIMPLE**, which is why I
love it. How it can be misunderstood is something I'm having trouble
understanding, and I don't know how to make it any clearer than I
already have; numerous times over, including in my last reply to you,
which you have ignored and dismissed it seems.

Please go back and read my response to your last program.


Re: shared - i need it to be useful

2018-10-21 Thread Manu via Digitalmars-d
On Sun, Oct 21, 2018 at 11:31 AM Manu  wrote:
>
> On Sun., 21 Oct. 2018, 2:55 am Walter Bright via Digitalmars-d,
>  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 = 
> > 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-@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*)); }
> }
>
> Atomic!int i;
> Atomic!int* a = 
> 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 

Re: shared - i need it to be useful

2018-10-21 Thread Nicholas Wilson via Digitalmars-d

On Sunday, 21 October 2018 at 22:12:18 UTC, Neia Neutuladh wrote:

On Sun, 21 Oct 2018 12:04:16 -0700, Manu wrote:
On Sun, Oct 21, 2018 at 12:00 PM Timon Gehr via Digitalmars-d 
 wrote:
Note that there may well be a good way to get the good 
properties of MP without breaking the type system, but MP 
itself is not good because it breaks @safe.


Show me. Nobody has been able to show that yet. I'd really 
like to know this.


If we only used your proposal and only used @safe code, we 
wouldn't have any data races,


Only of the @trusted implementation is thread safe, which it 
_should_ be. This is the same caveat as non threaded 
-@safe/@tusted code.


but that's only because we wouldn't have any shared data. We'd 
have shared *variables*, but they would not contain any data we 
could read


Reads must be atomic.


or alter, and that's pretty much useless.

To use your proposal, we need to cast data back from shared to 
unshared.


Yes but this is in the @trusted implementation that forms the 
basis of your threadsafety.


When it's unshared, we need to make sure that exactly one 
thread has a reference to that data as unshared.


Nod.


And @safe *should* help us with that.


Nod.

Currently, it helps because casting unshared to shared is not 
@safe,


This remains the case, and should be done (enforced by the 
compiler) only in @trusted/@system code as a basis for thread 
safe, @safe code.


because it makes it trivial to get multiple threads with 
unshared references to the same data.


That is @trusted or @system code and therefore is the programmers 
responsibility.


And that's when you're using shared as expected rather than 
doing something weird.


That forms the basis of your thread safe stack. From there on, 
the basis that shared arguments to functions are treated safely 
in the presence of threading means that code that calls the 
@trusted implementations is @safe.


Re: shared - i need it to be useful

2018-10-21 Thread Nicholas Wilson via Digitalmars-d

On Sunday, 21 October 2018 at 21:32:14 UTC, Walter Bright wrote:

On 10/21/2018 2:08 PM, Walter Bright wrote:

On 10/21/2018 12:20 PM, Nicholas Wilson wrote:
Yes, but the problem you describe is arises from implicit 
conversion in the other direction, which is not part of the 
proposal.


It's Manu's example.


Then I don't know what the proposal is. Pieces of it appear to 
be scattered over numerous posts, mixed in with other text, 
opinions, and handwavy stuff. There's nothing to point to that 
is "the proposal".


The proposal is:

Implicit conversion _to_ shared, e.g. passing it to a thread 
entry point, and not implicit conversion _from_ shared (just like 
implicit const conversions).


Shared disables reads and writes

 Your confusion results from the use of atomic add which, in 
Manu's examples had a different signature than before.


I suggest you and Manu write up a proper proposal. Something 
that is complete, has nothing else in it, has a rationale, 
illuminating examples, and explains why alternatives are 
inferior.


We will eventually. This started as a "please point out any 
problems with this" and has probably outlived that phase.



Trying to rewrite the semantics of shared is not a simple task,


Not as much as trying to explain it! Having talked to Manu in 
person it is much easier to understand.


doing multithreading correctly is a minefield of "OOPS! I 
didn't think of that!"


The above case in point, this is about assuming your 
implementation of thread safe primitives are thread safe 
(@trusted) that you use it correctly (@safe).





Re: shared - i need it to be useful

2018-10-21 Thread Neia Neutuladh via Digitalmars-d
On Sun, 21 Oct 2018 12:04:16 -0700, Manu wrote:
> On Sun, Oct 21, 2018 at 12:00 PM Timon Gehr via Digitalmars-d
>  wrote:
>> Note that there may well be a good way to get the good properties of MP
>> without breaking the type system, but MP itself is not good because it
>> breaks @safe.
> 
> Show me. Nobody has been able to show that yet. I'd really like to know
> this.

If we only used your proposal and only used @safe code, we wouldn't have 
any data races, but that's only because we wouldn't have any shared data. 
We'd have shared *variables*, but they would not contain any data we could 
read or alter, and that's pretty much useless.

To use your proposal, we need to cast data back from shared to unshared. 
When it's unshared, we need to make sure that exactly one thread has a 
reference to that data as unshared. And @safe *should* help us with that. 
Currently, it helps because casting unshared to shared is not @safe, 
because it makes it trivial to get multiple threads with unshared 
references to the same data. And that's when you're using shared as 
expected rather than doing something weird.


Re: shared - i need it to be useful

2018-10-21 Thread Walter Bright via Digitalmars-d

On 10/21/2018 2:08 PM, Walter Bright wrote:

On 10/21/2018 12:20 PM, Nicholas Wilson wrote:
Yes, but the problem you describe is arises from implicit conversion in the 
other direction, which is not part of the proposal.


It's Manu's example.


Then I don't know what the proposal is. Pieces of it appear to be scattered over 
numerous posts, mixed in with other text, opinions, and handwavy stuff. There's 
nothing to point to that is "the proposal".


I suggest you and Manu write up a proper proposal. Something that is complete, 
has nothing else in it, has a rationale, illuminating examples, and explains why 
alternatives are inferior.


For examples of how to do it:

https://github.com/dlang/DIPs/tree/master/DIPs

Trying to rewrite the semantics of shared is not a simple task, doing 
multithreading correctly is a minefield of "OOPS! I didn't think of that!" and 
if anything cries out for a DIP, your and Manu's proposal does.


Re: shared - i need it to be useful

2018-10-21 Thread Walter Bright via Digitalmars-d

On 10/21/2018 12:20 PM, Nicholas Wilson wrote:
Yes, but the problem you describe is arises from implicit conversion in the 
other direction, which is not part of the proposal.


It's Manu's example.


Re: shared - i need it to be useful

2018-10-21 Thread Stanislav Blinov via Digitalmars-d

On Sunday, 21 October 2018 at 19:22:45 UTC, Manu wrote:
On Sun, Oct 21, 2018 at 5:50 AM Stanislav Blinov via 
Digitalmars-d  wrote:


Because the whole reason to have `shared` is to avoid the 
extraneous checks that you mentioned above,


No, it is to assure that you write correct not-broken code.


You can do that without `shared`.

and only write actual useful code (i.e. lock-write-unlock, or 
read-put-to-queue-repeat, or whatever), not busy-work (testing 
if the file is open on every call).


`shared` is no comment on performance. You have written a slow 
locking API.
If you care about perf, you would write a completely different 
API that favours perf.

This not impossible, nor even particularly hard.


You're conflating your assumptions about the code with the topic 
of this discussion. I can write a million examples and you'll 
still find a million reasons to talk about how they're 
incorrectly implemented, instead of focusing on merits or 
disadvantages of your proposal with the code given as is.


If you have a `shared` reference, it better be to existing 
data.


I mean, if I dereference a pointer, it had better not be null!


Why would you share a null pointer?


That's why having `shared` and un-`shared`
references to the same data simultaneously is not safe: you 
can't guarantee in any way that the owning thread doesn't 
invalidate

the data through it's non-`shared` reference while you're doing
your threadsafe `shared` work; you can only "promise" that by
convention (documentation).


The owning thread is not a special actor. Your reasoning is 
wonky here.


Why have it then at all? If it's not a "special" actor, just make 
all shared data `shared`. But your proposal specifically targets 
the conversion, suggesting you *do* need a special actor.



And I have partially-read or partially-written data.


I expect you flushed before killing the file.


So? The threads still weren't done yet.

Or Maybe I call closeFile(), main thread continues and opens 
another file,

which gives the same file descriptor, `shared` references to
FileHandle which the user forgot to wait on continue to work
oblivious to the fact that it's a different file now.


It's wild to suggest that ANY design for `shared` should 
somehow deal with the OS recycling a file handle...


I'm not suggesting that at all, you've completely misrepresenting 
what I'm saying by splitting a quote.


And it's still not an un-@safe crash! It's just a program with 
a bug.


Ok, if you say that sticking @safe on code that can partially 
piece together data from unrelated sources is fine, then sure.



> I'm going to assume that `shareWithThreads()` was implemented
> by an 'expert' who checked the function results for errors...


But you can only find out about these errors in 
`waitForThreads`, the very call that the user "forgot" to make!


...what?


Please suggest another way of handling errors reported by other 
threads. More `shared` state?



[ ... snip ... ]


You have to concede defeat at this point.


I agree. No matter how hard I try or how many times I ask you to 
demonstrate, I still fail to see the value in assuming @safe 
implicitly conversion of mutable data to shared. Instead of 
defending your proposal, you chose to attack the opposition. 
You've defeated me, flawless victory.



Destroy my proposal with another legitimately faulty program.


Is there a point? I post code, you: "nah, that's wrong". Steven 
posts code, you: "nah, that's wrong". Timon posts code, you: 
"nah, that's wrong". Walter posts code, you: "nah, that's 
wrong"... What's right then?


Re: shared - i need it to be useful

2018-10-21 Thread Nicholas Wilson via Digitalmars-d

On Sunday, 21 October 2018 at 19:07:37 UTC, Nicholas Wilson wrote:

On Sunday, 21 October 2018 at 09:50:09 UTC, Walter Bright wrote:
Your proposal makes that impossible because the compiler would 
allow unshared data to be implicitly typed as shared.


Yes, but not the other way around.


Whoops that should read


Your proposal makes that impossible


No

because the compiler would allow unshared data to be 
implicitly typed as shared.


Yes, but the problem you describe is arises from implicit 
conversion in the other direction, which is not part of the 
proposal.


Re: shared - i need it to be useful

2018-10-21 Thread Manu via Digitalmars-d
On Sun, Oct 21, 2018 at 5:50 AM Stanislav Blinov via Digitalmars-d
 wrote:
>
> On Sunday, 21 October 2018 at 05:47:14 UTC, Manu wrote:
>
> >> And yet, if we're lucky, we get
> >> a consistent instacrash. If we're unlucky, we get memory
> >> corruption, or an unsolicited write to another currently open
> >> file, either of which can go unnoticed for some time.
>
> > Woah! Now this is way off-piste..
> > Why would get a crash? Why would get memory corruption? None of
> > those things make sense.
>
> Because the whole reason to have `shared` is to avoid the
> extraneous checks that you mentioned above,

No, it is to assure that you write correct not-broken code.

> and only write actual
> useful code (i.e. lock-write-unlock, or read-put-to-queue-repeat,
> or whatever), not busy-work (testing if the file is open on every
> call).

`shared` is no comment on performance. You have written a slow locking API.
If you care about perf, you would write a completely different API
that favours perf.
This not impossible, nor even particularly hard.

> If you have a `shared` reference, it better be to existing
> data.

I mean, if I dereference a pointer, it had better not be null!

> If it isn't, the program is invalid already: you've shared
> something that doesn't "exist" (good for marketing, not so good
> for multithreading).

I mean, if I dereference a pointer, it had better not be null!

> That's why having `shared` and un-`shared`
> references to the same data simultaneously is not safe: you can't
> guarantee in any way that the owning thread doesn't invalidate
> the data through it's non-`shared` reference while you're doing
> your threadsafe `shared` work; you can only "promise" that by
> convention (documentation).

The owning thread is not a special actor. Your reasoning is wonky here.
Lets say there is no owning instance, only a collection of shared
instances... any one of them can theoretically do an operation that
interferes with the others.
That's issues for general threading, and no design for `shared` can
(or should) interact with that problem. That's a problem for
architecture.

> > So, you call closeFile immediately and read/write start
> > returning null.
>
> And I have partially-read or partially-written data.

I expect you flushed before killing the file.

> Or Maybe I
> call closeFile(), main thread continues and opens another file,
> which gives the same file descriptor, `shared` references to
> FileHandle which the user forgot to wait on continue to work
> oblivious to the fact that it's a different file now.

It's wild to suggest that ANY design for `shared` should somehow deal
with the OS recycling a file handle...
And it's still not an un-@safe crash! It's just a program with a bug.

> It's a
> horrible, but still @safe, implementation of FileHandle, yes, but
> the caller (user) doesn't know that, and can't know that just
> from the interface. The only advice against that is "don't do
> that", but that's irrespective of your proposal.

No proposal can (or should) address these issues. You're concerned
with an issue that is so far left-field at this stage.
Programming languages don't validate that you wrote a working bug-free program.

> > I'm going to assume that `shareWithThreads()` was implemented
> > by an
> > 'expert' who checked the function results for errors. It was
> > detected that the reads/write failed, and an error "failed to
> > read file" was emit, then the function returned promptly.
> > The uncertainty of what happens in this program is however
> > `shareWithThreads()` handles read/write emitting an error.
>
> But you can only find out about these errors in `waitForThreads`,
> the very call that the user "forgot" to make!

...what?

> [ ... snip ... ]

You have to concede defeat at this point.

Destroy my proposal with another legitimately faulty program.


> I understand that. So... it would seem that your proposal focuses
> more on @safe than on threadsafety?

I am trying to achieve @safe-ty _with respect to threadsafety_.

'threadsafety' with respect to "proper program operation" is a job for
programmers, and program architecture.
No language attribute can make your program right.


Re: shared - i need it to be useful

2018-10-21 Thread 12345swordy via Digitalmars-d

On Sunday, 21 October 2018 at 18:45:15 UTC, Walter Bright wrote:
I'd like to add that if the compiler can prove that a T* points 
to a unique T, then it can be implicitly cast to shared(T)*. 
And it does so, like the result of .dup can be so converted.


This can be achieved by using the unique struct and enforce the 
uniqueness at compile time.


https://github.com/dlang/phobos/blob/master/std/typecons.d#L130


Re: shared - i need it to be useful

2018-10-21 Thread Nicholas Wilson via Digitalmars-d

On Sunday, 21 October 2018 at 09:50:09 UTC, Walter Bright wrote:

 Manu's Proposal ---
@safe:
int i;
int* a = 
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!


No, does not compile, lockedIncrement takes an int*
Error cannot convert shared(int)* to int*

Your proposal means that the person writing the 
lockedIncrement(), which is a perfectly reasonable thing to do,


Indeed.

simply cannot write it in a way that has a @safe interface, 
because the person writing the lockedIncrement() library 
function has no way to know that the data it receives is 
actually unshared data.


It does, it takes an int* which is not implicitly convertible to 
given an shared(int)*



I.e. @trusted code is obliged to proved a safe interface.


Yes.

Your proposal makes that impossible because the compiler would 
allow unshared data to be implicitly typed as shared.


Yes, but not the other way around.



Re: shared - i need it to be useful

2018-10-21 Thread Manu via Digitalmars-d
On Sun, Oct 21, 2018 at 12:00 PM Timon Gehr via Digitalmars-d
 wrote:
>
> On 21.10.18 17:54, Nicholas Wilson wrote:
> >
> >> As soon as that is done, you've got a data race with the other
> >> existing unshared aliases.
> >
> > You're in @trusted code, that is the whole point. The onus is on the
> > programmer to make that correct, same with regular @safe/@trusted@system
> > code.
>
> Not all of the parties that participate in the data race are in @trusted
> code. The point of @trusted is modularity: you manually check @trusted
> code according to some set of restrictions and then you are sure that
> there is no memory corruption.
>
> Note that you are not allowed to look at any of the @safe code while
> checking your @trusted code. You will only see an opaque interface to
> the @safe code that you call and all you know is that all the @safe code
> type checks according to @safe rules. Note that there might be an
> arbitrary number of @safe functions and methods that you do not see.
>
> Think about it this way: you first write all the @trusted and @system
> code, and some evil guy who does not like you comes in after you looks
> at your code and writes all the @safe code. If there is any memory
> corruption, it will be your fault and you will face harsh consequences.
> Now, design the @safe type checking rules. It won't be MP!
>
> Note that there may well be a good way to get the good properties of MP
> without breaking the type system, but MP itself is not good because it
> breaks @safe.

Show me. Nobody has been able to show that yet. I'd really like to know this.


Re: shared - i need it to be useful

2018-10-21 Thread Timon Gehr via Digitalmars-d

On 21.10.18 17:54, Nicholas Wilson wrote:


As soon as that is done, you've got a data race with the other 
existing unshared aliases.


You're in @trusted code, that is the whole point. The onus is on the 
programmer to make that correct, same with regular @safe/@trusted@system 
code.


Not all of the parties that participate in the data race are in @trusted 
code. The point of @trusted is modularity: you manually check @trusted 
code according to some set of restrictions and then you are sure that 
there is no memory corruption.


Note that you are not allowed to look at any of the @safe code while 
checking your @trusted code. You will only see an opaque interface to 
the @safe code that you call and all you know is that all the @safe code 
type checks according to @safe rules. Note that there might be an 
arbitrary number of @safe functions and methods that you do not see.


Think about it this way: you first write all the @trusted and @system 
code, and some evil guy who does not like you comes in after you looks 
at your code and writes all the @safe code. If there is any memory 
corruption, it will be your fault and you will face harsh consequences. 
Now, design the @safe type checking rules. It won't be MP!


Note that there may well be a good way to get the good properties of MP 
without breaking the type system, but MP itself is not good because it 
breaks @safe.


Re: shared - i need it to be useful

2018-10-21 Thread Walter Bright via Digitalmars-d
I'd like to add that if the compiler can prove that a T* points to a unique T, 
then it can be implicitly cast to shared(T)*. And it does so, like the result of 
.dup can be so converted.


Re: shared - i need it to be useful

2018-10-21 Thread Manu via Digitalmars-d
On Sun, Oct 21, 2018 at 3:00 AM Walter Bright via Digitalmars-d
 wrote:
>
> On 10/20/2018 11:08 AM, Nicholas Wilson wrote:
> > You can if no-one else writes to it, which is the whole point of Manu's
> > proposal. Perhaps it should be const shared instead of shared but still.
>
> There is no purpose whatsoever to data that can be neither read nor written.

There is no primitive type with implicit threadsafety... nor need there be.
Shared data simply can not be read or written in any threadsafe
manner. This is a rock-solid reality, and the type system needs to
reflect that reality as a fundamental premise.
Only from there can we start to define meaningful threadsafety.

All threadsafe interactions with anything involve calling functions.
It's completely reasonable to make `shared` inhibit all read and write
access to data. We can only call shared methods, because only
real-code can implement threadsafety.

> Shared data is only useful if, at some point, it is read/written, presumably 
> by
> casting it to unshared in @trusted code. As soon as that is done, you've got a
> data race with the other existing unshared aliases.

If such a race is possible, then the @trusted function is not
threadsafe, so it is not @trusted by definition.
You wrote a bad @trusted function, and you should feel bad.

The simplest way to guarantee that no unsafe access is possible is to
use encapsulation to assure no unregulated access exists.


Re: shared - i need it to be useful

2018-10-21 Thread Manu via Digitalmars-d
On Sun., 21 Oct. 2018, 2:55 am Walter Bright via Digitalmars-d,
 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 = 
> 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-@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*)); }
}

Atomic!int i;
Atomic!int* a = 
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 

Re: shared - i need it to be useful

2018-10-21 Thread Neia Neutuladh via Digitalmars-d
On Sat, 20 Oct 2018 22:47:14 -0700, Manu wrote:
> Looking at the meat of the program; you open a file, and distribute it
> to do accesses (I presume?)
> Naturally, this is a really weird thing to do, because even if the API
> is threadsafe such that it doesn't crash and reads/writes are
> serialised, the sequencing of reads/writes will be random, so I don't
> believe any sane person (let alone an expert) would write this
> program... but moving on.
> Then you wait for them to finish, and close the file.
> 
> Fine. You have a file with randomly interleaved data... for whatever
> reason.

I'd expect almost every nontrivial multithreaded program to do this. It's 
called a log file.

You don't need to read data pointed at by a log file, but you do need to 
read the FILE* or the file descriptor.

Database-like things using a journal file might need a shared file for 
both reading and writing.

> So, you call closeFile immediately and read/write start returning null.

You start threads. You give them access to the log file. You wait for the 
threads to exit. Then you close the file.


Re: shared - i need it to be useful

2018-10-21 Thread Nicholas Wilson via Digitalmars-d

On Sunday, 21 October 2018 at 09:58:18 UTC, Walter Bright wrote:

On 10/20/2018 11:08 AM, Nicholas Wilson wrote:
You can if no-one else writes to it, which is the whole point 
of Manu's proposal. Perhaps it should be const shared instead 
of shared but still.


There is no purpose whatsoever to data that can be neither read 
nor written.


Indeed but there is a subtle difference between that and Manu's 
proposal: access through the shared variable may not have 
non-atomic reads, not no reads.


Shared data is only useful if, at some point, it is 
read/written,


Yes


presumably by casting it to unshared in @trusted code.


That is one way to do it, others include atomics  and other 
@trusted primitives


As soon as that is done, you've got a data race with the other 
existing unshared aliases.


You're in @trusted code, that is the whole point. The onus is on 
the programmer to make that correct, same with regular 
@safe/@trusted@system code.




Re: shared - i need it to be useful

2018-10-21 Thread Simen Kjærås via Digitalmars-d
On Sunday, 21 October 2018 at 13:24:49 UTC, Stanislav Blinov 
wrote:

On Sunday, 21 October 2018 at 11:25:16 UTC, aliak wrote:
When I say ok, I mean assuming the implementer actually wrote 
correct code. This applies to any shared method today as well.


This ("ok") can only be achieved if the "implementor" (the 
"expert") writes every function self-contained, at which point 
sharing something from user code becomes a non-issue (i.e. it 
becomes unnecessary). But that's not a very useful API. As soon 
as you have more than one function operating on the same data, 
the onus is on the user (the caller) to call those functions in 
correct order, or, more generally, without invalidating the 
state of shared data.


The onus is *always* on the user to write function calls in the 
correct order, multi-threading or not. We expect programmers to 
be able to figure out why this doesn't print 'Hello world!':


void main() {
import std.stdio;
string hello;
writeln(hello);
hello = "Hello world!";
}

We also expect writeln to be written in such a way that it 
doesn't corrupt random data or cause life-threatening situations 
just because hello was uninitialized upon calling writeln, and 
assigned afterwards. We should expect the same of multi-threaded 
programs.


This places the onus of writing thread-safe code on the writer of 
the multi-threaded equivalent of writeln and string.opAssign. 
Only this way can the user of the library write code and not 
expect things to blow up in their face.


--
  Simen


Re: shared - i need it to be useful

2018-10-21 Thread Simen Kjærås via Digitalmars-d
On Sunday, 21 October 2018 at 12:45:43 UTC, Stanislav Blinov 
wrote:

On Sunday, 21 October 2018 at 05:47:14 UTC, Manu wrote:
On Sat, Oct 20, 2018 at 10:10 AM Stanislav Blinov via 
Digitalmars-d  wrote:


Synchronized with what? You still have `a`, which isn't 
`shared` and doesn't require any atomic access or 
synchronization. At this point it doesn't matter if it's an 
int or a struct. As soon as you share `a`, you can't just 
pretend that reading or writing `a` is safe.


`b` can't read or write `a`... accessing `a` is absolutely 
safe.


It's not, with or without your proposal. The purpose of sharing 
`a` into `b` is to allow someone to access `*a` in a threadsafe 
way (but un-@safe, as it *will* require casting away `shared` 
from `b`). That is what's making keeping an unshared reference 
`a` un-@safe: whoever accesses `*a` in their @trusted 
implementations via `*b` can't know that `*a` is being 
(@safe-ly!) accessed in a non-threadsafe way at the same time.


Then someone has not done their job. Since the pieces of code 
that will actually use the un-@safe building blocks at the bottom 
are few and far between, it is reasonable to assume that an 
expert will be writing this code, and that such code be placed in 
a separate module where all access to the shared type is 
controlled.


It seems you expect regular users to have calls to atomicOp!"++" 
scattered all over their code. I find this an unreasonable 
expectation, and fully agree that this will lead to problems.



Someone must do something unsafe to undermine your 
threadsafety... and
if you write unsafe code and don't know what you're doing, 
there's

nothing that can help you.


Ergo, it follows that anyone that is making an implicit cast 
from mutable to shared better know what they're doing, which 
mere mortal users (not "experts") might not. I.e. it's a way to 
giving a loaded gun to someone who never held a weapon before.


No.


Close does not promise threadsafety itself (but of course, it 
doesn't violate read/write's promise, or the program is 
invalid).


Yep, and that's the issue. It SHALL NOT violate threadsafety, 
but it can't promise such in any way :(


Can you demonstrate any system that can promise something like 
that? (apart from all-immutable)



read and write will appropriately check their file-open state 
each time they perform their actions.


Why? The only purpose of giving someone a `shared` reference is 
to give a reference to an open file. `shared` references can't 
do anything with the file but read and write, they would expect 
to be able to do so.


Because otherwise it's not thread-safe. Exactly as you point out, 
the owner could call closeFile before some other thread was 
finished writing. If the implementer of FileHandle fails to take 
this into account, then no, it's not thread-safe.



I'm going to assume that `shareWithThreads()` was implemented  
by an
'expert' who checked the function results for errors. It was 
detected that the reads/write failed, and an error "failed to 
read file" was emit, then the function returned promptly.

The uncertainty of what happens in this program is however
`shareWithThreads()` handles read/write emitting an error.


But you can only find out about these errors in 
`waitForThreads`, the very call that the user "forgot" to make!


Of course not. You can throw exceptions, you could add a 
destructor that reports on these errors, you could set an error 
flag somewhere and check that every now and then. The fact that 
you've managed to write a horribly broken API under MP and can't 
see a way to do better inside that system does not necessarily 
mean the problem is with MP.


--
  Simen


Re: shared - i need it to be useful

2018-10-21 Thread Simen Kjærås via Digitalmars-d

On Sunday, 21 October 2018 at 09:58:18 UTC, Walter Bright wrote:

On 10/20/2018 11:08 AM, Nicholas Wilson wrote:
You can if no-one else writes to it, which is the whole point 
of Manu's proposal. Perhaps it should be const shared instead 
of shared but still.


There is no purpose whatsoever to data that can be neither read 
nor written. Shared data is only useful if, at some point, it 
is read/written, presumably by casting it to unshared in 
@trusted code. As soon as that is done, you've got a data race 
with the other existing unshared aliases.


No, because every part of the public interface has to work 
together to ensure thread-safety.


This code is invalid (but compiles) under MP:

module A;
struct S {
private int n;
void foo() @safe {
n--; // Not thread-safe
}
void bar() shared @trusted {
atomicOp!"++"(n.assumeUnshared);
}
}

module B;
import A;
void passToOtherThread(shared(S)*); // Calls S.bar()

void main() {
S* s = new S();
passToOtherThread(s);
s.foo();
}

The reason: foo() breaks bar()s promise of thread-safety. This 
means that S does not provide a thread-safe interface. It would 
be nice if the compiler could statically notice that, but I don't 
see how that'd work.


Now, for a thread-safe version:

module A;
struct S {
int n;
void foo() @trusted {
atomicOp!"--"(n); // Thread-safe
}
void bar() shared @trusted {
atomicOp!"++"(n.assumeUnshared);
}
}

module B;
import A;
void passToOtherThread(shared(S)*); // Calls S.bar()

void main() {
S* s = new S();
passToOtherThread(s);
s.foo();
}

In this case, passToOtherThread is free to call S.bar as often as 
it may feel like, since atomic operations are used in every 
possible access to S.n. This is true even though one thread has 
unshared access and other threads have shared access.


--
  Simen


Re: shared - i need it to be useful

2018-10-21 Thread Simen Kjærås via Digitalmars-d

On Sunday, 21 October 2018 at 09:50:09 UTC, Walter Bright 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.


No. Instead, it proposes something more useful: once cast to 
shared(T)*, only thread-safe operations may be performed on it.




> int* a;
> shared(int)* b = a;

This is not safe.


Under MP, this is perfectly safe - you can do nothing with a 
shared(int)*, except call un-@safe, non-thread-safe functions on 
it, which will *fail to compile* under @safe.




 Manu's Proposal ---
@safe:
int i;
int* a = 
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!
}


Someone's messed up if they've marked lockedIncrement @safe - 
under MP, it shouldn't be. lockedIncrement is a very low-level 
piece of functionality, and should be @system. It also shouldn't 
take a shared(int)*, but a int*, forcing an unsafe cast and 
making it obvious the code is un@safe.


--
  Simen


Re: shared - i need it to be useful

2018-10-21 Thread Stanislav Blinov via Digitalmars-d

On Sunday, 21 October 2018 at 11:25:16 UTC, aliak wrote:
On Saturday, 20 October 2018 at 16:41:41 UTC, Stanislav Blinov 
wrote:
Those are not "ok". They're only "ok" under Manu's proposal so 
long as the author of C promises (via documentation) that 
that's indeed "ok". There can be no statically-enforced 
guarantees that those calls are "ok", or that issuing them in 
that order is "ok". Yet Manu keeps insisting that somehow 
there is.


No he is not insisting you can statically enforce thread safety.


I stand corrected, it would seem so.

When I say ok, I mean assuming the implementer actually wrote 
correct code. This applies to any shared method today as well.


This ("ok") can only be achieved if the "implementor" (the 
"expert") writes every function self-contained, at which point 
sharing something from user code becomes a non-issue (i.e. it 
becomes unnecessary). But that's not a very useful API. As soon 
as you have more than one function operating on the same data, 
the onus is on the user (the caller) to call those functions in 
correct order, or, more generally, without invalidating the state 
of shared data.


Re: shared - i need it to be useful

2018-10-21 Thread Stanislav Blinov via Digitalmars-d

On Sunday, 21 October 2018 at 05:47:14 UTC, Manu wrote:
On Sat, Oct 20, 2018 at 10:10 AM Stanislav Blinov via 
Digitalmars-d  wrote:


Synchronized with what? You still have `a`, which isn't 
`shared` and doesn't require any atomic access or 
synchronization. At this point it doesn't matter if it's an 
int or a struct. As soon as you share `a`, you can't just 
pretend that reading or writing `a` is safe.



`b` can't read or write `a`... accessing `a` is absolutely safe.


It's not, with or without your proposal. The purpose of sharing 
`a` into `b` is to allow someone to access `*a` in a threadsafe 
way (but un-@safe, as it *will* require casting away `shared` 
from `b`). That is what's making keeping an unshared reference 
`a` un-@safe: whoever accesses `*a` in their @trusted 
implementations via `*b` can't know that `*a` is being 
(@safe-ly!) accessed in a non-threadsafe way at the same time.


Someone must do something unsafe to undermine your 
threadsafety... and
if you write unsafe code and don't know what you're doing, 
there's

nothing that can help you.


Ergo, it follows that anyone that is making an implicit cast from 
mutable to shared better know what they're doing, which mere 
mortal users (not "experts") might not. I.e. it's a way to giving 
a loaded gun to someone who never held a weapon before.



Today, every interaction with shared is unsafe.


Nod.

Creating a safe interaction with shared will lead to people not 
doing unsafe things at every step.


Triple nod.


Encapsulate it all you want, safety only remains a
contract of convention, the language can't enforce it.


You're talking about @trusted code again. You're fixated on 
unsafe interactions... my proposal is about SAFE interactions. 
I'm trying to obliterate unsafe interactions with shared.


I know... Manu, I *know* what you're trying to do. We (me, Atila, 
Timon, Walter...) are not opposing your goals, we're pointing out 
the weakest spot of your proposal, which, it would seem, would 
require more changes to the language than just disallowing 
reading/writing `shared` members.



module expertcode;

@safe:

struct FileHandle {
 @safe:

 void[] read(void[] storage) shared;
 void[] write(const(void)[] buffer) shared;
}

FileHandle openFile(string path);
// only the owner can close
void closeFile(ref FileHandle);

void shareWithThreads(shared FileHandle*); // i.e. generate a
number of jobs in some queue
void waitForThreads(); // waits until all
processing is done

module usercode;

import expertcode;

void processHugeFile(string path) {
 FileHandle file = openFile(path);
 shareWithThreads();// implicit cast
 waitForThreads();
 file.closeFile();
}


This is a very strange program...


Why? That's literally the purpose of being able to `share`: you 
create/acquire a resource, share it, but keep a non-`shared` 
reference to yourself. If that's not required, you'd just create 
the data `shared` to begin with.


I'm dubious it is in fact "expertcode"... but let's look into 
it.


You're fixating on it being file now. I give an abstract example, 
you dismiss it as contrived, I give a concrete one, you want to 
dismiss it as "strange".


Heh, replace 'FileHandle' with 'BackBuffer', 'openFile' with 
'acquireBackBuffer', 'shareWithThreads' with 
'generateDrawCommands', 'waitForThreads' with 
'gatherCommandsAndDraw', 'closeFile' with 'postProcessAndPresent' 
;)


File handle seems to have just 2 methods... and they are both 
threadsafe. Open and Close are free-functions.


It doesn't matter if they're free functions or not. What matters 
is signature: they're taking non-`shared` (i.e. 'owned') 
reference. Methods are free functions in disguise.


Close does not promise threadsafety itself (but of course, it 
doesn't violate read/write's promise, or the program is 
invalid).


Yep, and that's the issue. It SHALL NOT violate threadsafety, but 
it can't promise such in any way :(


I expect the only possible way to achieve this is by an 
internal mutex to make sure read/write/close calls are 
serialised.


With that particular interface, yes.

read and write will appropriately check their file-open state 
each time they perform their actions.


Why? The only purpose of giving someone a `shared` reference is 
to give a reference to an open file. `shared` references can't do 
anything with the file but read and write, they would expect to 
be able to do so.


What read/write do in the case of being called on a closed 
file... anyones guess? I'm gonna say they do no-op... they 
return a null pointer to indicate the error state.


Looking at the meat of the program; you open a file, and 
distribute it to do accesses (I presume?)


Naturally, this is a really weird thing to do, because even if 
the API is threadsafe such that it doesn't crash and 
reads/writes are
serialised, the sequencing of reads/writes will be random, so I 
don't believe any sane person (let alone an expert) would write 

Re: shared - i need it to be useful

2018-10-21 Thread aliak via Digitalmars-d
On Saturday, 20 October 2018 at 16:41:41 UTC, Stanislav Blinov 
wrote:
Those are not "ok". They're only "ok" under Manu's proposal so 
long as the author of C promises (via documentation) that 
that's indeed "ok". There can be no statically-enforced 
guarantees that those calls are "ok", or that issuing them in 
that order is "ok". Yet Manu keeps insisting that somehow there 
is.


No he is not insisting you can statically enforce thread safety.

When I say ok, I mean assuming the implementer actually wrote 
correct code. This applies to any shared method today as well.




Re: shared - i need it to be useful

2018-10-21 Thread Stanislav Blinov via Digitalmars-d

On Sunday, 21 October 2018 at 09:58:18 UTC, Walter Bright wrote:

On 10/20/2018 11:08 AM, Nicholas Wilson wrote:
You can if no-one else writes to it, which is the whole point 
of Manu's proposal. Perhaps it should be const shared instead 
of shared but still.


There is no purpose whatsoever to data that can be neither read 
nor written. Shared data is only useful if, at some point, it 
is read/written, presumably by casting it to unshared in 
@trusted code. As soon as that is done, you've got a data race 
with the other existing unshared aliases.


Just a thought: if a hard requirement is made on `shared` data to 
be non-copyable, a @safe conversion could be guaranteed. But it 
can't be implicit either:


shared(T) share(T)(T value) if (!is(T == shared) && 
!isCopyable!T) {

shared(T) result = move(value);
return result;
}

struct ShareableData {
@disable ; // Generated by 
compiler in presence of `shared` members and/or `shared` methods


/* ... */
}

void sendToThread(T)(shared T* ptr) @safe;

void usage() @safe {
int x;
sendToThread(); // Error: 'x' is not shared
shared y = x; // Ok
sendToThread(); // Ok


ShareableData data;
sendToThread(); // Error: 'data' is not shared
auto p = 
sendToThread(p); // Error: *p is not shared

auto sharedData = share(move(data));
sendToThread(); // Ok

auto yCopy = y;   // Error: cannot copy 'shared' y
auto dataCopy = sharedData; // Error: cannot copy 'shared' 
sharedData


ShareableData otherData;
sendToThread(cast(shared(ShareableData)*) ); // 
Error non-@safe cast in @safe code

}

And again, we're back to 'once it's shared, it can't be @safe-ly 
unshared', which ruins the distinction between owned and shared 
references, which is one of the nicer properties that Manu's 
proposal seems to want to achieve :(


Re: shared - i need it to be useful

2018-10-21 Thread Walter Bright via Digitalmars-d

On 10/20/2018 11:08 AM, Nicholas Wilson wrote:
You can if no-one else writes to it, which is the whole point of Manu's 
proposal. Perhaps it should be const shared instead of shared but still.


There is no purpose whatsoever to data that can be neither read nor written. 
Shared data is only useful if, at some point, it is read/written, presumably by 
casting it to unshared in @trusted code. As soon as that is done, you've got a 
data race with the other existing unshared aliases.




Re: shared - i need it to be useful

2018-10-21 Thread rikki cattermole via Digitalmars-d

On 21/10/2018 10:41 PM, Manu wrote:
On Sun., 21 Oct. 2018, 2:05 am Walter Bright via Digitalmars-d, 
mailto:digitalmars-d@puremagic.com>> wrote:


On 10/20/2018 11:30 AM, Manu wrote:
 > You can write an invalid program in any imaginable number of ways;
 > that's just not an interesting discussion.

What we're discussing is not an invalid program, but what guarantees
the type
system can provide.

D's current type system guarantees that a T* and a shared(T)* do not
point to
the same memory location in @safe code.


My proposal guarantees that too, but in a more interesting way, because 
it opens the door to a whole working model. And it's totally @safe.


To get them to point to the same memory location, you've got to dip
into @system
code, where *you* become responsible for maintaining the guarantees.


My model preserves that property. Why do you think I'm running that 
static guarantee?


It's all irrelevant if you don't express any mechanism to *do* anything. 
Shared today does not have any use. It simply expresses that data *is* 
shared, and says nothing about what you can do with it.
If you don't express a safe mechanism for interacting with shared data, 
then simply expressing the distinction of shared data really is 
completely uninteresting.
It's just a marker that's mixed up in a bunch of unsafe code. I'm no 
more satisfied than I am with C++.


Shared needs to do something; I propose that it strictly models 
operations that are threadsafe and semantic restrictions required to 
support that, and then you have a *usage* scheme, which is safe, and API 
conveys proper interaction.. not just an uninteresting marker.


I'm genuinely amazed that you're not intrigued by a @safe shared 
proposition. Nobly likes @safe more than you.


I could run our entire SMP stack 100% @safe.

I am going to fork D with this feature one way or another. It's the most 
meaningful and compelling opportunity I've seen in ever. If there's ever 
been a single thing that could truly move a bunch of C++ programmers, 
this is it. C++ can do a crappy job of modelling most stuff in D, but it 
simply can't go anywhere near this, and I've been working on competing 
C++ models for months.
SMP is the future, we're going all-in this generation. Almost every 
function in our codebase runs in an SMP environment... And I was 
staggered that I was able to work this definition through to such a 
simple and elegant set of rules.
I can't get my head around why people aren't more excited about this... 
fully @safe SMP is huge!


I'm excited, but you need to write a DIP even if preliminary which shows 
both new semantics but also shows both working and current code to 
compare them.




Re: shared - i need it to be useful

2018-10-21 Thread Walter Bright via Digitalmars-d

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.


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. Using a 'scope' qualifier won't work, because 'scope' isn't transitive, 
while shared is, i.e. U** and shared(U*)*.


> 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 = 
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!
}
--- Current D ---
@safe:
int i;
int* a = 
StartNewThread(a);   // Danger, Will Robinson! Does Not Compile!
StartNewThread(cast(shared(int)*) a) // Danger, Will Robinson!
 // Unsafe Cast! Does Not Compile!
---

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, because the person writing the lockedIncrement() library 
function has no way to know that the data it receives is actually unshared data.


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.


Re: shared - i need it to be useful

2018-10-21 Thread Manu via Digitalmars-d
On Sun., 21 Oct. 2018, 2:05 am Walter Bright via Digitalmars-d, <
digitalmars-d@puremagic.com> wrote:

> On 10/20/2018 11:30 AM, Manu wrote:
> > You can write an invalid program in any imaginable number of ways;
> > that's just not an interesting discussion.
>
> What we're discussing is not an invalid program, but what guarantees the
> type
> system can provide.
>
> D's current type system guarantees that a T* and a shared(T)* do not point
> to
> the same memory location in @safe code.
>

My proposal guarantees that too, but in a more interesting way, because it
opens the door to a whole working model. And it's totally @safe.

To get them to point to the same memory location, you've got to dip into
> @system
> code, where *you* become responsible for maintaining the guarantees.
>

My model preserves that property. Why do you think I'm running that static
guarantee?

It's all irrelevant if you don't express any mechanism to *do* anything.
Shared today does not have any use. It simply expresses that data *is*
shared, and says nothing about what you can do with it.
If you don't express a safe mechanism for interacting with shared data,
then simply expressing the distinction of shared data really is completely
uninteresting.
It's just a marker that's mixed up in a bunch of unsafe code. I'm no more
satisfied than I am with C++.

Shared needs to do something; I propose that it strictly models operations
that are threadsafe and semantic restrictions required to support that, and
then you have a *usage* scheme, which is safe, and API conveys proper
interaction.. not just an uninteresting marker.

I'm genuinely amazed that you're not intrigued by a @safe shared
proposition. Nobly likes @safe more than you.

I could run our entire SMP stack 100% @safe.

I am going to fork D with this feature one way or another. It's the most
meaningful and compelling opportunity I've seen in ever. If there's ever
been a single thing that could truly move a bunch of C++ programmers, this
is it. C++ can do a crappy job of modelling most stuff in D, but it simply
can't go anywhere near this, and I've been working on competing C++ models
for months.
SMP is the future, we're going all-in this generation. Almost every
function in our codebase runs in an SMP environment... And I was staggered
that I was able to work this definition through to such a simple and
elegant set of rules.
I can't get my head around why people aren't more excited about this...
fully @safe SMP is huge!

>


Re: shared - i need it to be useful

2018-10-21 Thread Simen Kjærås via Digitalmars-d

On Sunday, 21 October 2018 at 09:04:34 UTC, Walter Bright wrote:

On 10/20/2018 11:30 AM, Manu wrote:
You can write an invalid program in any imaginable number of 
ways;

that's just not an interesting discussion.


What we're discussing is not an invalid program, but what 
guarantees the type system can provide.


D's current type system guarantees that a T* and a shared(T)* 
do not point to the same memory location in @safe code.


To get them to point to the same memory location, you've got to 
dip into @system code, where *you* become responsible for 
maintaining the guarantees.


The only difference between this and Manu's proposal is when you 
need to dip into @system code - in MP it's perfectly fine for the 
pointers to be equal, but when you want to read from or write to 
the address, you'll need to use @system. In other words, the dip 
into @system happens deeper in the codebase, meaning more code 
can be @safe.


--
  Simen


Re: shared - i need it to be useful

2018-10-21 Thread Walter Bright via Digitalmars-d

On 10/20/2018 11:30 AM, Manu wrote:

You can write an invalid program in any imaginable number of ways;
that's just not an interesting discussion.


What we're discussing is not an invalid program, but what guarantees the type 
system can provide.


D's current type system guarantees that a T* and a shared(T)* do not point to 
the same memory location in @safe code.


To get them to point to the same memory location, you've got to dip into @system 
code, where *you* become responsible for maintaining the guarantees.


Re: shared - i need it to be useful

2018-10-20 Thread Manu via Digitalmars-d
On Sat, Oct 20, 2018 at 10:10 AM Stanislav Blinov via Digitalmars-d
 wrote:
>
> On Saturday, 20 October 2018 at 16:48:05 UTC, Nicholas Wilson
> wrote:
> > On Saturday, 20 October 2018 at 09:04:17 UTC, Walter Bright
> > wrote:
> >> On 10/19/2018 11:18 PM, Manu wrote:
> >>> The reason I ask is because, by my definition, if you have:
> >>> int* a;
> >>> shared(int)* b = a;
> >>>
> >>> While you have 2 numbers that address the same data, it is
> >>> not actually aliased because only `a` can access it.
> >>
> >> They are aliased,
> >
> > Quoting Wikipedia:
> >
> >>two pointers A and B which have the same value, then the name
> >>A[0] aliases the name B[0]. In this case we say the pointers A
> >>and B alias each other. Note that the concept of pointer
> >>aliasing is not very well-defined – two pointers A and B may or
> >>may not alias each other, depending on what operations are
> >>performed in the function using A and B.
> >
> > In this case given the above: `a[0]` does not alias `b[0]`
> > because `b[0]` is ill defined under Manu's proposal, because
> > the memory referenced by `a` is not reachable through `b`
> > because you can't read or write through `b`.
> >
> >> by code that believes it is unshared
> >
> > you cannot `@safe`ly modify the memory  through `b`, `a`'s view
> > of the memory is unchanged in @safe code.
>
> And that's already a bug, because the language can't enforce
> threadsafe access through `a`, regardless of presence of `b`.
> Only the programmer can.
>
> >> and, code that believes it is shared.
> >
> > you cannot have non-atomic access though `b`, `b` has no @safe
> > view of the memory, unless it is atomic (which by definition is
> > synchronised).
>
> Synchronized with what? You still have `a`, which isn't `shared`
> and doesn't require any atomic access or synchronization. At this
> point it doesn't matter if it's an int or a struct. As soon as
> you share `a`, you can't just pretend that reading or writing `a`
> is safe.

`b` can't read or write `a`... accessing `a` is absolutely safe.
Someone must do something unsafe to undermine your threadsafety... and
if you write unsafe code and don't know what you're doing, there's
nothing that can help you.
Today, every interaction with shared is unsafe. Creating a safe
interaction with shared will lead to people not doing unsafe things at
every step.

> Encapsulate it all you want, safety only remains a
> contract of convention, the language can't enforce it.

You're talking about @trusted code again. You're fixated on unsafe
interactions... my proposal is about SAFE interactions. I'm trying to
obliterate unsafe interactions with shared.


> module expertcode;
>
> @safe:
>
> struct FileHandle {
>  @safe:
>
>  void[] read(void[] storage) shared;
>  void[] write(const(void)[] buffer) shared;
> }
>
> FileHandle openFile(string path);
> // only the owner can close
> void closeFile(ref FileHandle);
>
> void shareWithThreads(shared FileHandle*); // i.e. generate a
> number of jobs in some queue
> void waitForThreads(); // waits until all
> processing is done
>
> module usercode;
>
> import expertcode;
>
> void processHugeFile(string path) {
>  FileHandle file = openFile(path);
>  shareWithThreads();// implicit cast
>  waitForThreads();
>  file.closeFile();
> }

This is a very strange program... I'm dubious it is in fact
"expertcode"... but let's look into it.

File handle seems to have just 2 methods... and they are both threadsafe.
Open and Close are free-functions. Close does not promise threadsafety
itself (but of course, it doesn't violate read/write's promise, or the
program is invalid).

I expect the only possible way to achieve this is by an internal mutex
to make sure read/write/close calls are serialised. read and write
will appropriately check their file-open state each time they perform
their actions. What read/write do in the case of being called on a
closed file... anyones guess? I'm gonna say they do no-op... they
return a null pointer to indicate the error state.

Looking at the meat of the program; you open a file, and distribute it
to do accesses (I presume?)
Naturally, this is a really weird thing to do, because even if the API
is threadsafe such that it doesn't crash and reads/writes are
serialised, the sequencing of reads/writes will be random, so I don't
believe any sane person (let alone an expert) would write this
program... but moving on.
Then you wait for them to finish, and close the file.

Fine. You have a file with randomly interleaved data... for whatever reason.

> Per your proposal, everything in 'expertcode' can be written
> @safe, i.e. not violating any of the points that @safe forbids,
> or doing so only in a @trusted manner. As far as the language is
> concerned, this would mean that processHugeFile can be @safe as
> well.

This program does appear to be safe (assuming that the implementations
aren't invalid), but a very strange program nonetheless.

> 

Re: shared - i need it to be useful

2018-10-20 Thread Stanislav Blinov via Digitalmars-d

On Saturday, 20 October 2018 at 18:30:59 UTC, Manu wrote:
On Sat, Oct 20, 2018 at 9:45 AM Stanislav Blinov via 
Digitalmars-d  wrote:


On Saturday, 20 October 2018 at 16:18:53 UTC, aliak wrote:

> class C {
>   void f();
>   void g() shared;
> }

Those are not "ok". They're only "ok" under Manu's proposal so 
long as the author of C promises (via documentation) that 
that's indeed "ok". There can be no statically-enforced 
guarantees that those calls are "ok", or that issuing them in 
that order is "ok". Yet Manu keeps insisting that somehow 
there is.


I only insist that if you write a shared method, you promise 
that it is threadsafe.
If f() undermines g() threadsafety, then **g() is NOT 
threadsafe**, and you just write an invalid program.


You can write an invalid program in any imaginable number of 
ways; that's just not an interesting discussion. An interesting 
discussion is what we might to to help prevent writing such an 
invalid program... I don't suggest here what we can do to 
statically encorce this, but I suspect there does exist *some* 
options which may help, which can be further developments.


What I also assert is that *this unsafe code is rare*... it 
exists only at the bottom of the tooling stack, and anyone else 
using a shared object will not do unsafe, and therefore will 
not be able to create the problem. If you do unsafety anywhere 
near `shared`, you should feel nervous. I'm trying to make a 
world where you aren't *required* to do unsafety at every 
single interaction.


Understand: f() can only undermine g() promise of threadsafety 
**if f() is not @safe**. Users won't create this situation 
accidentally, they can only do it deliberately.


---

module expertcode;

@safe:

struct FileHandle {
@safe:

void[] read(void[] storage) shared;
void[] write(const(void)[] buffer) shared;
}

FileHandle openFile(string path);
// only the owner can close
void closeFile(ref FileHandle);

void shareWithThreads(shared FileHandle*); // i.e. generate a 
number of jobs in some queue
void waitForThreads(); // waits until all 
processing is done


module usercode;

import expertcode;

void processHugeFile(string path) {
FileHandle file = openFile(path);
shareWithThreads();// implicit cast
waitForThreads();
file.closeFile();
}

---

Per your proposal, everything in 'expertcode' can be written 
@safe, i.e. not violating any of the points that @safe forbids, 
or doing so only in a @trusted manner. As far as the language is 
concerned, this would mean that processHugeFile can be @safe as 
well.


Remove the call to `waitForThreads()` (assume user just forgot 
that, i.e. the "accident"). Nothing would change for the 
compiler: all calls remain @safe. And yet, if we're lucky, we get 
a consistent instacrash. If we're unlucky, we get memory 
corruption, or an unsolicited write to another currently open 
file, either of which can go unnoticed for some time.


Of course the program becomes invalid if you do that, there's no 
question about it, this goes for all buggy code. The problem is, 
definition of "valid" lies beyond the type system: it's an 
agreement between different parts of code, i.e. between expert 
programmers who wrote FileHandle et al., and users who write 
processHugeFile(). The main issue is that certain *runtime* 
conditions can still violate @safe-ty.


Your proposal makes the language more strict wrt. to writing 
@safe 'expertmodule', thanks to disallowing reads and writes 
through `shared`, which is great.
However the implicit conversion to `shared` doesn't in any way 
improve the situation as far as user code is concerned, unless 
I'm still missing something.


Re: shared - i need it to be useful

2018-10-20 Thread Manu via Digitalmars-d
On Sat, Oct 20, 2018 at 9:45 AM Stanislav Blinov via Digitalmars-d
 wrote:
>
> On Saturday, 20 October 2018 at 16:18:53 UTC, aliak wrote:
>
> > class C {
> >   void f();
> >   void g() shared;
> > }
> >
> > void t1(shared C c) {
> >   c.g; // ok
> >   c.f; // error
> > }
> >
> > void t2(shared C c) {
> >   c.g; // ok
> >   c.f; // error
> > }
> >
> > auto c = new C();
> > spawn(, c);
> > spawn(, c);
> > c.f; // ok
> > c.g; // ok
>
> Those are not "ok". They're only "ok" under Manu's proposal so
> long as the author of C promises (via documentation) that that's
> indeed "ok". There can be no statically-enforced guarantees that
> those calls are "ok", or that issuing them in that order is "ok".
> Yet Manu keeps insisting that somehow there is.

I only insist that if you write a shared method, you promise that it
is threadsafe.
If f() undermines g() threadsafety, then **g() is NOT threadsafe**,
and you just write an invalid program.

You can write an invalid program in any imaginable number of ways;
that's just not an interesting discussion. An interesting discussion
is what we might to to help prevent writing such an invalid program...
I don't suggest here what we can do to statically encorce this, but I
suspect there does exist *some* options which may help, which can be
further developments.

What I also assert is that *this unsafe code is rare*... it exists
only at the bottom of the tooling stack, and anyone else using a
shared object will not do unsafe, and therefore will not be able to
create the problem. If you do unsafety anywhere near `shared`, you
should feel nervous. I'm trying to make a world where you aren't
*required* to do unsafety at every single interaction.

Understand: f() can only undermine g() promise of threadsafety **if
f() is not @safe**. Users won't create this situation accidentally,
they can only do it deliberately.


Re: shared - i need it to be useful

2018-10-20 Thread Manu via Digitalmars-d
On Sat, Oct 20, 2018 at 2:05 AM Walter Bright via Digitalmars-d
 wrote:
>
> On 10/19/2018 11:18 PM, Manu wrote:
> > The reason I ask is because, by my definition, if you have:
> > int* a;
> > shared(int)* b = a;
> >
> > While you have 2 numbers that address the same data, it is not actually 
> > aliased
> > because only `a` can access it.
>
> They are aliased, by code that believes it is unshared, and code that believes
> it is shared.

This situation could only occur if you do unsafe code badly.
Unlike today, where you must do unsafe code to do any interaction of
any kind, you will be able to do fully-safe interaction with the stack
of tooling.
In that world, any unsafe code and *particularly* where it interacts
with shared will be an obnoxious and scary code smell.
If it was possible to interact with shared safely, it would be
blindingly suspicious when people are likely to be shooting themselves
in the foot.

The situation you describe here is *exactly* what we have right now,
and I'm trying to prevent that.

> This is not going to work

This is an unfair dismissal. Have you tried it? I have.
Write me the rules in a patch that I can take for a drive and
demonstrate what the stack looks like.

> > Exclusively distinguishing shared and unshared data is not an interesting
> > distinction if shared data has no access.
>
> Somehow, you still have to find a way to give the shared path access, through 
> a
> gate or a cast or a lock or whatever.

I'm not sure you've understood the proposal.
This is the reason for the implicit conversion. It provides safe
transition. That's why I'm so insistent on it.

> And then it breaks, because two different
> threads are accessing the same data each thinking that data is not shared.

This can only occur if you deliberately violate your @safety, and then mess up.
This is *exactly* the interaction prescribed to shared today. This is
what I'm fixing by making a fully @safe path!

I think you demonstrate here that you haven't understood the reason,
or the semantics of my proposal. I'm not sure how to clarify it, what
can I give you?


Re: shared - i need it to be useful

2018-10-20 Thread Nicholas Wilson via Digitalmars-d
On Saturday, 20 October 2018 at 17:06:22 UTC, Stanislav Blinov 
wrote:
On Saturday, 20 October 2018 at 16:48:05 UTC, Nicholas Wilson 
wrote:
On Saturday, 20 October 2018 at 09:04:17 UTC, Walter Bright 
wrote:

by code that believes it is unshared


you cannot `@safe`ly modify the memory  through `b`, `a`'s 
view of the memory is unchanged in @safe code.


And that's already a bug, because the language can't enforce 
threadsafe access through `a`, regardless of presence of `b`. 
Only the programmer can.


 access through `a` is through the owned reference threadsafety 
through a does't mean anything, all _other_ access must ensure 
that the are ordered correctly.





and, code that believes it is shared.


you cannot have non-atomic access though `b`, `b` has no @safe 
view of the memory, unless it is atomic (which by definition 
is synchronised).


Synchronized with what? You still have `a`, which isn't 
`shared` and doesn't require any atomic access or 
synchronization.


Synchronized w.r.t any writes to that memory, e.g. from `a`.


At this point it doesn't matter if it's an int
or a struct.


Yes.

As soon as you share `a`, you can't just pretend that reading 
or writing `a` is safe.


You can if no-one else writes to it, which is the whole point of 
Manu's proposal. Perhaps it should be const shared instead of 
shared but still.





Re: shared - i need it to be useful

2018-10-20 Thread Nicholas Wilson via Digitalmars-d
On Saturday, 20 October 2018 at 16:41:41 UTC, Stanislav Blinov 
wrote:

On Saturday, 20 October 2018 at 16:18:53 UTC, aliak wrote:


class C {
  void f();
  void g() shared;
}

void t1(shared C c) {
  c.g; // ok
  c.f; // error
}

void t2(shared C c) {
  c.g; // ok
  c.f; // error
}

auto c = new C();
spawn(, c); // line 20
spawn(, c); // line 21
c.f; // ok
c.g; // ok // line 23


Those are not "ok". They're only "ok" under Manu's proposal so 
long as the author of C promises (via documentation) that 
that's indeed "ok". There can be no statically-enforced 
guarantees that those calls are "ok", or that issuing them in 
that order is "ok". Yet Manu keeps insisting that somehow there 
is.


Backing up a bit and making a few observations (after adding 
imports and wrapping the bottom code in a function):


1. the code above currently does not compile, error messages are:
i.  line 20 & 21: spawn fails to instantiate because c is not 
shared
ii. line 23: shared method C.g is not callable using a 
non-shared object

iii. the lines already marked // error

2. in order to fix 1.i, one must cast c to shared at the call 
site, this is not @safe


3 fixing 1.ii requires doing `(cast(shared)c).g`, this is also 
not @safe


4 fixing 1.iii fixing requires casting away shared, this is not 
only not @safe, but also wrong. c is a class so one could try 
locking it although I'm not sure what the implications are for 
doing that when another thread owns the data, probably bad.


5 the current means of dealing with shared with lock and cast 
away shared is also not @safe


6 under Manu's proposal reading and writing shared objects 
results in compilation error


7 The static guarantees we have in the language are type safety 
and @safe


8 under Manu's proposal to do anything one must call shared 
functions on said object, this implies a "@trusted" 
implementation at the bottom of the stack for ensuring thread 
safety (atomics and lock + cast (assuming it is not wrong), other 
sync primitives) that are not @safe, but not outright wrong 
either.


The question then becomes: assuming the implementation _is_ @safe 
type correct and thread safe etc., can the author of C provide 
guarantees of @safe and type correctness? and can this guarantee 
be free of false positives?


Currently the answer is no: the requirement to cast to and from 
shared is un-@safe and that burden is on the user which means 
that they must understand the inner workings of C to know it that 
is the case.


Manu's proposal is slightly more interesting. shared becomes a 
guarantee that accesses to that object will not race, assuming 
that the @trusted implementation at the bottom of the stack are 
correct. In the above if t1 and t2 took `const shared C` and `g` 
was also const shared, then I think that it could.







Re: shared - i need it to be useful

2018-10-20 Thread Stanislav Blinov via Digitalmars-d
On Saturday, 20 October 2018 at 16:48:05 UTC, Nicholas Wilson 
wrote:
On Saturday, 20 October 2018 at 09:04:17 UTC, Walter Bright 
wrote:

On 10/19/2018 11:18 PM, Manu wrote:

The reason I ask is because, by my definition, if you have:
int* a;
shared(int)* b = a;

While you have 2 numbers that address the same data, it is 
not actually aliased because only `a` can access it.


They are aliased,


Quoting Wikipedia:

two pointers A and B which have the same value, then the name 
A[0] aliases the name B[0]. In this case we say the pointers A 
and B alias each other. Note that the concept of pointer 
aliasing is not very well-defined – two pointers A and B may or 
may not alias each other, depending on what operations are 
performed in the function using A and B.


In this case given the above: `a[0]` does not alias `b[0]` 
because `b[0]` is ill defined under Manu's proposal, because 
the memory referenced by `a` is not reachable through `b` 
because you can't read or write through `b`.



by code that believes it is unshared


you cannot `@safe`ly modify the memory  through `b`, `a`'s view 
of the memory is unchanged in @safe code.


And that's already a bug, because the language can't enforce 
threadsafe access through `a`, regardless of presence of `b`. 
Only the programmer can.



and, code that believes it is shared.


you cannot have non-atomic access though `b`, `b` has no @safe 
view of the memory, unless it is atomic (which by definition is 
synchronised).


Synchronized with what? You still have `a`, which isn't `shared` 
and doesn't require any atomic access or synchronization. At this 
point it doesn't matter if it's an int or a struct. As soon as 
you share `a`, you can't just pretend that reading or writing `a` 
is safe. Encapsulate it all you want, safety only remains a 
contract of convention, the language can't enforce it.


Re: shared - i need it to be useful

2018-10-20 Thread Nicholas Wilson via Digitalmars-d

On Saturday, 20 October 2018 at 09:04:17 UTC, Walter Bright wrote:

On 10/19/2018 11:18 PM, Manu wrote:

The reason I ask is because, by my definition, if you have:
int* a;
shared(int)* b = a;

While you have 2 numbers that address the same data, it is not 
actually aliased because only `a` can access it.


They are aliased,


Quoting Wikipedia:

two pointers A and B which have the same value, then the name 
A[0] aliases the name B[0]. In this case we say the pointers A 
and B alias each other. Note that the concept of pointer 
aliasing is not very well-defined – two pointers A and B may or 
may not alias each other, depending on what operations are 
performed in the function using A and B.


In this case given the above: `a[0]` does not alias `b[0]` 
because `b[0]` is ill defined under Manu's proposal, because the 
memory referenced by `a` is not reachable through `b` because you 
can't read or write through `b`.



by code that believes it is unshared


you cannot `@safe`ly modify the memory  through `b`, `a`'s view 
of the memory is unchanged in @safe code.



and, code that believes it is shared.


you cannot have non-atomic access though `b`, `b` has no @safe 
view of the memory, unless it is atomic (which by definition is 
synchronised).



This is not going to work.


Aú contraire.



Re: shared - i need it to be useful

2018-10-20 Thread Stanislav Blinov via Digitalmars-d

On Saturday, 20 October 2018 at 16:18:53 UTC, aliak wrote:


class C {
  void f();
  void g() shared;
}

void t1(shared C c) {
  c.g; // ok
  c.f; // error
}

void t2(shared C c) {
  c.g; // ok
  c.f; // error
}

auto c = new C();
spawn(, c);
spawn(, c);
c.f; // ok
c.g; // ok


Those are not "ok". They're only "ok" under Manu's proposal so 
long as the author of C promises (via documentation) that that's 
indeed "ok". There can be no statically-enforced guarantees that 
those calls are "ok", or that issuing them in that order is "ok". 
Yet Manu keeps insisting that somehow there is.


Re: shared - i need it to be useful

2018-10-20 Thread aliak via Digitalmars-d

On Saturday, 20 October 2018 at 09:04:17 UTC, Walter Bright wrote:
Somehow, you still have to find a way to give the shared path 
access, through a gate or a cast or a lock or whatever. And 
then it breaks, because two different threads are accessing the 
same data each thinking that data is not shared.


When you say that, then under Manu's proposal and the code below:

class C {
  void f();
  void g() shared;
}

void t1(shared C c) {
  c.g; // ok
  c.f; // error
}

void t2(shared C c) {
  c.g; // ok
  c.f; // error
}

auto c = new C();
spawn(, c);
spawn(, c);
c.f; // ok
c.g; // ok

Do you mean the implementation of C.g? Since that is shared 
wouldn't that just be a normal understanding that you'd need to 
synchronize the data access in shared since it's a shared method? 
And if you mean C.f, then if that accessed data (that was 
accessed by C.g) unsafely, then that's just a bad implementation 
no? Or?


Cheers,
- Ali



Re: shared - i need it to be useful

2018-10-20 Thread Walter Bright via Digitalmars-d

On 10/19/2018 11:18 PM, Manu wrote:

The reason I ask is because, by my definition, if you have:
int* a;
shared(int)* b = a;

While you have 2 numbers that address the same data, it is not actually aliased 
because only `a` can access it.


They are aliased, by code that believes it is unshared, and code that believes 
it is shared. This is not going to work.



Exclusively distinguishing shared and unshared data is not an interesting 
distinction if shared data has no access.


Somehow, you still have to find a way to give the shared path access, through a 
gate or a cast or a lock or whatever. And then it breaks, because two different 
threads are accessing the same data each thinking that data is not shared.


Re: shared - i need it to be useful

2018-10-20 Thread Manu via Digitalmars-d
On Fri, Oct 19, 2018 at 9:45 AM Steven Schveighoffer via Digitalmars-d
 wrote:
>
> On 10/18/18 9:09 PM, Manu wrote:
> > On Thu, Oct 18, 2018 at 5:30 PM Timon Gehr via Digitalmars-d
> >  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 

Re: shared - i need it to be useful

2018-10-20 Thread Manu via Digitalmars-d
On Fri., 19 Oct. 2018, 3:10 am Walter Bright via Digitalmars-d, <
digitalmars-d@puremagic.com> wrote:

> On 10/17/2018 12:20 AM, Manu wrote:
> > What does it mean 'aliased' precisely?
>
> Aliasing means there are two paths to the same piece of data. That could
> be two
> pointers pointing to the same data, or one pointer to a variable that is
> accessible by name.
>

The reason I ask is because, by my definition, if you have:
int* a;
shared(int)* b = a;

While you have 2 numbers that address the same data, it is not actually
aliased because only `a` can access it. It is not aliased in any practical
sense.

> It doesn't really give us
> > anything in practice that we don't have in C++.
>
> It provides a standard, enforced way to distinguish shared data from
> unshared
> data, and no way to bypass it in @safe code. There's no way to do that in
> C++.
>

Right, but we can do so much better. I want shared to model "what is
thread-@safe to do", because that models what you are able to do, and what
API's should encourage when operating on `shared` things.

Exclusively distinguishing shared and unshared data is not an interesting
distinction if shared data has no access.
I've been trying to say over and over; ignore what you think you know about
that definition, accept my rules strictly as given (they're very simple and
concise, there's only 2 rules), such that shared will mean "is threadsafe
to call with this data" when applied to function args...
Build the thought experiment outward from there.
That's an interesting and useful definition for shared, and it leads to a
framework where shared is useful in a fully @safe SMP program, and even
models @safe transitions across unshared -> shared boundaries (parallel
for, map/reduce, etc, fork and join style workloads), which are typical
lock-free patterns.

Lock-and-cast semantics are preserved unchanged for those that interact in
the way shared is prescribed today, but strictly modeling that workflow is
uninteresting, because it's unsafe by definition. I'm not losing that, but
I'm trying to introduce a safe workflow that exists in complement, and my
model works.
I don't even know if we have a mutex defined in our codebase, we don't use
them... but we can max out a 64core thread ripper.

>


Re: shared - i need it to be useful

2018-10-19 Thread Simen Kjærås via Digitalmars-d

On Friday, 19 October 2018 at 18:00:47 UTC, Atila Neves wrote:

Because int or int* does not have threadsafe member functions.


https://dlang.org/phobos/core_atomic.html


Atomic and thread-safe are two very different concepts. 
Thread-safe is more of an ecosystem thing - if there are ways to 
do non-thread-safe operations on something, atomic operations are 
not enough to make things thread-safe. This is what core.atomic 
does:


https://i.imgur.com/PnKMigl.jpg

If you close the other openings, atomics are fantastic building 
blocks to making something thread-safe, but on their own they are 
not enough.


--
  Simen


Re: shared - i need it to be useful

2018-10-19 Thread rikki cattermole via Digitalmars-d

On 20/10/2018 2:07 AM, Dominikus Dittes Scherkl wrote:

This document provide no reasoning about what usecases it supports:


It was a basic idea of mine... It was never meant to be PR'd.


Re: shared - i need it to be useful

2018-10-19 Thread Atila Neves via Digitalmars-d

On Thursday, 18 October 2018 at 21:24:53 UTC, jmh530 wrote:

On Thursday, 18 October 2018 at 17:17:37 UTC, Atila Neves wrote:

[snip]


Assuming this world... how do you use shared?


https://github.com/atilaneves/fearless



I had posted your library before to no response...

I had two questions, if you'll indulge me.

The first is perhaps more wrt automem. I noticed that I 
couldn't use automem's Unique with @safe currently. Is there 
any way to make it @safe, perhaps with dip1000?


Yeah, I punted on making anything there @safe. I have to go back 
and fix it. At least I wrote Vector from scratch to be @safe.


Second, Rust's borrow checker is that you can only have one 
mutable borrow. This is kind of like Exclusive, but it does it 
at compile-time, rather than with GC/RC. Is this something that 
can be incorporated into fearless?


Well, Rust's version of Exclusive is Mutex, and that's pretty 
much always used with Arc. The closest we have to a borrow 
checker is DIP1000 and that's what fearless relies on.






Re: shared - i need it to be useful

2018-10-19 Thread Atila Neves via Digitalmars-d
On Thursday, 18 October 2018 at 19:04:58 UTC, Erik van Velzen 
wrote:
On Thursday, 18 October 2018 at 17:47:29 UTC, Stanislav Blinov 
wrote:
On Thursday, 18 October 2018 at 17:17:37 UTC, Atila Neves 
wrote:

On Monday, 15 October 2018 at 18:46:45 UTC, Manu wrote:
Assuming the rules above: "can't read or write to members", 
and the understanding that `shared` methods are expected to 
have threadsafe implementations (because that's the whole 
point), what are the risks from allowing T* -> shared(T)* 
conversion?


int i;
tid.send();
++i;  // oops, data race


Doesn't work. No matter what you show Manu or Simen here they 
think it's just a bad contrived example. You can't sway them 
by the fact that the compiler currently *prevents* this from 
happening.


Manu said clearly that the receiving thread won't be able to 
read or write the pointer.


Not directly - but obviously there must be *some* way to using 
it, in this case since it's an int with one of the core.atomic 
functions. At that point the spawned thread will be accessing it 
correctly, but the parent thread can modify the int in a 
non-atomic fashion.



Because int or int* does not have threadsafe member functions.


https://dlang.org/phobos/core_atomic.html





Re: shared - i need it to be useful

2018-10-19 Thread Manu via Digitalmars-d
On Fri., 19 Oct. 2018, 6:10 am Dominikus Dittes Scherkl via Digitalmars-d, <
digitalmars-d@puremagic.com> wrote:

> On Friday, 19 October 2018 at 06:25:00 UTC, rikki cattermole
> wrote:
> > On 19/10/2018 7:09 PM, Norm wrote:
>
> > [0]
> > https://github.com/rikkimax/DIPs/blob/shared/DIPs/DIP1xxx-RC2.md
>
> This document provide no reasoning about what usecases it
> supports:
>
> Is it possible to create objects that are shared just for short
> periods during their livetime and guarantee that they can be used
> threadsave like Manu want it to be?
>
> Does it prohibit misuse any better than Manus proposal (that
> requires the "Expert" to implement all theadsave API)?
>

No, a key misunderstanding. My proposal is @safe. The only thing an expert
must do is write the few @trusted implementations that live at the very
bottom of the stack.
That would always be in a lib. When was the last time you rewrote std::map
because you thought you could do better?

The whole stack from there on up (the user stack) is safe, and you can have
confidence in the @safe-ty.
My goal is to make it safe, clearly communicate how a user interact with
the API, and mechanically confirm that users do the right stuff.
My proposal is specifically structured to not require *any* unsafe
interactions at the user level. Only core machinery that is @trusted needs
expert attention.

I don't think it's possible to invent a proposal with a higher degree of
verifiable safety.


Re: shared - i need it to be useful

2018-10-19 Thread Steven Schveighoffer via Digitalmars-d

On 10/18/18 9:09 PM, Manu wrote:

On Thu, Oct 18, 2018 at 5:30 PM Timon Gehr via Digitalmars-d
 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...

-Steve


Re: shared - i need it to be useful

2018-10-19 Thread Dominikus Dittes Scherkl via Digitalmars-d
On Friday, 19 October 2018 at 06:25:00 UTC, rikki cattermole 
wrote:

On 19/10/2018 7:09 PM, Norm wrote:


[0] 
https://github.com/rikkimax/DIPs/blob/shared/DIPs/DIP1xxx-RC2.md


This document provide no reasoning about what usecases it 
supports:


Is it possible to create objects that are shared just for short 
periods during their livetime and guarantee that they can be used 
threadsave like Manu want it to be?


Does it prohibit misuse any better than Manus proposal (that 
requires the "Expert" to implement all theadsave API)?


Is the "normal" User still enforced to do some unsave casts?

Has the normal User to have high knownledge of how a threadsave 
API is to be used or can the compiler provide any guarantees that 
using them can only fail if the implementation behind the API has 
bugs (e.g. provide some encapsulation)?


Or any other usecases why and how this design is better than what 
we have now?


And also some ideas how to implement some useacases (examples) 
are completely missing.


Re: shared - i need it to be useful

2018-10-19 Thread Walter Bright via Digitalmars-d

On 10/17/2018 12:20 AM, Manu wrote:

What does it mean 'aliased' precisely?


Aliasing means there are two paths to the same piece of data. That could be two 
pointers pointing to the same data, or one pointer to a variable that is 
accessible by name.



It doesn't really give us
anything in practice that we don't have in C++.


It provides a standard, enforced way to distinguish shared data from unshared 
data, and no way to bypass it in @safe code. There's no way to do that in C++.


Re: shared - i need it to be useful

2018-10-19 Thread rikki cattermole via Digitalmars-d

On 19/10/2018 9:02 PM, Walter Bright wrote:

On 10/17/2018 4:29 AM, jmh530 wrote:

Isn't that also true for isolated data (data that only allows one alias)?


That's colloquially called "unique" data. And yes, it is also true for 
that. That's why casting the return value of malloc() to 'shared' is 
safe. It's just that the language has no way to semantically identify 
unique data with its current type system.


Actually we kind of have a way to do this (I think). Scope. It can't 
cross the thread boundary and it has a pretty clear owner as far as the 
stack is concerned. Given a bit of time and empowering of scope, we 
could do a @scopeonly type attribute for functions.




Re: shared - i need it to be useful

2018-10-19 Thread Walter Bright via Digitalmars-d

On 10/17/2018 4:29 AM, jmh530 wrote:

Isn't that also true for isolated data (data that only allows one alias)?


That's colloquially called "unique" data. And yes, it is also true for that. 
That's why casting the return value of malloc() to 'shared' is safe. It's just 
that the language has no way to semantically identify unique data with its 
current type system.




Re: shared - i need it to be useful

2018-10-19 Thread rikki cattermole via Digitalmars-d

On 19/10/2018 7:09 PM, Norm wrote:
There's another way; Stanislav isn't one you need to convince so if that 
particular discussion is unproductive and disruptive just ignore it. I.e 
technical discussions should be robust but once they become personal 
just ignore that input and move on. Isn't always possible I know but in 
this case I reckon you can.


Trust me, I think a few of us at least have already figured that out.

Convincing Walter, Andrei and the rest of the core dev team of course 
will require a DIP.


Keep going on this, it is the first hint of movement with shared since 
like foreva!


As long as it doesn't look like my idea[0] (Andrei doesn't like it, I 
may have asked) it should have some sort of legs.


[0] https://github.com/rikkimax/DIPs/blob/shared/DIPs/DIP1xxx-RC2.md




Re: shared - i need it to be useful

2018-10-19 Thread Norm via Digitalmars-d

On Friday, 19 October 2018 at 02:20:22 UTC, Manu wrote:
On Thu., 18 Oct. 2018, 7:10 pm Stanislav Blinov via 
Digitalmars-d, < digitalmars-d@puremagic.com> wrote:



On Friday, 19 October 2018 at 01:53:00 UTC, Manu wrote:

> This is a red-herring.
> In short, he made up this issue, it doesn't exist.
> This is just hot air, and only strengthen my conviction.

>> Produce, or drop this presumptious crap.

> You are an obscene person. I'm out.

Oooh, I'm srry, come baack!
Really though, what is it that you wanted to achieve here? You
ask for counter-arguments, are given them on *17 pages 
already*,

are asked numerous times to actually demonstrate the value of a
small contained portion of your proposal, and all you do is 
shrug

this all off just because you presume to "know better", and on
top of that have the audacity to call someone else *obscene*?
Wow... just... wow!

> You win.

I didn't know it was a contest.



I've given use cases constantly, about taking object ownership, 
promotions, and distribution for periods (think parallel for), 
I can achieve all my goals with full @safety, absolutely no 
casts in user code, and I have infrastructure in production 
that applies these patterns successfully. It's worth pursuing.


I've spent years thinking on this, I'm trying to move the 
needle on this issue for the first time in over a decade at 
least, and you have violently opposed, in principle, from the 
very first post, and make no effort to actually understand the 
proposition.


It's clearly a contest from your insurance that my proposal in 
worthless in every single post you've made. You want me to 
admit defeat and desist. Fuck you. You win. I don't have the 
time or energy to argue against a wall.


You are obscene, you're complete unproductive, and destructive 
from no

apparent reason.
I hope you continue to love shared, just the way it is... 
useless.


There's another way; Stanislav isn't one you need to convince so 
if that particular discussion is unproductive and disruptive just 
ignore it. I.e technical discussions should be robust but once 
they become personal just ignore that input and move on. Isn't 
always possible I know but in this case I reckon you can.


Convincing Walter, Andrei and the rest of the core dev team of 
course will require a DIP.


Keep going on this, it is the first hint of movement with shared 
since like foreva!


bye,
norm

bye,
Norm


Re: shared - i need it to be useful

2018-10-18 Thread Stanislav Blinov via Digitalmars-d

On Friday, 19 October 2018 at 02:20:22 UTC, Manu wrote:

I've given use cases constantly, about taking object ownership, 
promotions, and distribution for periods (think parallel for),


Manu, you haven't shown *any* code in which conversion from 
mutable to shared, an *implicit* one at that, was even present, 
let alone useful. While at the same time blatantly dismissing 
*all* examples to the contrary as "bad code" or any other reason, 
especially the "you just don't understand". How do you expect us 
to understand if all you do is talk about how it's useful, but 
don't explain why?
You can talk and brag all you want, but until you do show at 
least one example, I'm sorry but you have no case. And what's 
this all about? A *small*, localized portion of your proposal, 
that isn't event the source of the problems you're talking about 
in the current state of affairs.
In all examples flying about in this thread, all of *required* 
casts were *the opposite*, from shared to mutable, and you 
yourself acknowledge that those are indeed required on the lowest 
level.
As I've said numerous times, from all that's been said and 
implied in this thread, the *only* use of implicit casting that 
comes to mind is avoiding writing some forwarding methods, that's 
about it. I honestly can't see how this is valuable to warrant 
such a drastic change in the language. Now maybe it's that I'm 
dumb, then just say so already and we'll all move on. Just don't 
act like you're the only one who knows it all and everyone else 
is an unwashed pleb clinging to worshiping the faces in the trees.


I can achieve all my goals with full @safety, absolutely no 
casts in user code, and I have infrastructure in production 
that applies these patterns successfully. It's worth pursuing.


Okay... Then please do show *one* example of useful implicit 
conversion from mutable to shared.


I've spent years thinking on this, I'm trying to move the 
needle on this issue for the first time in over a decade at 
least,


And you're behaving in this thread as if everyone else, myself 
included, were sitting on our thumbs counting flies for this same 
decade.


and you have violently opposed, in principle, from the very 
first post, and make no effort to actually understand the 
proposition.


I haven't done such a thing. I have asked you, numerous times, 
one, and only one question, and you never so much as replied to 
*that question*. What is the big value of this implicit 
conversion that it would warrant changing current language rules 
regarding type conversions?


It's clearly a contest from your insurance that my proposal in 
worthless in every single post you've made. You want me to 
admit defeat and desist.


And here you are, continuing to presume. Now you suddenly know 
what I want. Marvelous.


I *don't* want you to admit any defeat, *or* desist. I *want* you 
to succeed. I *want* to help make `shared` useful so that I and 
everyone else can actually start writing code with it, not in 
spite of it. I've agreed with you on pretty much everything in 
your proposal, *except one thing*. I want you to demonstrate the 
practical value of *that thing*, and it's benefits over the 
current state of affairs, and I asked you several times to 
explain to us mere unwashed mortals exactly how it's useful. What 
have we been doing for 17 pages? Discussing the benefits of 
disabling reads/writes on shared? No. Estimating how much 
existing code could go to trash, what parts of DRuntime/Phobos 
would need a rewrite? No. We were in constant back-and-forth of 
"But... nope... but... nope" about this implicit conversion, 
which you value so much yet for some reason fail to defend. 
Saying "I had good time with it" is not a very practical defense, 
not for a language construct anyway.


Fuck you. You win. I don't have the time or energy to argue 
against a wall.


If you ask to destroy, be prepared for a fight. Or don't ask. 
Just stop appealing to your own authority.


You are obscene, you're complete unproductive, and destructive 
from no apparent reason.


Give it all you've got, please. Let it all out all at once.

I hope you continue to love shared, just the way it is... 
useless.


Yet another presumption. Good on you.


  1   2   3   >