Example: Two Modules, One Lock Each
One fine day, you decide to write a new web browser that lets users write plug-ins to customize the behavior or rendering of individual page elements. Consider the following possible code, where we simply protect all the data structures representing elements on a given page using a single mutex mutPage:
// Example 1: Thread 1 of a potential deadly embrace
//
class CoreBrowser {
... other methods ...
private void RenderElements() {
mutPage.lock(); // acquire exclusion on the page elements
try {
for( each PageElement e on the page ) {
DoRender( e ); // do our own default processing
plugin.OnRender( e ); // let the plug-in have a crack at it
}
} finally {
mutPage.unlock(); // and then release it
}
}
}
Do you see the potential for deadlock? The trouble is that if inside the call to plugin.OnRender the plug-in might acquire some internal lock of its own, which could be one arm of a potential deadly embrace. For example, consider this plug-in implementation that does some basic instrumentation of how many times certain actions have been performed and protects its internal data with a single mutex mutMyData:
class MyPlugin {
... other methods ...
public void OnRender( PageElement e ) {
mutMyData.lock(); // acquire exclusion on some internal shared data
try {
renderCount[e]++; // update #times e has been rendered
} finally {
mutMyData.unlock(); // and then release it
}
}
}
Thread 1 can therefore acquire mutPage and mutMyData in that order. Thread 1 is potential deadlock-bait, but the trouble will only manifest if some other Thread 2 that could run concurrently with the aforementioned code performs something like the following:
// Example 2: Thread 2 of a potential deadly embrace
//
class MyPlugin {
... other methods ...
public void RefreshDisplay( PageElement e ) {
mutMyData.lock(); // acquire exclusion on some internal shared data
try { // display stuff in a debug window
for( each element e we've counted ) {
listRenderedCount.Add( e.Name(), renderCount[e] );
}
textHiddenCount = browser.CountHiddenElements();
} finally {
mutMyData.unlock(); // and then release it
}
}
}
Notice how the plugin calls code unknown to it, namely browser.CountHiddenElements? You can probably see the trouble coming on like a steamroller:
class CoreBrowser {
... other methods ...
public int CountHiddenElements() {
mutPage.lock(); // acquire exclusion on the page elements
try {
int count = 0;
for( each PageElement e on the page ) {
if( e.Hidden() ) count++;
}
return count;
} finally {
mutPage.unlock(); // and then release it
}
}
}
Threads 1 and 2 can therefore acquire mutPage and mutMyData in the opposite order, so this is a deadlock waiting to happen if Threads 1 and 2 can ever run concurrently. For added fun, note that each mutex is purely an internal implementation detail of its module that is never exposed in the interface; neither module knows anything about the internal lock being used within the other. (Nor, in a better programming world than the one we now inhabit, should it have to.)


