Jump to page: 1 2 3
Thread overview
`shared`...
Oct 01, 2018
Manu
Oct 01, 2018
Nicholas Wilson
Oct 01, 2018
Manu
Oct 01, 2018
Nicholas Wilson
Oct 01, 2018
Manu
Oct 01, 2018
Nicholas Wilson
Oct 01, 2018
ag0aep6g
Oct 01, 2018
Nicholas Wilson
Oct 01, 2018
ag0aep6g
Oct 01, 2018
Nicholas Wilson
Oct 01, 2018
Jonathan M Davis
Oct 01, 2018
Manu
Oct 01, 2018
Kagamin
Oct 01, 2018
Nicholas Wilson
Oct 01, 2018
RazvanN
Oct 01, 2018
Timon Gehr
Oct 01, 2018
Manu
Oct 01, 2018
Timon Gehr
Oct 02, 2018
Walter Bright
Oct 02, 2018
Manu
Oct 02, 2018
Walter Bright
Oct 02, 2018
Manu
Oct 02, 2018
Walter Bright
Oct 03, 2018
Atila Neves
Oct 04, 2018
Walter Bright
Oct 04, 2018
Kagamin
Oct 01, 2018
deadalnix
Oct 01, 2018
Manu
September 30, 2018
struct Bob
{
  void setThing() shared;
}

As I understand, `shared` attribution intends to guarantee that I dun
synchronisation internally.
This method is declared shared, so if I have shared instances, I can
call it... because it must handle thread-safety internally.

void f(ref shared Bob a, ref Bob b)
{
  a.setThing(); // I have a shared object, can call shared method

  b.setThing(); // ERROR
}

This is the bit of the design that doesn't make sense to me...
The method is shared, which suggests that it must handle
thread-safety. My instance `b` is NOT shared, that is, it is
thread-local.
So, I know that there's not a bunch of threads banging on this
object... but the shared method should still work! A method that
handles thread-safety doesn't suddenly not work when it's only
accessed from a single thread.

I feel like I don't understand the design...
mutable -> shared should work the same as mutable -> const... because
surely that's safe?

I'm not sure what the intended use here is... for every method I write `shared`, I also need a non-shared shim that casts `&this` to shared, and calls through... and that's just pointless and noisy busy-work.

The only reason I should have to overload for mutable and shared is when I want to implement a non-thread-safe overload for performance when interacting with non-shared instances.
October 01, 2018
On Monday, 1 October 2018 at 02:29:40 UTC, Manu wrote:
> struct Bob
> {
>   void setThing() shared;
> }
>
> As I understand, `shared` attribution intends to guarantee that I dun
> synchronisation internally.
> This method is declared shared, so if I have shared instances, I can
> call it... because it must handle thread-safety internally.

seems reasonable

https://github.com/dlang/dmd/pull/8782
September 30, 2018
On Sun, Sep 30, 2018 at 8:20 PM Nicholas Wilson via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> On Monday, 1 October 2018 at 02:29:40 UTC, Manu wrote:
> > struct Bob
> > {
> >   void setThing() shared;
> > }
> >
> > As I understand, `shared` attribution intends to guarantee that
> > I dun
> > synchronisation internally.
> > This method is declared shared, so if I have shared instances,
> > I can
> > call it... because it must handle thread-safety internally.
>
> seems reasonable
>
> https://github.com/dlang/dmd/pull/8782

Haha, sneaky bugger :P
I reckon the patch is gonna be a lot bigger than that though!
October 01, 2018
On Monday, 1 October 2018 at 03:33:16 UTC, Manu wrote:
> On Sun, Sep 30, 2018 at 8:20 PM Nicholas Wilson via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>>
>> On Monday, 1 October 2018 at 02:29:40 UTC, Manu wrote:
>> > struct Bob
>> > {
>> >   void setThing() shared;
>> > }
>> >
>> > As I understand, `shared` attribution intends to guarantee that
>> > I dun
>> > synchronisation internally.
>> > This method is declared shared, so if I have shared instances,
>> > I can
>> > call it... because it must handle thread-safety internally.
>>
>> seems reasonable
>>
>> https://github.com/dlang/dmd/pull/8782
>
> Haha, sneaky bugger :P
> I reckon the patch is gonna be a lot bigger than that though!

