0

Is there a foolproof way to automatically release mutexes held by a thread when that thread is exiting (in its destructor)?

The approach I have been taking is to create a structure for each mutex which hold the identity of the thread that holds it, and then in the destructor to scan through this list and if any mutexes match the thread being finished, to release it then. But I'm thinking that this actually has a race condition: what happens if after I lock the mutex but before I set the data structure the destructor is called?

I've also looked at pthread_mutexattr_setrobust_np, but my understanding is that np functions are non-portable, and I have had issues with that in the past.

For reference, each thread is associated with a TCP/IP connection, and locking/unlocking occurs in response to requests over this connection. If the connection abnormally closes I need to clean up i.e. release any locks held.

Michael
  • 9,060
  • 14
  • 61
  • 123
  • "But I'm thinking that this actually has a race condition: what happens if after I lock the mutex but before I set the data structure the destructor is called?" - then it sounds like you already have a race condition even before trying to add tracking to the mutex. If you're calling a destructor on an object while it may be in use on another thread there's a problem. – Michael Burr Jul 30 '12 at 20:24
  • well, if i call pthread_kill on a thread that is in pthread_mutex_lock, in the destructor the lock will either be held or not, as acquiring the lock is an atomic operation. – Michael Jul 30 '12 at 20:56

1 Answers1

0

I found a solution which appears to work. First, I use an error checking mutex (PTHREAD_ERRORCHECK_MUTEX_INITIALIZER or PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP).

Next, in the destructor, I trying to unlock all mutexes, with the idea being any mutex not owned by the thread will be left alone, but any mutex owned by the thread will be released.

For some reason even mutexes owned by the thread return EPERM, but a subsequent attempt to re-lock the mutex from another thread succeeds whereas without trying to unlock another attempt will deadlock. Conversely, other mutexes not owned by the destructed thread are still found to be locked after the destructor runs.

Michael
  • 9,060
  • 14
  • 61
  • 123
  • Are you saying that the dtor is running on the same thread as the thread that acquired the mutex? If so, then getting an `EPERM` when unlocking the mutex is an indication that things aren't in the state you think they are. How is the thread getting to the dtor without having released the mutex? You may want to research cancellation points and cancellation cleanup handlers. – Michael Burr Jul 30 '12 at 22:45
  • I'm assuming it does. When I call pthread_self() from the destructor it returns the value I expect from the thread. The reason the thread doesn't release the mutex before getting to the destructor is that the mutex is acquired by the request of a client on a TCP connection. (The client is actually getting a lock, but the lock is implemented in terms of mutexes) If the connection dies before the client releases we have to clean up (release the locks) somehow. – Michael Jul 30 '12 at 23:12
  • i have been looking at cleanup handlers too, but i'm not sure how well they would work if they aren't done in the same scope as the mutex locking, i.e. they would need to be done at the top level of the thread, whereas mutex locking could occur at any level of function nesting. – Michael Jul 30 '12 at 23:23
  • If the dtor is (and always will be) running in the context of the thread acquiring the mutex, then there's no chance of a race condition when trying to add tracking information for the mutex as you described in the question. A thread can't race with itself (that's why single threaded programs don't need to worry about them). – Michael Burr Jul 30 '12 at 23:23
  • i believe the race condition would be against the thread trying to kill it, not against itself – Michael Jul 30 '12 at 23:48