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