[dmd-concurrency] synchronized, shared, and regular methods inside the same class

Sean Kelly sean at invisibleduck.org
Mon Jan 4 16:28:06 PST 2010


On Jan 4, 2010, at 4:12 PM, Andrei Alexandrescu wrote:

> Sean Kelly wrote:
>>>> So if I have:
>>>>   class A
>>>>   {
>>>>       void fn() shared { x = 5; }
>>>>       int x;
>>>>   }
>>>> Is this legal?  If the type of the object doesn't change then I'd guess that I won't be allowed to access non-shared fields inside a shared function?
>>> Shared automatically propagates to fields, so typeof((new shared(A)).x) is shared int. Of course that's not the case right now; the typeof expression doesn't even compile :o).
>> Hm... but what if fn() were synchronized instead of shared?  Making x shared in that instance seems wasteful.  I had thought that perhaps a shared function would simply only be allowed to access shared variables, and possibly call synchronized functions:
>>    class A {
>>        void fnA() shared { x = 5; } // ok, x is shared
>>        void fnB() shared { y = 5; } // not ok, y is not shared
>>        void fnC() synchronized { y = 5; } // ok, non-shared ops are ok if synchronized
>>        shared int x;
>>        int y;
>>    }
> 
> Aha! You've just discovered the tail-shared exemption: inside a synchronized method, direct fields can be accessed without barriers (neither implicit or explicit) although technically their type is still shared. Fortunately the compiler has all the information it needs to elide those barriers.

Oh, I see.  I can't decide if it's weird that this optimization means that the sharedRead() and sharedWrite() functions wouldn't be necessary, but I'm leaning towards saying that it's actually a good thing since the changed behavior is obvious.

The only catch with the approach above (and you've mentioned this before) is:

    class A {
        void fnA() shared { x = 5; }
        void fnB() synchronized { x = 6; }
        int x;
    }

I had thought that explicitly labeling variables as shared would sidestep this by requiring ops on the vars to always be atomic.  An alternative (as you've said before) would be to not allow shared and synchronized methods to both be used in a class, but it's pretty common that I'll want to do something like this:

    class A {
        void fnA() synchronized { sharedWrite( flag, true ); }
        void fnB() shared { return sharedRead( flag ); }
        shared flag;
    }

Maybe my explicitly declaring flag as shared somehow provides an exemption?  Or did you have another idea for a way around this?


More information about the dmd-concurrency mailing list