Jump to page: 1 2
Thread overview
shared: Has anyone used it without a lot of pain?
Apr 04, 2017
Atila Neves
Apr 04, 2017
Jonathan M Davis
Apr 05, 2017
Dukc
Apr 05, 2017
Andrea Fontana
Apr 05, 2017
Guillaume Piolat
Apr 06, 2017
Kagamin
Apr 06, 2017
Suliman
Apr 06, 2017
Shachar Shemesh
Apr 06, 2017
deadalnix
Apr 06, 2017
Guillaume Piolat
April 04, 2017
I feel dirty if I write `__gshared`. I sneeze when I read it. But everytime I try and use `shared` I get trouble for it.

TIL that if I want a struct to be both `shared` and not, destructors are out of the way. Because while constructors are easy because we can have more than one:

struct Foo {
    this(this T)() { }
}

auto f1 = const Foo();
auto f2 = shared Foo();

There can be only one destructor:


struct Bar {
    this(this T)() { }
    ~this() {} // no shared, if there was the problem would reverse
    // ~this(this T)() {} isn't a thing
}

auto b1 = const Bar();
// Error: non-shared method foo.Bar.~this is not callable using a shared object
// auto b2 = shared Bar(); //oops

The reason why what I was trying to do isn't possible is obvious in hindsight, but it's still annoying. So either code duplication or mixins, huh?

Atila


April 04, 2017
On Tuesday, April 04, 2017 21:56:37 Atila Neves via Digitalmars-d wrote:
> I feel dirty if I write `__gshared`. I sneeze when I read it.

Well, it was designed with C global variables in mind, and it's pretty risky to use it for anything else, though you can get away with it if you're careful. However, I'm inclined to argue that __gshared really shouldn't ever be used on anything that's extern(D). The problem of course is that shared is a bit of a pain to use. Some of those pain points make sense even if they're annoying and some of them should probably be fixed.

> But everytime I try and use `shared` I get trouble for it.

Yeah. It works, but it is annoying to use.

> The reason why what I was trying to do isn't possible is obvious in hindsight, but it's still annoying. So either code duplication or mixins, huh?

Unfortunately, the idea seems to be that any user-defined objects that are marked as shared should have been designed to be shared and that a type would normally either always be shared or never shared. On some level, that makes sense, but it can also be really annoying, because sometimes, it really does make sense to use the same type in both shared and un-shared contexts. But the reality of the matter is that anything that isn't really basic is going to tend to have to either always be shared or never shared. The destructor case does seem like it should be fixed though.

- Jonathan M Davis

April 05, 2017
On Tuesday, 4 April 2017 at 21:56:37 UTC, Atila Neves wrote:
> The reason why what I was trying to do isn't possible is obvious in hindsight, but it's still annoying. So either code duplication or mixins, huh?

template flag parameter for the struct for being shared or not? I may well be missing something. But in hindsight it could work.

Of course, you would still have to type the shared flag explicitly when instantiating which is less than desirable.
April 05, 2017
On Tuesday, 4 April 2017 at 21:56:37 UTC, Atila Neves wrote:
> I feel dirty if I write `__gshared`. I sneeze when I read it. But everytime I try and use `shared` I get trouble for it.
> [...]
> Atila

https://p0nce.github.io/d-idioms/#The-truth-about-shared
April 05, 2017
On Wednesday, 5 April 2017 at 13:18:26 UTC, Andrea Fontana wrote:
> On Tuesday, 4 April 2017 at 21:56:37 UTC, Atila Neves wrote:
>> I feel dirty if I write `__gshared`. I sneeze when I read it. But everytime I try and use `shared` I get trouble for it.
>> [...]
>> Atila
>
> https://p0nce.github.io/d-idioms/#The-truth-about-shared

Do we have a missed opportunity with shared?

My pet peeve with shared is the RoI.
You were supposed to use it whenever something is "shared across threads". My problem with it is with the RoI of putting entire object graphs under that type constructor.

For now it seems shared!T is about being a monadic hatch for the adventurous, one you can bring your own meaning for.
April 05, 2017
On Tuesday, 4 April 2017 at 21:56:37 UTC, Atila Neves wrote:
> I feel dirty if I write `__gshared`. I sneeze when I read it. But everytime I try and use `shared` I get trouble for it.
>
> TIL that if I want a struct to be both `shared` and not, destructors are out of the way. Because while constructors are easy because we can have more than one:
>
> struct Foo {
>     this(this T)() { }
> }
>
> auto f1 = const Foo();
> auto f2 = shared Foo();
>
> There can be only one destructor:
>
>
> struct Bar {
>     this(this T)() { }
>     ~this() {} // no shared, if there was the problem would reverse
>     // ~this(this T)() {} isn't a thing
> }
>
> auto b1 = const Bar();
> // Error: non-shared method foo.Bar.~this is not callable using a shared object
> // auto b2 = shared Bar(); //oops
>
> The reason why what I was trying to do isn't possible is obvious in hindsight, but it's still annoying. So either code duplication or mixins, huh?
>
> Atila

