Proposals: Synchronization

Regan Heath regan at netwin.co.nz
Tue Jul 25 06:20:42 PDT 2006


On Tue, 25 Jul 2006 21:59:36 +0930, Kent Boogaart  
<kentcb at internode.on.net> wrote:
> I feel the need for a more concrete example. Suppose you write this class
> (pseudo code):
>
> public class MyClass
> {
>     //data protected by lock(this)
>     private object _data1;
>     private object _data2;
>     //data protected by lock(_list);
>     private object _data3;
>     private object _data4;
>     private List _list;
>
>     ...
>
>     public void someMethod()
>     {
>         lock (this)
>         lock (_list)
>         {
>             //do something with data1 through data4
>             _list.someListMethod();
>         }
>     }
>
>     public void someOtherMethod()
>     {
>         _list.someListMethod();
>     }
> }
>
> Seems pretty innocent, right? Now suppose you didn't write the List  
> class.
> Someone else did or maybe it's part of Phobos or the .NET FCL or the Java
> class libraries. And suppose it looks a bit like this:
>
> public class List
> {
>     private object _owner;
>
>     public List(object owner)
>     {
>         _owner = owner;
>     }
>
>     public void someListMethod()
>     {
>         lock (this)
>         lock (_owner)
>         {
>             //do something
>         }
>     }
> }
>
> See the problem? Take 2 threads: A and B. Thread A enters
> MyClass.someMethod() and takes a lock on this (ie. the instance of  
> MyClass).
> Then it is preempted and thread B enters someOtherMethod(). It goes into
> List.someListMethod() and takes a lock on this (ie. the instance of  
> List).
> It then attempts to lock on _owner (ie. the instance of MyClass). It  
> can't
> because thread A has that lock. Thread A can't continue because thread B  
> has
> the lock on the list instance. Bang - deadlock.

But not if you use my idea! ;)

If you remove object locking and re-write the code above, you'll still get  
a deadlock. Why? Because 'owner' or whatever you can actually lock will  
still be shared and will still be locked 2nd in one place and 1st in  
another and that is what causes the deadlock.

> This example may seem a little contrived but this kind of thing has  
> happened
> and continues to happen in the real world. Why?

Because people don't understand multithreading (because it's hard) and  
because people write bad code.

> Because any object instance
> is lockable and *developers have little to no way of knowing what locks  
> are being taken by the code they're calling and in what order*.Because  
> any object can be locked, the following is true:
> 1. Developers will often just lock on this (or the TypeInfo when in  
> static scope) because it is "easier" and "cleaner". This ensures the  
> problems
> discussed above can occur.

I disagree that it "ensures" the problem can occur. I believe the *order*  
in which the locks are taken cause the problem, not what can or cannot be  
locked.

One thing removing object locking will do, IMO, is decrease the number of  
things you have to consider _may_ be locked and causing a particular  
deadlock which you're hunting for. In other words it will also make it  
more obvious where locking is or can occur.

> If developers had to explicitly create an object
> to lock on, they will likely think harder about whether they expose that
> object for others to lock.

I agree that having to create an object and then explicitly share it will  
make it more obvious where a deadlock could occur (but only to half  
competant programmer). It wont prevent one from happing however because as  
soon as the "object for others to lock" is shared you introduce the  
possibility that the same locks will be taken in a different order in 2  
different peices of code.

> Most (all?) of the time they will realize that
> there is no point doing that.

Why? Either they need to lock it, or they don't. If they do, they have to  
share it, if they share it .. a deadlock can occur.

> 2. Malicious code can deliberately lock objects so that the host is
> deadlocked if it also attempts to lock on those objects (for example,
> consider a .NET assembly loaded into SQL Server 2005's CLR or a Java  
> class loaded into Eclipse)

There is no cure for stupidity, or malice.. except perhaps a bigger gun. ;)

> 3. As I already pointed out, every object carries around at least 4 extra
> bytes on the heap - "just in case" it is ever locked. To me that is
> enormously wasteful because most objects are never locked and I never  
> lock on public objects for the reasons above. I'd much rather be in  
> direct
> control of the memory being used to support locking.

I dislike waste too but you don't get something for nothing. That said, if  
we did remove those 4 bytes perhaps we could use template bolt-ons to make  
existing classes lockable, something like..

template Lockable(Base) : Base {
   ..code to lock/unlock etc..
}

auto f = new Lockable!(BufferedFile)();
..etc..

this solution would also make it more obvious things were being locked.

> Sorry to bang on about this. What can I say? I'm passionate.

Nothing wrong with that.

> I daresay that if you asked the designers of the .NET and Java threading  
> support they will express regret at allowing locks on arbitrary objects.  
> Heck, I'd be happy to do that for you if you like . . . ?

Sure, link me to their reply :)

In fact, link me to any discussions in .NET or Java on this topic that you  
know about. I'm interested in this also.

Regan



More information about the Digitalmars-d mailing list