Of course, there will be updating the test suite. And Walter will probably tell you to bugzilla this.

implicit conversion of mutable (i.e. no mods) to const share should be absolutely no problem, as that it rusts borrowing model (one owning mutable thread local reference and zero or more non thread local non-owning const references) and is fine.

 mutable to mutable shared I'm not so sure as references to otherwise owned references could escape.

shared Bob* sneaky;

struct Bob
{
  void setSneaky() shared // legit as this is shared
   {
      sneaky = &this;
   }
}
void oblivious(ref shared Bob a, ref Bob b)
{
  a.setSneaky(); // Fine

  b. setSneaky(); // would become not an error, but totally not fine.
}

unfortunately scope is not a modifier so the PR will have to be larger, oh well.


September 30, 2018
On Sun, Sep 30, 2018 at 9:00 PM Nicholas Wilson via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
>
> On Monday, 1 October 2018 at 03:33:16 UTC, Manu wrote:
> > On Sun, Sep 30, 2018 at 8:20 PM Nicholas Wilson via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> >>
> >> On Monday, 1 October 2018 at 02:29:40 UTC, Manu wrote:
> >> > struct Bob
> >> > {
> >> >   void setThing() shared;
> >> > }
> >> >
> >> > As I understand, `shared` attribution intends to guarantee
> >> > that
> >> > I dun
> >> > synchronisation internally.
> >> > This method is declared shared, so if I have shared
> >> > instances,
> >> > I can
> >> > call it... because it must handle thread-safety internally.
> >>
> >> seems reasonable
> >>
> >> https://github.com/dlang/dmd/pull/8782
> >
> > Haha, sneaky bugger :P
> > I reckon the patch is gonna be a lot bigger than that though!
>
> Of course, there will be updating the test suite. And Walter will probably tell you to bugzilla this.
>
> implicit conversion of mutable (i.e. no mods) to const share should be absolutely no problem, as that it rusts borrowing model (one owning mutable thread local reference and zero or more non thread local non-owning const references) and is fine.
>
>   mutable to mutable shared I'm not so sure as references to
> otherwise owned references could escape.
>
> shared Bob* sneaky;
>
> struct Bob
> {
>    void setSneaky() shared // legit as this is shared
>     {
>        sneaky = &this;
>     }
> }
> void oblivious(ref shared Bob a, ref Bob b)
> {
>    a.setSneaky(); // Fine
>
>    b. setSneaky(); // would become not an error, but totally not
> fine.
> }
>
> unfortunately scope is not a modifier so the PR will have to be larger, oh well.

Ah, good point. So, it could only be allowed if scope...

struct Bob
{
  void setThing() shared scope;
}

That's going to require far-reaching proliferation of `scope`.
Do we infer `scope` like the other attributes? The default for `scope`
is totally backwards. :/
October 01, 2018
On Monday, 1 October 2018 at 04:22:24 UTC, Manu wrote:
> Ah, good point. So, it could only be allowed if scope...
>
> struct Bob
> {
>   void setThing() shared scope;
> }
>
> That's going to require far-reaching proliferation of `scope`.
> Do we infer `scope` like the other attributes?

For templates (either the function or the struct it is in) and auto returning functions, I think so: definitely under -dip1000, probably also when not using -dip1000.

>The default for  `scope` is totally backwards. :/

