Creating and avoiding deadlock conditions in C++

by

·

A short and to-the-point post illustrating how a deadlock situation can be created in C++, together with a possible strategy for avoiding such a condition.

I’m am using the boost libraries to implement threads and mutexes, but it should be possible to the standard library implementations std::thread, std::mutex etc as well.

Scenario 1: Deadlock Condition

If thread A is executing and isn’t holding mutex lock 1 yet and thread B acquires mutex lock 2, neither of the threads can continue past the second lock acquisition:

#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>

boost::mutex mutex1, mutex2;

void ThreadA()
{
	// Creates deadlock problem
	mutex2.lock();
	std::cout << "Thread A" << std::endl;
	mutex1.lock();
	mutex2.unlock();
	mutex1.unlock();
}

void ThreadB()
{
	// Creates deadlock problem
	mutex1.lock();
	std::cout << "Thread B" << std::endl;
	mutex2.lock();
	mutex1.unlock();
	mutex2.unlock();
}

void ExecuteThreads()
{
	boost::thread t1( ThreadA );
	boost::thread t2( ThreadB );

	t1.join();
	t2.join();

	std::cout << "Finished" << std::endl;
}

int main()
{
	ExecuteThreads();

	return 0;
}

When running this notice that the program hangs and is unable to proceed beyond the second mutex lock acquisition:

Deadlock1

Scenario 2: Avoiding Deadlocks

The problem of deadlocks can avoided by maintaining consistency in the ordering of the locking and unlocking of mutexes:

#include <iostream>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>

boost::mutex mutex1, mutex2;

void ThreadA()
{
	// Solves deadlock problem
	mutex1.lock();
	std::cout << "Thread A" << std::endl;
	mutex2.lock();
	mutex2.unlock();
	mutex1.unlock();
}

void ThreadB()
{
	// Solves deadlock problem
	mutex1.lock();
	std::cout << "Thread B" << std::endl;
	mutex2.lock();
	mutex1.unlock();
	mutex2.unlock();
}

void ExecuteThreads()
{
	boost::thread t1( ThreadA );
	boost::thread t2( ThreadB );

	t1.join();
	t2.join();

	std::cout << "Finished" << std::endl;
}

int main()
{
	ExecuteThreads();

	return 0;
}

Instead of waiting indefinitely threads A and B are able to complete:

Deadlock2

Comments

One response to “Creating and avoiding deadlock conditions in C++”

  1. David Avatar
    David

    Great example. Thanks for sharing. Which of the four necessary and sufficient conditions would this be considered as solving the deadlock problem? To me it seems like the reordering of locking/unlocking mutexes would be solving the circular wait..? I just wanted to clarify that. Thanks!