synchronized (this[.classinfo]) in druntime and phobos

Steven Schveighoffer schveiguy at yahoo.com
Thu May 31 04:24:55 PDT 2012


On Wed, 30 May 2012 17:45:32 -0400, Andrei Alexandrescu  
<SeeWebsiteForEmail at erdani.org> wrote:

> On 5/30/12 1:31 PM, Steven Schveighoffer wrote:
>> On Wed, 30 May 2012 15:48:47 -0400, Andrei Alexandrescu
>> <SeeWebsiteForEmail at erdani.org> wrote:
>>> synchronized (object) {
>>> writeln("about to unlock the object");
>>> XXX
>>> writeln("unlocked the object");
>>> }
>>>
>>> Replace "XXX" with a construct that unlocks the object.
>>
>> This is not what we are talking about.
>
> Oh yes it is. "Expose the mutex" means "make the two mutex primitive  
> operations lock() and unlock() freely usable against the mutex object".

Stop that!  You are blatantly arguing minutia when you should be trying to  
understand what we are saying!  This is like dealing with a stubborn child.

>> I guess the easiest way to say it is, the API used to "lock and then
>> subsequently unlock" the mutex. That is, even this:
>>
>> Object o = new Object;
>>
>> synchronized(o)
>> {
>> }
>>
>> is exposing the mutex in such a way that you can interfere with the
>> semantic meaning of the mutex, and cause preventable deadlocks.
>>
>> It would be preferrable for:
>>
>> synchronized(o)
>> {
>> }
>>
>> to be an error, unless typeof(o) allows it explicitly.
>
> Yah, it should be only allowed for synchronized classes.

Not good enough.  Whether I want all accesses to methods and members to be  
synchronized should be orthogonal to whether I want to allow arbitrary  
scoped locking and unlocking of my object.

>
>> I gave you a case where you can have a deadlock, even with simple fully
>> synchronized classes. You might say, "yeah, but all mutexes can have
>> deadlocks!".
>
> Not only I might say that, I'm actually gonna say it. Yeah, but all  
> mutexes can have deadlocks!

Right, if used incorrectly.  In my example, you cannot have a deadlock  
unless the ability to scope-lock the object is exposed to the public.

>> My contention is that if the default is you do *not* expose the mutex to
>> external callers, there *cannot* be a deadlock.
>
> That's shuffling the fundamental issue from client code to library code.  
> This can be done in either approach (synchronized vs. raw mutex), except  
> it's clunkier with raw mutex.

Not talking about raw mutexes, my code is based on synchronized classes.   
Period.

>> Here is the example again:
>>
>> synchronized class A
>> {
>> private int _foo;
>> @property int foo() { return _foo;}
>> }
>>
>> synchronized class B
>> {
>> private A _a;
>> @property A a() { return _a;}
>> void fun() {}
>> }
>>
>> void thread(B b)
>> {
>> synchronized(b.a)
>> {
>> b.fun();
>> }
>> }
>>
>> If synchronized(b.a) is valid, deadlock can occur. If it's invalid,
>> deadlock *cannot* occur.
>
> I don't get what this is supposed to prove.

If you cannot synchronize on b.a, as an *external* entity, then you cannot  
deadlock.  This is an important property.  The author of A and B can say  
"deadlock-proof".  How do you not see this as valuable?

Right now, we have a choice:

a. use synchronized -> prone to deadlocks that you cannot control or  
prevent.
b. roll your own locking -> possible to prevent deadlocks in all cases,  
but prone to error since you could accidentally miss an unlock or lock.

I want:

c. use synchronized with *no* public access -> possible to prevent  
deadlocks in all cases, safe to use without forgetting to unlock/lock.

-Steve


More information about the Digitalmars-d mailing list