digitalmars.D - Shared Classes
- S S.com (14/14) Nov 09 2013 It seems to me that the way things are currently implemented that a
- evilrat (33/48) Nov 09 2013 actually you also need shared methods/memebers, shared works
- Shammah Chancellor (23/49) Nov 09 2013 I understand how it works right now. My point is that it's
- Jonathan M Davis (47/61) Nov 09 2013 It's by design. The argument is that any type which is used across threa...
It seems to me that the way things are currently implemented that a class itself has to be specifically made to handle being shared. That is to say, I cannot import some general library and do (new shared LibraryType()) if the class doesn't support all the proper shared methods. In order for the class to properly implement the shared methods, it basically needs to be defined as such: shared class Foo { .... } But now I still need to do shared Foo everywhere I use that class. This seems a bit off to me. R/ Shammah
Nov 09 2013
On Saturday, 9 November 2013 at 16:55:18 UTC, S S.com wrote:It seems to me that the way things are currently implemented that a class itself has to be specifically made to handle being shared. That is to say, I cannot import some general library and do (new shared LibraryType()) if the class doesn't support all the proper shared methods. In order for the class to properly implement the shared methods, it basically needs to be defined as such: shared class Foo { .... } But now I still need to do shared Foo everywhere I use that class. This seems a bit off to me. R/ Shammahactually you also need shared methods/memebers, shared works similar to const, so for example look at this: ------------------------------------------ import std.concurrency; import std.stdio; import core.thread; // notice no shared at class class A { shared void AddX() { x++; } shared int x; } // accepts shared instances only, so in this way you can't accidentally modify non-shared instances void worker(shared A inst) { inst.AddX(); writeln(inst.x); } void main() { shared A a = new shared A(); A b = new A(); // worker(b); // <-- fail! spawn(&worker, a); thread_joinAll(); } ----------------------------------------- so it is just storage specifier like const or immutable. and... ugh, sorry i'm too crappy on teaching people, but i hope you find this example somewhat helpful and someone else could give you more info on this.
Nov 09 2013
On 2013-11-09 17:12:46 +0000, evilrat said:On Saturday, 9 November 2013 at 16:55:18 UTC, S S.com wrote:I understand how it works right now. My point is that it's dysfunctional. It's nearly impossible to make a class which can be declared as "shared" but also not shared. It's an all-or-nothing type proposition right now the way that implicit casting of shared works. Aside from that, if your class has any EXTERNAL dependencies, everything breaks: class Foo { Tid childFiber; .... <-- None of STD.concurrencies } auto x = new shared Foo(); <-- boom, none of std.concurrency takes shared(Tid). int[shared Foo] FooSet; <--- boom toHash not implemented for shared(Foo); class Foo { DataType initializationData; this( DataType input ) shared { initializationData = input; //Does not work as soon as Foo is declared is shared. } this(shared DataType input ) shared { initializationData = input; //Okay, but does not work if new is declared as as normally, without the shared keyword. } }It seems to me that the way things are currently implemented that a class itself has to be specifically made to handle being shared. That is to say, I cannot import some general library and do (new shared LibraryType()) if the class doesn't support all the proper shared methods. In order for the class to properly implement the shared methods, it basically needs to be defined as such: shared class Foo { .... } But now I still need to do shared Foo everywhere I use that class. This seems a bit off to me. R/ Shammahactually you also need shared methods/memebers, shared works similar to const, so for example look at this: so it is just storage specifier like const or immutable. and... ugh, sorry i'm too crappy on teaching people, but i hope you find this example somewhat helpful and someone else could give you more info on this.
Nov 09 2013
On Saturday, November 09, 2013 11:55:19 S S.com wrote:It seems to me that the way things are currently implemented that a class itself has to be specifically made to handle being shared. That is to say, I cannot import some general library and do (new shared LibraryType()) if the class doesn't support all the proper shared methods. In order for the class to properly implement the shared methods, it basically needs to be defined as such: shared class Foo { .... } But now I still need to do shared Foo everywhere I use that class. This seems a bit off to me.It's by design. The argument is that any type which is used across threads should be designed for it. I think that that's true to a point but that it's also frequently the case that it's desirable to use something as shared which is also normally used as thread-local, so on some level, I agree with you. It's something that Andrei is quite adamament about though. However, even if you don't believe that types which are supposed to be used as shared are fundamentally supposed to be designed to be used as shared and not in TLS, in order for shared to do its job, a variable needs to be typed as shared wherever it's used. Without that, the compiler can't make any guarantees about shared or TLS variables. It needs to be able to keep them separate - which it can't do if something isn't marked as shared everywhere that it's used as shared (including the this reference of member functions). So, even if you don't agree that types which are used as shared should be specially designed for it, you run into the problem that the type system pretty much requires marking member functions as shared in order to guarantee that shared objects are treated as shared. In theory, synchronized classes are supposed to strip the top level shared of their member variables inside their member functions, allowing you to use them as if they were TLS (since the type system then guarantees that they're only being used on that thread) and avoiding the need to make their member functions shared. However, synchronized classes have not yet been implemented (only synchronized functions), and that doesn't help with synchronized blocks or mutexes, and it doesn't help when you need more than just the top level of shared to be stripped off. So, I expect that in many cases, the way to use shared is going to be to protect it (with a mutex or synchronized block or whatever), cast away shared to operate on the object, make sure that no TLS references to it exist, and then unlock the mutex (or synchronized block or whatever). e.g. synchronized(myMutex) { auto n = cast(T)mySharedT; //do stuff to n like n.foo() or bar(n) //nothing should have a reference to n at this point } However some folks (Andrei in particular) don't like that paradigm, because it involves casting and is arguably bug-prone (certainly, you're losing any help from the type system in making sure that you don't end up with any non-shared references to shared data or accidentally end up using a shared object without having protected it with a lock first). I don't see any way around it though, not when even synchronized classes wouldn't be enough in many cases (because they only strip one layer of shared), and I think that it would often be overkill to create a synchronized class just to strip away shared on something (though it would have the advantage of giving you somewhere to put the mutex). But with synchronized classes not yet fully implemented, it's not like there's much choice at the moment. - Jonathan M Davis
Nov 09 2013