Thread overview | |||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
February 08, 2014 is(Mutex == shared) == false? | ||||
---|---|---|---|---|
| ||||
Ditto for other core.sync primitives. This has been haunting me for a while now. Currently all those guys are not qualified shared at all. What that means is that we cannot use them in any shared classes: shared class C { Mutex m; this() { m = new Mutex; // error, cannot implicitly convert Mutex to shared(Mutex) m = new shared Mutex; // error, Mutex.this is not callable using a shared object } } Same goes for shared methods too. So the only possible ways to use Mutex et al. are to either declare them __gshared, which implies static, or apply casts. So we can't have per-instance mutexes, condition variables, etc, unless using casts everywhere. As far as I can see this state is the same between dmd, ldc and gdc. So, question number 1: Is this at all intentional or just inherent and no one got around to adding support for shared? I can't at all see why would they be non-shared. Because if that is to change, it'd better be sooner than later, right? With mutexes the roots run as deep as object_.{d,di} where the Monitor interface is declared (having both lock and unlock methods non-shared). I've been able to coerce my local pull of druntime to define Mutex et al. as shared by default. Aside from sorcery with casts inside e.g. src/gc/gc.d and combating segfaults in GC and Thread initialization, it involved: - qualifying Monitor's methods as shared - renaming existing classes (e.g. class Mutex -> shared class Mutex_) - providing default-shared aliases (e.g. alias Mutex = shared(Mutex)) The renaming and alias are needed to (a) not break existing code and (b) because in my understanding they should be shared anyway :) Surprisingly, this didn't take all that long, though I suspect there are some underwater rocks still remaining. But at least both druntime and Phobos pass their unittests. Thus, question number 2: Am I going in the right direction, or is there something already planned regarding this? The complete set of changes would be rather large, as not only it spans currently supported OSs, but I imagine also would concern both druntime and Phobos (e.g. std.concurrency, std.parallelism). I am primarily on Linux, but given time I can also edit the relevant files for Windows too. However, I don't have access to any other OSs, so I still won't be able to create a complete pull request. Hence, question number 3: Provided you've read to this point, and question number 2 yields positive answer, is there anybody willing to collaborate on this? E.g. complete/test the changes on Windows, OSX, etc? I understand that the community is currently battling in the fields of GC vs ARC vs manual memory management, but I still hope to hear your feedback :) |
February 08, 2014 Re: is(Mutex == shared) == false? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Stanislav Blinov | > - providing default-shared aliases (e.g. alias Mutex = shared(Mutex))
This should've been 'alias Mutex = shared(Mutex_)'.
|
February 08, 2014 Re: is(Mutex == shared) == false? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Stanislav Blinov | On Saturday, 8 February 2014 at 03:11:17 UTC, Stanislav Blinov wrote:
> Ditto for other core.sync primitives.
>
> This has been haunting me for a while now. Currently all those guys are not qualified shared at all. What that means is that we cannot use them in any shared classes:
>
> shared class C
> {
> Mutex m;
>
> this()
> {
> m = new Mutex; // error, cannot implicitly convert Mutex to shared(Mutex)
> m = new shared Mutex; // error, Mutex.this is not callable using a shared object
> }
> }
>
> Same goes for shared methods too. So the only possible ways to use Mutex et al. are to either declare them __gshared, which implies static, or apply casts. So we can't have per-instance mutexes, condition variables, etc, unless using casts everywhere.
>
> As far as I can see this state is the same between dmd, ldc and gdc.
>
> So, question number 1:
>
> Is this at all intentional or just inherent and no one got around to adding support for shared? I can't at all see why would they be non-shared.
>
> Because if that is to change, it'd better be sooner than later, right?
>
> With mutexes the roots run as deep as object_.{d,di} where the Monitor interface is declared (having both lock and unlock methods non-shared).
>
> I've been able to coerce my local pull of druntime to define Mutex et al. as shared by default. Aside from sorcery with casts inside e.g. src/gc/gc.d and combating segfaults in GC and Thread initialization, it involved:
>
> - qualifying Monitor's methods as shared
> - renaming existing classes (e.g. class Mutex -> shared class Mutex_)
> - providing default-shared aliases (e.g. alias Mutex = shared(Mutex))
>
> The renaming and alias are needed to (a) not break existing code and (b) because in my understanding they should be shared anyway :)
>
> Surprisingly, this didn't take all that long, though I suspect there are some underwater rocks still remaining. But at least both druntime and Phobos pass their unittests.
>
> Thus, question number 2:
>
> Am I going in the right direction, or is there something already planned regarding this?
>
> The complete set of changes would be rather large, as not only it spans currently supported OSs, but I imagine also would concern both druntime and Phobos (e.g. std.concurrency, std.parallelism).
>
> I am primarily on Linux, but given time I can also edit the relevant files for Windows too. However, I don't have access to any other OSs, so I still won't be able to create a complete pull request.
>
> Hence, question number 3:
>
> Provided you've read to this point, and question number 2 yields positive answer, is there anybody willing to collaborate on this? E.g. complete/test the changes on Windows, OSX, etc?
>
> I understand that the community is currently battling in the fields of GC vs ARC vs manual memory management, but I still hope to hear your feedback :)
When I first time saw D language, I called it as my dream language. Well, every nice thing has its problems. I hated that "shared" keyword since first day. It ruins my codes whenever I need to write multiple thread programs.
|
February 08, 2014 Re: is(Mutex == shared) == false? | ||||
---|---|---|---|---|
| ||||
Posted in reply to tcak | I tried making Mutex shared once, and ended up down a rabbit hole of needing to make various Posix and Windows types shared, which in turn meant changing function signatures... I reverted the change and decided to revisit it later... Which never happened. I suppose it's time to revisit this and see how it goes. |
February 08, 2014 Re: is(Mutex == shared) == false? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | On Saturday, 8 February 2014 at 06:48:50 UTC, Sean Kelly wrote: > I tried making Mutex shared once, and ended up down a rabbit hole of needing to make various Posix and Windows types shared, which in turn meant changing function signatures... I think most of those can be resolved with casts (or methods that return unshared-casted pointers)? > I reverted the change and decided to revisit it later... Which never happened. I suppose it's time to revisit this and see how it goes. I've created a branch here: https://github.com/radcapricorn/druntime/tree/shared_sync_primitives There are currently changes to make core.sync primitives shared on Posix. |
February 08, 2014 Re: is(Mutex == shared) == false? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Stanislav Blinov | Also, on a related note, is there any benefit in having core.sync primitives not final? What would be a use case to inherit from e.g. Mutex or Condition? GC does that with Mutex, but only to devirtualize all methods :) Maybe we should also consider making them final along the way? |
February 08, 2014 Re: is(Mutex == shared) == false? | ||||
---|---|---|---|---|
| ||||
Posted in reply to tcak | On 2/7/14, 10:21 PM, tcak wrote:
> When I first time saw D language, I called it as my dream language.
> Well, every nice thing has its problems. I hated that "shared" keyword
> since first day. It ruins my codes whenever I need to write multiple
> thread programs.
We should add finalizing shared to our H1 goals.
Andrei
|
February 11, 2014 Re: is(Mutex == shared) == false? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Here's bugzilla enhancement request: https://d.puremagic.com/issues/show_bug.cgi?id=12132 |
February 11, 2014 Re: is(Mutex == shared) == false? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Stanislav Blinov | On Saturday, 8 February 2014 at 16:46:26 UTC, Stanislav Blinov wrote:
> Also, on a related note, is there any benefit in having core.sync primitives not final? What would be a use case to inherit from e.g. Mutex or Condition? GC does that with Mutex, but only to devirtualize all methods :)
>
> Maybe we should also consider making them final along the way?
Probably. The new std.concurrency patch overrides Condition, but mostly for convenience. I think those methods not being virtual was an oversight on my part.
|
February 11, 2014 Re: is(Mutex == shared) == false? | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sean Kelly | On Tuesday, 11 February 2014 at 05:58:15 UTC, Sean Kelly wrote: > On Saturday, 8 February 2014 at 16:46:26 UTC, Stanislav Blinov >> Maybe we should also consider making [core.sync primitives] final along the way? > > Probably. The new std.concurrency patch overrides Condition, but mostly for convenience. I think those methods not being virtual was an oversight on my part. Great :) Now to another issue, or possible enhancement. It would seem that we may benefit from some sort of relaxed shared ops. For example, Condition on Windows has several int fields, which of course would become shared(int) due to transitivity. Currently, those ints are modified directly using ++, --, += and so on. As per https://d.puremagic.com/issues/show_bug.cgi?id=3672, such code would be illegal. A straightforward hack would be to move such code into non-shared methods and then call those methods by casting away shared on the this reference. A better, but still naive replacement would be to use atomicOp() for all those operations. The problem is that many of those operations happen under a lock, where a strong do {} while (!cas()) loop, to which atomicOp() currently translates, would be unnecessary pessimization. Of course, they could be replaced manually with something like: // atomicOp!"-="(m_numWaitersBlocked, 1); atomicStore!(MemoryOrder.rel)(m_numWaitersBlocked, atomicLoad!(MemoryOrder.acq)(m_numWaitersBlocked) - 1); But that is just tedious to type, hard to read, and still may be less efficient that a straightforward --. Maybe an additional function like this could be helpful: --8<-- HeadUnshared!T localOp(string op,T,V)(T what, V mod) if (is(T == shared)) { ... } -->8-- Or even something like this: --8<-- ref auto assumeLocal(T)(ref T v) if (is(T == shared)) { // Cast via pointer to preserve lvalue return *cast(HeadUnshared!T*)&v; } -->8-- With which we can perform this: --8<-- --assumeLocal(m_numWaitersBlocked); -->8-- What do you think? |
Copyright © 1999-2021 by the D Language Foundation