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