Difference between __gshared and shared.

Jonathan M Davis via Digitalmars-d digitalmars-d at puremagic.com
Wed Jul 8 05:21:20 PDT 2015


On Wednesday, 8 July 2015 at 11:02:19 UTC, Márcio Martins wrote:
> If I remember correctly, it was also a pain to use most of 
> Phobos with 'shared' without casting left and right.

In general, if you're doing much with a shared object other than 
passing it around, you're using shared incorrectly. Various 
operations can be done with core.atomic and shared objects, but a 
number of operations are outright forbidden by the language, and 
to use a non-thread local object correctly (be it __gshared or 
shared), you _have_ to protect it with lock except in cases where 
you know what you're doing and are _extremely_ careful with 
atomic operations. D is effectively trying to force you to not 
try and do operations on a shared object unless it's protected by 
a lock.

Per TDPL, we're supposed to have synchronized classes which then 
strip off the outer layer of shared internally when operating on 
their member variables so that the sharedness can be stripped 
safely without relying on the programmer getting the locks and 
casting right, but synchronized classes have never been 
implemented (just synchronized functions), so that doesn't work 
at the moment, and stripping off just the outer layer of shared 
often isn't enough anyway. So, what you end up having to do is 
something along the lines of

synchronized(mutexObj)
{
     auto unshared = cast(T)sharedT;

     // ...
     // do stuff on unshared, since it's considered thread-local, 
and normal
     // code will work on it.
     //...

     // make sure that there are no references to unshared before 
leaving the
     // synchronized block
}

That way, you can use the shared object with normal code as long 
as you've protected it properly with a mutex, and the language 
prevents you from accidentally operating no the shared object 
through the shared reference (which would then be when it's not 
protected by a lock). The problem is that unlike with 
synchronized classes, you have to explicitly cast away shared 
yourself and ensure that no thread-local references to that data 
escape the synchronized block, and if you screw it up, you could 
have subtle, entertaining bugs, whereas synchronized classes 
would be able to guarantee that the outer layer of shared was 
safely removed.

What we would ideally have would be a way to do the above code 
safely without having to do the cast explicitly and make sure 
that that was done correctly, but we haven't figured out how to 
do that yet. But this idiom ensures that you operate on shared 
objects only when they're protected by a lock. Actually operating 
on shared or __gshared objects without locking is just plain 
buggy unless you're talking about atomic operations and using 
core.atomic properly to deal with that (which isn't necessarily 
easy). So, if anything, the fact that folks find shared so 
annoying implies that they're trying to write code in a manner 
which is not thread-safe. That's not to say that shared doesn't 
need to be improved or that there aren't things that you can't do 
with it right now that you should be able to, but in general, the 
stuff that you can't do with a shared object is stuff that you 
shouldn't be doing with a shared object anyway.

- Jonathan M Davis


More information about the Digitalmars-d mailing list