digitalmars.D - shared: Has anyone used it without a lot of pain?
- Atila Neves (25/25) Apr 04 2017 I feel dirty if I write `__gshared`. I sneeze when I read it. But
- Jonathan M Davis via Digitalmars-d (17/22) Apr 04 2017 Well, it was designed with C global variables in mind, and it's pretty r...
- Dukc (5/8) Apr 05 2017 template flag parameter for the struct for being shared or not? I
- Andrea Fontana (2/6) Apr 05 2017 https://p0nce.github.io/d-idioms/#The-truth-about-shared
- Guillaume Piolat (8/14) Apr 05 2017 Do we have a missed opportunity with shared?
- Kagamin (6/10) Apr 06 2017 Risk of infection?
- Suliman (2/6) Apr 06 2017 Return of Investment :)
- Shachar Shemesh (3/8) Apr 06 2017 Actually, to be pedantic, it's "return *on* investment".
- deadalnix (35/36) Apr 06 2017 Yes we do.
- Guillaume Piolat (6/12) Apr 06 2017 Heretic idea floating in the air: kill shared with a secret
- Petar Kirov [ZombineDev] (61/86) Apr 05 2017 The error message pretty much tells you that multiple threads
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
Apr 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
Apr 04 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.
Apr 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. [...] Atilahttps://p0nce.github.io/d-idioms/#The-truth-about-shared
Apr 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: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.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. [...] Atilahttps://p0nce.github.io/d-idioms/#The-truth-about-shared
Apr 05 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.
Apr 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:Return of Investment :)My pet peeve with shared is the RoI.Risk of infection?
Apr 06 2017
On 06/04/17 12:37, Suliman wrote:On Thursday, 6 April 2017 at 09:00:33 UTC, Kagamin wrote:Actually, to be pedantic, it's "return *on* investment". ShacharOn Wednesday, 5 April 2017 at 14:01:24 UTC, Guillaume Piolat wrote:Return of Investment :)My pet peeve with shared is the RoI.Risk of infection?
Apr 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. 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.
Apr 06 2017
On Thursday, 6 April 2017 at 13:52:01 UTC, deadalnix wrote: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 ).Heretic idea floating in the air: kill shared with a secret ritual, transmute it into "owned", keep the number of type constructor constant :) The paper you linked also solves the "static this" having special rights on immutable data IIRC.
Apr 06 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? AtilaThe 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
Apr 05 2017