Deadlocks and Timing Problems

Thread deadlocks can occur in a multithreaded program running inside the debugger, even though they never seem to occur when the program runs outside the debugger. Consider a program with two threads running, in which the threads both request two mutex semaphores but in different orders:

THREAD 1                                  THREAD 2
Lots of code                              A bit of code, then request semaphore Y
Request semaphore X                       Request semaphore X
Lots of Code                              Release semaphores Y and X
Requests semaphore Y
Some code
Release sempahores X and Y

These two threads may deadlock. However, because the operating system may timeslice your program and the threads within it in an unpredictable fashion, such deadlocks may not become obvious until you try to debug the program. In the example, you may find that when run outside the debugger, Thread 2 can request and release both semaphores before Thread 1 has even finished its large initial code section; both semaphores may already be released by Thread 2 by the time they are requested by Thread 1. However, within the debugger, if you are setting breakpoints, stepping through code, or otherwise slowing down one thread compared to another, you may wind up finding the requests for the semaphores clashing and thereby blocking each other. For example, if you step through Thread 2, its short initial code section may take longer to execute than the long initial code section of Thread 1:

Thread 1: Lots of code
     Thread 2: A bit of code
.
     .
.
     .
Thread 1: Lots of code completes
     Thread 2: A bit of code completes
Thread 1: Request semaphore X (okay)
     Thread 2: Request semaphore Y (okay)
Thread 1: Lots of code, then request semaphore Y (waits)
     Thread 2: Request semaphore X (deadlock)
 

Because each thread is requesting a semaphore owned by the other thread, the two threads lock up.

To avoid this kind of lockup, always request a group of semaphores in the same order from every thread that uses them. It is also a good idea to release them in the reverse order from the request order. For example:

THREAD 1                 THREAD 2
Lots of code             A bit of code
Request semaphore X      Request semaphore X (waits)
Lots of code             (still waiting)
Request semaphore Y      (still waiting)
Release semaphore Y      (still waiting)
Release semaphore X      (obtains semaphore X)
                         Request semaphore Y (okay)
...                      ...
 

Regardless of which thread completes first, there is no deadlock because whichever thread requests X last will be forced to wait until X is freed, and X will not be freed until the other thread no longer requires either semaphore.

The debugger may expose semaphore-related deadlocks that do not normally occur, because of the following factors:



Debugging Threads