Alas, such is the nature of retrofitting.
October 01, 2018
On 10/01/2018 04:29 AM, Manu wrote:
> struct Bob
> {
>    void setThing() shared;
> }
[...]
> void f(ref shared Bob a, ref Bob b)
> {
>    a.setThing(); // I have a shared object, can call shared method
> 
>    b.setThing(); // ERROR
> }
> 
> This is the bit of the design that doesn't make sense to me...
> The method is shared, which suggests that it must handle
> thread-safety. My instance `b` is NOT shared, that is, it is
> thread-local.
[...]
> I feel like I don't understand the design...
> mutable -> shared should work the same as mutable -> const... because
> surely that's safe?

`shared` isn't analogous to `const`. It's analogous to `immutable`. Functions dealing with `shared` data can assume that other threads also see the data as `shared`. If you allow calling `shared` methods on non-`shared` objects, you're breaking that.

Example:

----
struct Bob
{
  int* p;
  void doThing() shared
  {
    p = &s;
  }
}

shared int s;

void main()
{
  Bob bob;
  (cast(shared Bob)bob).doThing();/* You'd make the cast implicit. */

  import core.thread;
  import core.atomic;
  enum n = 1_000_000;
  auto t = new Thread(() { foreach (i; 0 .. n) atomicOp!"+="(s, 1); });
  t.start();
  foreach (i; 0 .. n) ++*bob.p;
  thread_joinAll();

  import std.stdio;
  writeln(s); /* usually not "2000000", because of race */
}
----
October 01, 2018
On Monday, 1 October 2018 at 06:06:31 UTC, ag0aep6g wrote:
> `shared` isn't analogous to `const`. It's analogous to `immutable`. Functions dealing with `shared` data can assume that other threads also see the data as `shared`. If you allow calling `shared` methods on non-`shared` objects, you're breaking that.
>
> Example:
>
> ----
> struct Bob
> {
>   int* p;
>   void doThing() shared
>   {
>     p = &s;
>   }
> }
>
> shared int s;
>
> void main()
> {
>   Bob bob;
>   (cast(shared Bob)bob).doThing();/* You'd make the cast implicit. */
>
>   import core.thread;
>   import core.atomic;
>   enum n = 1_000_000;
>   auto t = new Thread(() { foreach (i; 0 .. n) atomicOp!"+="(s, 1); });
>   t.start();
>   foreach (i; 0 .. n) ++*bob.p;
>   thread_joinAll();
>
>   import std.stdio;
>   writeln(s); /* usually not "2000000", because of race */
> }
> ----

We've realised that.

In order to be safe, a mutable parameter can be implicitly cast to shared iff the parameter is also scope (that includes the `this` reference`). With an implicit cast in place of the explicit cast under the new rules it would fail to compile because the `this` reference is not scope.
October 01, 2018
On Monday, 1 October 2018 at 02:29:40 UTC, Manu wrote:
> So, I know that there's not a bunch of threads banging on this
> object... but the shared method should still work! A method that
> handles thread-safety doesn't suddenly not work when it's only
> accessed from a single thread.

Shared data may need different algorithms. If unshared data is implicitly convertible to shared, you start to conflate shared data with unshared, so you're back to C-style sharing.

This is how you can do it:

shared struct SharedBob
{
    this(int){}
    void setThing(){}
}
alias shared SharedBob Bob;

void f(ref shared Bob a, ref Bob b)
{
  a.setThing(); // I have a shared object, can call shared method

  b.setThing(); // ok
}

int main()
{
    auto b=Bob(0);
    Bob c;
    f(b,c);
    return 0;
}
October 01, 2018
On 10/01/2018 08:47 AM, Nicholas Wilson wrote:
> In order to be safe, a mutable parameter can be implicitly cast to shared iff the parameter is also scope (that includes the `this` reference`). With an implicit cast in place of the explicit cast under the new rules it would fail to compile because the `this` reference is not scope.

I don't see why it would fail to compile. There's no reason why my `doThing` couldn't be marked as `scope`. It doesn't leak anything.

`pure` would break the example. I'm not sure if it would ensure safety, though. Can a `pure` method spawn a new thread (that outlives the method call)?
« First   ‹ Prev
1 2 3