Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

looking through the docs of posix' pthread_mutex_t and window's mutex and CRITICAL_SECTION I noticed there is no easy way of checking whether the current thread holds a specific mutex

posix' pthread_mutex_t can be tested when it's not reentrant but that is only a artifact of a non-reentrant mutex

ofcourse checking whether another thread holds a mutex is trivial with trylock

java does allow checking whether the current (and only current) thread holds an object monitor with Thread.holdsLock(Object) which is advised to only be used for debugging

likewise the interface of java.util.concurrent.locks.Lock in also doesn't provide any checking method (the implementation ReentrantLock however does with ReentrantLock.isHeldByCurrentThread())


I can imagine that this can be quite easily implemented alongside any other lock (and is even required when implementing a reentrant mutex)

all it would require is a unique handle for each thread (already available in threading libraries or could even be the TLS pointer whichever is smaller) and at most 2 extra fields (one for the handle and one for the holdcount)

  1. when locking: after the lock is acquired increment the holdcount and set the handle to the current thread handle

  2. when unlocking: before unlocking decrement the holdcount and set the handle to nil only when the holdcount is now 0

  3. when testing: get the handle and compare it to the current thread's handle
    this may be possible without any membarriers as long as a simultaneous write doesn't mangle the bits of the read in a such way that a handle is read that wasn't being written (i.e. the reading thread's handle)

the only race conditions possible are in #3 (the rest happens when the lock is held) and may be irrelevant depending on the memory model given the size of the read value (one word) (edit: if a atomic read/write scheme is used this is even solved entirely)

the holdcount is not even be necessary when the mutex is not reentrant and you're not making it reentrant


EDIT2: I want to clarify that the hasLock would only be used for debugging purposes (asserts) for example to ensure that a method that needs a lock held by the caller actually has it

as for the race condition being irrelevant:

  1. first requirement is that you need a way to write and read concurrently in a way that you would only get values that were actually written
  2. second the test handle == currentHandle would treat both nil and the handle of another thread the same way
  3. the only way that currentHandle would be equal to handle is when the current thread has written it during locking
share|improve this question
    
Try doing atomic read/write on a machine with a lot of cores. It's hard, very very hard. If it fits in a 2U slot on a 19" rack, it's not a lot of cores… –  Donal Fellows Aug 2 '11 at 13:46
    
To elaborate on my comment, as you scale a system up to more cores, the only way to guarantee that nobody is writing some shared modifiable memory is to hold a lock. Making locks work on a big machine is really hard; top-end shared-memory supercomputers have lots of specialized hardware for it, but nobody else bothers because it is expensive and scales like crap. Probing to see if you hold the lock yourself is dumb — you should know or use a reentrant lock — probing to see another thread has it is impossibly dangerous (unless you freeze all the threads first, which is expensive). –  Donal Fellows Aug 11 '11 at 10:57

6 Answers 6

up vote 5 down vote accepted
+100

The reason is nothing to do with race conditions, or the method returning a value that you can't rely on:

  • If the current thread holds the lock before the test, it is guaranteed to hold it after the test.

  • If the current thread doesn't hold the lock before the test, it won't hold it after the test.

This situation will only change if the thread executes a call to release (or acquire) the lock.


I think that the real reason is that a holdLock() method is of little use to a well designed application.

Suppose that a thread calls holdsLock() and it returns true. What should the thread do?

  • If the result is entirely unexpected, the thread has gotten into an inconsistent state, and it probably has no safe option other than to terminate the application. But you could have achieve the same thing (more efficiently) by just attempting the lock call, and handling the exception / failure code that says "you've already got the lock".

  • If the result is entirely expected, then why bother with the test? And when you say "debugging", the retort is that it is not a good idea to bog down a (possibly) performance critical library method with debug support infrastructure. And (as Donal Fellows points out) there are concerns with stability, etc if the debugging support can be turned off and on.

  • If the result could have gone either way, then (presumably) the thread will have to set some flag / counter, and conditionally acquire and later conditionally release the lock. (If the acquire and release are in the same method, then a local variable will do, otherwise you've created a bigger problem.) But the real point is that if you are effectively making the non-reentrant lock work as a reentrant lock. A better idea would be to simply use a reentrant lock.

share|improve this answer
    
congratulations, you're the first one to really understand the question. As for debugging you turn the tests off (with conditional compilation) in the release version –  ratchet freak Aug 2 '11 at 18:18
4  
@ratchet: And then the behavior changes completely and you get lots of race conditions. –  Donal Fellows Aug 11 '11 at 10:58
    
@DonalFellows - exactly. "Debugging" code leading to new bugs. No thanks. –  Stephen C Feb 2 '13 at 5:43

That's probably because the best way to tell if you hold a mutex is that there's a lock a few lines up. If your mutexes are being held longer than that, your software needs some rearchitecting.

share|improve this answer
1  
unless you want debugging access when calling getters and setters of nested private objects –  ratchet freak Jul 22 '11 at 17:49
    
@ratchel freak: recursive lock is usually allowed from the same thread. Good debugger like WinDbg can dump the locks and their owners. –  Coder Aug 2 '11 at 12:19
    
@coder a recursive lock doesn't tell when the lock is already held before the acquire and an assert to ensure it is held in all cases is much faster to debug then break&dump each time a function is called –  ratchet freak Aug 2 '11 at 12:47
    
@RF: If you already hold a (recursive) lock, re-locking it for another critical section in the same thread is completely acceptable, because the same thread can't make other changes to the mutex-protected object at the same time. I agree with Karl: if you're having to look at the state of the lock, something isn't right. –  Blrfl Aug 2 '11 at 13:44

You Testing a lock because leads to race conditions.

The "only race conditions possible are in #3 ... and may be irrelevant" is a bad policy.

A race condition is simply a fatal flaw in the design.

That's why these "test" methods are not provided.

You've answered your own question very nicely.


Also, anyone writing an application using lock testing will write an application with a race condition.

You can only seize or wait to seize a mutex; anything else (like a test) involves designing a race condition.

share|improve this answer
1  
you forgot the "depending on the memory model" an atomic read and write on the handle will solve that race adequately made an edit in the question clarifying that –  ratchet freak Jul 22 '11 at 17:46
    
@ratchet freak: I didn't ignore it. "depending on the memory model" doesn't remove the race condition. Indeed, the lack of a well-defined Intel memory write order exacerbates the problem. –  S.Lott Jul 22 '11 at 18:12
    
@slott an atomic read/write structure still solves it because the only thread that will write a certain thread's handle is the thread itself and the thread handle is only compared for equality with the handle of the current thread. so a racing lock and hasLock will still give the same results no matter how they are interleaved –  ratchet freak Jul 22 '11 at 18:17
    
(Grrr -- misread the question. That'll teach me.) There's a difference between an "islocked()" or "haslock()" method/function and "trylock()", which either acquires the lock or returns failure. There is no race condition for a test-and-lock function that is completely atomic. The logic is even fairly simple -- acquire a lock on the lock (spinlocks are handy), determine if the lock is held, if not you can modify the lock (you've locked it, after all), release the lock on the lock, and you return success. If the lock is held, release the lock and return failure. –  Julie in Austin Apr 10 at 2:09

There's absolutely no reason, from a functionality standpoint, not to have included a test-and-lock functionality, and in previous jobs (I was an AIX kernel developer) I've used that to great effect.

Implementation is trivial -- any architecture which supports compare-and-swap operations can atomically test for a lock being set and acquire the lock at the same time. If the compare-and-swap works, you have the lock and no one else can acquire it until you compare-and-swap back to the unlocked state. If it fails, someone else has the lock.

In Java, it's fairly simple. Declare a Long variable. Synchronize on that variable and check to see if it is zero (unlocked) or non-zero. If it is zero, set the value to Thread.currentThread().getId(), and return true. If it is non-zero, set it to false. When you're done, reverse the process -- synchronize on the variable, set its value to zero and you're done.

What you do with a locking scheme that supports test-and-lock type functionality is up to you. Saying "It's bad -- don't do it!" is naive.

share|improve this answer

I see several reasons for that.

  • First, the result of the check function will not be reliable. Between the moment when the test is done and the moment you use that data, the result could have changed, thus the result is useless.
  • Second, the cost of the check would be as exepensive as a lock/unlock on the mutex itself because it will require atomic operation or - irronically - another mutex. Both operation are expensives.
  • Third, because it's totally useless. The mutex is here to protect a piece of code/data to be used by 2 thread at the same time. You cannot do anything usefull of the locking information of a mutex with that piece of code/data without risking race condition. You could eventually log this information, but why not do that in the code that use the mutex or by wrapping the mutex into a decorrator ? This will provide the same information, at a reduced cost, and with more accurate results.

Considering all dangerous usages that could be done with such a functionnality, the fact that thoses damages would be really hard to spot/debug and the lack of usefullness of the functionnality, the better choice is simply to not provide this functionnality.

A fucntion isHeldByCurrentThread would be easy to implement using thread local storage and checking a boolean here. The right place to impelment that is in a decorator associated with the mutex that update values on lock/release of the mutex (for exemple). Anyway, you should be already aware of this information as long as you locked that mutex in the given thread.

This isn't implemented in the standard mutex implementation because ti has to be as fast as possible and updating a thread local value would be an addition in the cost of lock/release so undesirable.

In addition, this information can be added by yourself to the mutex pretty easily and isn't related to problems you got with synchronization, which are hard enough to handle to justify in itself a separation of concerns between the lock itself, and the calculation of thread local boolean to provide an information that you are supposed to already know.

share|improve this answer
    
I meant that hasLock would be a isHeldByCurrentThread –  ratchet freak Aug 2 '11 at 12:37
    
Ok, editing to adress more specifically this point. –  deadalnix Aug 2 '11 at 12:39
    
a recursive mutex implementation needs to implement this already and as I said several time this is to ensure the calling code has the lock in situations getting the lock and then releasing it invalidates the return value –  ratchet freak Aug 2 '11 at 13:03
    
Ok, if you keep changing the question, no doubt nobody will provide a satisfying answer. It looks like you are trying to prove yourself smarter that those dumb asses that did mutexes, to have an answer. Anyway, the information you are looking for isn't at the lock level, but in the OS's scheduling system. It would be more complex and solwer than keeping track of that in thread local storage yourself than retrieve it, and would impose useless constraint on that part of the system. This part is critical enought to not add useless stuff in it. –  deadalnix Aug 2 '11 at 13:54

Testing for lock is useless, because by the time it returns true or false, the situation has changed. Redesign for TryAcquire semantics.

Also, you can look up few lines to see if there is a lock call, and in some implementations you can acquire a lock second time if you do it from the same thread that owns the lock. Helps when you have recursive calls.

share|improve this answer
    
it would be used for debugging, for example to check whether you hold the lock when a getter/setter (or any other function where it is useless to get the lock by itself like one that returns some info about private state that is invalidated when the lock is released) is called –  ratchet freak Aug 2 '11 at 12:40
    
@ratchel freak: If you are modifying/reading shared state, you have to hold a lock. Or there must be a volatile variable that guarantees that no one else can touch the state. If there are situation where thread "might" hold a lock, they will cause problems after someone else modifies the code, or you execute it on a machine with different timing behavior. –  Coder Aug 2 '11 at 12:47
    
and I want to assert that the lock is held when the modifying function that requires the caller to hold the lock is called –  ratchet freak Aug 2 '11 at 12:51
    
@ratchet freak: Then the function should receive the shared state as a reference from caller which will do the locking. Redesign the calling hierarchy. Say Foo(State &sharedState); This way everyone sees that sharedState can be modified inside the function when they review the code. –  Coder Aug 2 '11 at 13:03
    
but an assert will verify that the lock is held instead of needing to rely on documentation and programmers/code reviewers to ensure correctness. not to mention that Foo(State sharedState); would also need the lock (read access) and may just be called Foo(State state); –  ratchet freak Aug 2 '11 at 13:08

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.