Subj : Re: Deadlock Calculator To : comp.programming.threads From : Uenal Mutlu Date : Sun May 15 2005 02:44 pm "David Schwartz" wrote > > "Uenal Mutlu" wrote > > >> Right, and in case 1, if a thread erroneously locks an object it > >> already > >> holds, that is easily detected. In case 2, that is not detected. > > > Totally different point of view. In case 1 a deadlock will happen, in case > > 2 > > a deadlock will not happen. Which is better/safer? of course case 2, not > > case 1. > > I could not disagree more strongly. A deadlock is far preferable to > continuing erroneous operation. The point is: if you use a recursive locking method then you cannot deadlock yourself. Don't you agree with this? > >> It really is this simple: To write sane multi-threaded code, you must > >> know what locks you hold at the same or lower levels in the locking > >> hierarchy. The only use for recursive locks is to make it possible for > >> code > >> to not deadlock if it doesn't know what locks it holds at the same level > >> in > >> the locking hierarchy. But code has to know this anyway. > > > With recursive locking code does not need to know that. It just locks > > the objects it wants, regardless whether they are already locked by whom > > ever. > > Right, and that's wrong. Wrong, it's right! :-) > >> So all it does it hides bugs and make it possible to write bad code. > > > I totally disagree. Exactly the opposite is true. Recursive locking helps > > avoiding self-deadlocking, it simplifies coding, and it helps to > > encapsulate > > the problem; you can concentrate just on your own job without the need > > to know what the caller (or the caller of the caller etc.) of your routine > > has > > already done or has not done, ie. whether an object is already locked > > and/or > > by whom etc. It's so simple with recursive locking: just lock it yourself > > and proceed. > > But that's just the thing, you must not do this. You cannot manipulate > something sanely without knowing already whether or not you have a lock on > it. I don't think you are beginner in thread programming, but I have the feeling you just do so. Above I said "just lock it yourself and proceed" Of course you ("you" here means _the_current_thread_) cannot proceed if you don't get the lock, ie. if the lock was done in an another thread; you will have to wait (which usually happens inside the Lock() function). You must think of the "current thread" point of view and in your code concentrate yourself (the app logic) only on that fact only, you (the current thread) don't need to, and mostly also can't, know what other threads are doing. > Ask yourself this -- why do threads lock things and when do they unlock > them? You lock things so you can pass through intermediate states that are > not valid for other threads to see, and you unlock things when you have > returned them to a valid state. For example, you lock a linked list while > you add an object to it, because during the add, the linked list passes > through an inconsistent state. > > If the state is consistent, you should release the lock. If the state is > inconsistent, you should not call a function that expects the state to be > consistent. Very true. > There is also the serious problem of the semantic ambiguity of the > 'unlock' function. Does it actually unlock the mutex or not? It is easy to > write code that calls 'unlock' and then assumes the mutex is unlocked. With > recursive mutexes, all this code breaks. A recursive locking method unlocks the lock when the lock counter is decremented to 0 (done in Unlock()). If it is >0 then the current thread still has the lock. Unlock() just decrements the lock counter. Since only one thread can own (ie. lock) an object, all other threads (except the current lock owning thread) must wait until the lock counter becomes 0 again. That's the logic of recursive locking. Recursive locking is defined for the current "lock owning thread" only. I would suggest you inform yourself about how CriticalSection of Win32 works. It's a good example for recursive locking. I'm using similar logic but mine is at least twice faster. There is plenty of information available on the net, just google. For example Andrei Alexandrescu (the author of "Modern C++ Design") writes in one of his articles (http://www.informit.com/articles/article.asp?p=25298, page 4): <<<< - "Your mutex implementation might support the so-called recursive mutex semantics. This means that the same thread can lock the same mutex several times successfully. In this case, the implementation works but has a performance overhead due to unnecessary locking. [...]" - "Your mutex implementation might not support recursive locking, which means that as soon as you try to acquire it the second time, it blocks — so the ATMWithdrawal function enters the dreaded deadlock." <<<< The "performance overhead due to unnecessary locking" for recursive locking is neglectable if Lock() and Unlock() are carefully designed and implemented; Unfortunately the above mentioned CriticalSection of Win32 is a very bad (slow) implementation of recursive locking. .