You can use the following command to compile this program:
g++ Thread.cpp main.cpp -o deadlock.exe -std=c++17 -lpthread
Observe the output of the program, as follows:

Refer to the code snippets of the Thread::getCount() and Thread::updateCount() methods:
int Thread::getCount() {
cout << name << " is waiting for lock in getCount() method ..." << endl;
lock_guard<mutex> locker(commonLock);
cout << name << " has acquired lock in getCount() method ..." << endl;
return count;
}
int Thread::updateCount() {
count << name << " is waiting for lock in updateCount() method ..." << endl;
lock_guard<mutex> locker(commonLock);
cout << name << " has acquired lock in updateCount() method ..." << endl;
int value = getCount();
count = ++value;
return count;
}
From the previous output screenshot image, we can understand the WRITER thread seems to have started first. As per our design, the WRITER thread will invoke the Thread::updateCount() method, which in turn will invoke the Thread::getCount() method.
From the output's screenshot, it is evident from the print statements that the Thread::updateCount() method has acquired the lock first and has then invoked the Thread::getCount() method. But since the Thread::updateCount() method hasn't released the mutex lock, there is no way for the Thread::getCount() method invoked by the WRITER thread to proceed. Meanwhile, the OS scheduler has started the READER thread, which seems to wait for the mutex lock acquired by the WRITER thread. Hence, for the READER thread to complete its task, it has to acquire the lock on the Thread::getCount() method; however, this isn't possible until the WRITER thread releases the lock. To make things even worse, the WRITER thread can't complete its task until its own Thread::getCount() method call completes its task. This is what is called a deadlock.
This is either a design or logical issue. In Unix or Linux, we can make use of the Helgrind tool to find deadlocks by racing similar synchronization issues. The Helgrind tool comes along with the Valgrind tool. The best part is that both Valgrind and Helgrind are open source tools.
In order to get the source line number that leads to a deadlock or race issue, we need to compile our code in debug mode, as shown now with -g flag:
g++ main.cpp Thread.cpp -o deadlock.exe -std=c++17 -lpthread -g
The Helgrind tool can be used to detect deadlock and similar issues, as shown here:
valgrind --tool=helgrind ./deadlock.exe
Here's a short extract of the Valgrind output:

One simple fix to resolve the issue is to refactor the Thread::updateCount() method, as shown here:
int Thread::updateCount() {
int value = getCount();
count << name << " is waiting for lock in updateCount() method ..." << endl;
lock_guard<mutex> locker(commonLock);
cout << name << " has acquired lock in updateCount() method ..." << endl;
count = ++value;
return count;
}
The output of the refactored program is as follows:

Interestingly, for most complex issues, the solution will be generally very simple. In other words, silly mistakes sometimes may lead to serious critical bugs.