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

Reply via email to