FREE Subscription to Dr. Dobb’s Digest: Same Great Content, New Digital Edition
Site Archive (Complete)
C++
Email
Print
Reprint

add to:
Del.icio.us
Digg
Google
Furl
Slashdot
Y! MyWeb
Blink
July 31, 2008

The Many Faces of Deadlock

(Page 2 of 4)

Dead(b)locks

The key thing to recognize is that deadlock doesn't arise from a locking cycle exclusively. Any blocking (or, waiting) cycle will do. So a complete definition of deadlock could be put something like this:

deadlock, n.: When N concurrent tasks enter a cycle where each one is blocked waiting for the next.

Clearly, blocking operations include all kinds of blocking synchronization: acquiring a lock (of course); waiting for a semaphore or condition variable to be signaled; waiting to join with another thread or task...But it also includes any other kind of call that waits for a result, including:

  • Acquiring exclusive access to any resource (notably I/O, such as opening a file for writing).
  • Waiting for a future or write-once variable to be available (the result of an asynchronous task).
  • Waiting for any other kind of message to arrive from elsewhere, whether in-process (waiting for a queue container to become nonempty, waiting for a message to be placed into a GUI message queue) or out-of-process or out-of-box (performing a blocking receive call on a TCP socket, for instance).
  • Anything else that waits for some other condition to be satisfied.

Complex Combinations

For a more complex example, consider Figure 1. There are four things going on:

  • Thread 1 is a GUI window message handler that's in the middle of processing a message. As part of that processing, it needs to use some shared state and so wants to take a lock on mutex mut—which is currently held by Thread 4.
  • Thread 4 is doing something with the same shared state and so has acquired mut already, and is just waiting to receive a message from a message queue—a message that is yet to be sent from Thread 2. (Doing communication inside a critical section is problematic and should be avoided where possible. See [2] for more about why to avoid doing communication while holding a lock.)
  • Thread 2 hasn't got around to sending that message yet, because it's waiting to open a file for writing, which is an exclusive mode—a file currently opened for writing and appending on Thread 3.
  • Thread 3 is working with the file, and posts a message to the window, which is a synchronous call that blocks to wait for the message to be handled—but Thread 1 is already handling a message and so can't pump and handle this one.

[Click image to view at full size]

Figure 1: Sample deadlock among locks, message pumps, messages queues, and file access.

Finally, for extra fun and just to emphasize the point that any kind of blocking operation will do, consider that deadlock can involve a human—you! For example:

// Thread 1 (running in the CPU hardware) mut.lock(); char = ReadKeystroke(); // blocks

// Task 2 (running in your brain's wetware) Wait for the prompt to appear. // blocks OK, now start typing.

// Thread 3 (back in the hardware again) mut1.lock(); // blocks Print ( "Press 'Any' key to continue..." );

Here, Thread 1 can't make progress because it's blocked waiting for you to press a key. You haven't pressed a key because you're still waiting for a prompt. Thread 3 can't print the prompt until it returns from being blocked waiting to lock a mutex...held by Thread 1. Around and around we go.

Previous Page | 1 Deadlock | 2 Dead(b)locks | 3 Dealing With Deadlock | 4 General Deadlock Detection Next Page
TOP 5 ARTICLES
No Top Articles.



MICROSITES
FEATURED TOPIC

ADDITIONAL TOPICS

INFO-LINK