Shared Classes
Jonathan M Davis
jmdavisProg at gmx.com
Sat Nov 9 10:55:34 PST 2013
On Saturday, November 09, 2013 11:55:19 S at 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
More information about the Digitalmars-d
mailing list