The error message pretty much tells you that multiple threads should not be allowed to call the destructor concurrently, i.e. you should somehow guarantee that by the end of the scope only one thread has access to the object, which is what should happen in most multi-threaded programs.

A workable, but non the less dirty way of sharing RAII objects would be something along the lines:

struct Widget
{
    this()  { /* ... */ }

    void doWork() scope shared { /* ... */ }

    ~this() { /* ... */ }
}

Owner thread A
{
  /* 0) Make a new widget w/ automatic
     storage (RAII). Note: calling
     non-shared constructor since
     construction is a one thread endeavor
     anyway and we need non-shared `this`
     to call the destructor.             */
  auto w = Widget();

  /* 1) share `w` with other threads
     and perform some useful work...     */

  // Other thread B
  (ref scope shared(Widget) w) @safe
  {
    ssw.doWork();
  }

  /* 2) Ensure that A is now the only
     thread with reference to `w`.       */

  /* 3) w.~this() called automatically.
     Safe, since thread A is only
     one with reference to w.            */
}

2) can be achieved only if you pass `scope` references to `w` in 1), so that non of the other threads would be able to store a pointer to `w` in a variable with longer lifetime.
You also need to have a way of ensuring that the other threads have shorter lifetime than `w` (i.e. simple fork-join parallelism), or you need some sort of task infrastructure that allows you to block thread A until thread B finishes working on the task created in 1) and ensuring no references have escaped in thread B.

The other approach is to not use RAII at all, but instead to use an Arc (atomic reference counting) wrapper that @trusted-ly knows to cast the 'shared' qualifier off Widget when the ref count drops to zero in order to call the destructor.

// create a non-shared Widget and transfer
// the ownership to the Arc wrapper which
// makes the object `shared` with the world.
auto arcW = Arc!(shared W)(new W());

// auto w = new W();
// auto arcFromNonUniqueW = Arc!(shared W)(w); <- doesn't compile

void use1(ref shared(W) w) @safe;
// arcW.get().use1(); // Doesn't compile:
// Error: reference to local variable arcW assigned to non-scope parameter w calling arc_test.use1

void use2(ref scope shared(W) w) @safe;
arcW.get().use2(); // OK
April 06, 2017
On Wednesday, 5 April 2017 at 14:01:24 UTC, Guillaume Piolat wrote:
> My pet peeve with shared is the RoI.

Risk of infection?

> You were supposed to use it whenever something is "shared across threads". My problem with it is with the RoI of putting entire object graphs under that type constructor.

If you don't want to share data between threads and have an option to do so, sure do it; shared is for the case when you want to share data.
April 06, 2017
On Thursday, 6 April 2017 at 09:00:33 UTC, Kagamin wrote:
> On Wednesday, 5 April 2017 at 14:01:24 UTC, Guillaume Piolat wrote:
>> My pet peeve with shared is the RoI.
> Risk of infection?

Return of Investment :)

April 06, 2017
On 06/04/17 12:37, Suliman wrote:
> On Thursday, 6 April 2017 at 09:00:33 UTC, Kagamin wrote:
>> On Wednesday, 5 April 2017 at 14:01:24 UTC, Guillaume Piolat wrote:
>>> My pet peeve with shared is the RoI.
>> Risk of infection?
>
> Return of Investment :)
>

Actually, to be pedantic, it's "return *on* investment".

Shachar
April 06, 2017
On Wednesday, 5 April 2017 at 14:01:24 UTC, Guillaume Piolat wrote:
> Do we have a missed opportunity with shared?
>

Yes we do.

The #1 problem is that it lack a bridge to and from the "normal" thread local world. there is literally no way to use shared in a correct way, you always need to bypass part of the language ATM.

The 3 main way data are shared go as follow :
1/ Producer/consumer.
Thread 1 create some object, send it to thread 2 for processing. This is common in server applications for instance, where a thread will accept request and then dispatch it to worker threads.

Right now, there are no way to transfers ownership from one thread to another, so you pretty much got to cast to shared, move data to the other thread and then cast back to not shared. (People who followed closely will notice that this is somewhat entangled with other problem D has such as nogc exception, see http://forum.dlang.org/post/ikzzvbtvwhqweqlzxytz@forum.dlang.org ).

2/ Actually shared objects with temporal ownership via a mutex.

Once again, this is about ownership. One can get a temporary ownership of some shared object which one can manipulate as if it was thread local for the duration a mutex is held.

The current way to do this is to take the mutex and cast away shared. This is bad as it breaks all type system guarantees.

To be done safely, this is needs to specify what is actually owned by the object that is protected by the mutex and what isn't. A phobos facility could, granted ownership was known, take an object, take the mutex and then allow to access it in a "scope" manner based on the lifetime of the mutex's lock. the accessed object can even be const in case of RWlock and it works beautifully because const is transitive.

3/ Actually shared object providing method which use atomics and alike to be thread safe.

This use case is actually decently served by shared today, except the construction of the object in the first place.

« First   ‹ Prev
1 2