Creating and avoiding deadlock conditions in C++

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

Latest Comments

  1. David 22 March 2017

Leave a Reply