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

deadalnix deadalnix at gmail.com
Wed May 30 05:43:14 PDT 2012


Le 30/05/2012 14:32, Regan Heath a écrit :
> On Wed, 30 May 2012 10:21:00 +0100, deadalnix <deadalnix at gmail.com> wrote:
>
>> Le 30/05/2012 00:53, Andrei Alexandrescu a écrit :
>>> On 5/29/12 3:01 PM, Alex Rønne Petersen wrote:
>>>> On 29-05-2012 23:54, Andrei Alexandrescu wrote:
>>>>> On 5/29/12 2:49 PM, Alex Rønne Petersen wrote:
>>>>>> On 29-05-2012 23:32, Andrei Alexandrescu wrote:
>>>>>>> On 5/29/12 1:35 AM, deadalnix wrote:
>>>>>>>> Le 29/05/2012 01:38, Alex Rønne Petersen a écrit :
>>>>>>>>> I should probably add that Java learned it long ago, and yet we
>>>>>>>>> adopted
>>>>>>>>> it anyway... blergh.
>>>>>>>>>
>>>>>>>>
>>>>>>>> That is what I was about to say. No point of doing D if it is to
>>>>>>>> repeat
>>>>>>>> previously done errors.
>>>>>>>
>>>>>>> So what is the lesson Java learned, and how does it address
>>>>>>> multithreaded programming in wake of that lesson?
>>>>>>>
>>>>>>> Andrei
>>>>>>
>>>>>> It learned that allowing locking on arbitrary objects makes
>>>>>> controlling
>>>>>> locking (and thus reducing the chance for deadlocks) impossible.
>>>>>
>>>>> And how does Java address multithreading in general, and those
>>>>> issues in
>>>>> particular, today?
>>>>>
>>>>> Andrei
>>>>>
>>>>
>>>> It doesn't, and neither does C#. Java still encourages using
>>>> synchronized, and C# still encourages using lock, but many prominent
>>>> figures in those programming language communities have written blog
>>>> posts on why these language constructs are evil and should be avoided.
>>>
>>> Some citations (beyond the known fallacies of Java 1.0) would be great.
>>> Thanks.
>>>
>>>> Besides, it seems to me that D can't quite make up its mind. We have
>>>> TLS
>>>> by default, and we encourage message-passing (through a library
>>>> mechanism), and then we have the synchronized statement and attribute.
>>>> It just seems so incredibly inconsistent. synchronized encourages doing
>>>> the wrong thing (locks and synchronization).
>>>
>>> Each paradigm has its place. Lock-based programming is definitely here
>>> to stay, and when the paradigm is needed, doing it with synchronized
>>> objects is better than most alternatives.
>>>
>>>
>>> Andrei
>>
>> No !
>>
>> You don't want to synchronize on ANY object. You want to synchronize
>> on explicit mutexes.
>
> +1 .. this is the key point/issue. If all objects can be locked, and if
> synchronized classes/methods use the /same/ mutex you can
> easily/accidentally have hard to find deadlocks. The deadlocks in Q are
> usually (at least) two threads locking (at least) two objects in the
> opposite order and with synchronized classes/methods the fact that a
> lock is being taken is not always obvious from the code (at the call
> site) so the problem code can look fairly innocuous.
>
> **[Example 1]**
>
> class C
> {
> synchronized void ccc() { }
> }
>
> class D
> {
> synchronized void ddd() { }
> }
>
> C c = new C();
> D d = new D();
>
> [thread1]
> ..lock(c) // locks c
> { // deadlock window
> d.ddd(); // locks d
> }
>
> [thread2]
> ..lock(d) // locks d
> { // deadlock window
> c.ccc(); // locks c
> }
>
> **[Example 2]**
>
> class A
> {
> B b;
> ..
> synchronized void foo() // locks a
> { // deadlock window
> b.bar(); // locks b
> }
> }
>
> class B
> {
> A a;
> ..
> synchronized void bar() // locks b
> { // deadlock window
> a.foo(); // locks a
> }
> }
>
> A a = new A();
> B b = new B();
>
> a.b = b;
> b.a = a;
>
> [thread1]
> a.foo(); // locks a then b
>
> [thread2]
> b.bar(); // locks b then a
>
> So, the root cause of the problem is that synchronized classes/methods
> use a publicly visible mutex (the object/this pointer) to perform their
> locking. The solution is to lock on a private mutex, which no-one else
> can lock unexpectedly as described in the link below:
>
>> See that link for instance :
>> http://msdn.microsoft.com/en-us/library/ms173179.aspx
>
> Now, ideally we want to make it hard for people to screw this up in this
> manner and there are several ways to do it.
>
> 1. Prevent locking on any/every object. People would have to create a
> separate mutex object for locking. This is a little tedious, but it does
> make people think about the scope of the locking (where to put the
> mutex, who can see/use it, etc). On the flipside it can make people
> re-use a mutex for multiple conceptual critical section areas of code,
> which is less than ideal, but at least it's not 'broken'.
>

It is suboptimal, but correct.

> 2. Allow locking on any/every object but use a different mutex for
> synchronized class/methods. So 'synchronized' on a method call locks a
> private mutex object, not the object itself. And synchronized(object)
> locks the public mutex object. The downside here is that now every
> object potentially has 2 mutex objects/handles internally - a public and
> a private one.
>

It doesn't address most of the drawback cited. Notably the fact that 
every object have a monitor field, but most of them will not use it.


More information about the Digitalmars-d mailing list