I'm writing a singleton Logger class in c++. this class provides logging API to multiple threads. to make it thread safe I'm using wxCRIT_SECT_LOCKER macro.
say I have this in my Logger class the following functions (simple example):
void Logger::error( string msg )
{
wxCRIT_SECT_LOCKER(adapter_locker, gs_LogBufferShield);
// do something such as getting/setting class members
m_err_cnt++;
do_log("Error: " + msg);
}
void Logger::warning( string msg )
{
wxCRIT_SECT_LOCKER(adapter_locker, gs_LogBufferShield);
// do something such as getting/setting class members
m_warn_cnt++;
do_log("Warning: " + msg);
}
void Logger::do_log( string msg )
{
wxCRIT_SECT_LOCKER(adapter_locker, gs_LogBufferShield);
// do something such as getting/setting class members
m_log_cnt++;
cout << msg << endl;
}
Problem:
when Logger::warning() is called we'll enter the critical section twice, once in Logger::warning() and one more time in *Logger::do_log()*.
If you agree the problem is real and can cause deadlock, how can I avoid multiple locks (using wxCriticalSection class/macros).
What is often done is to create internal APIs that do not take locks that are called by the public APIs that do take the locks.
void Logger::error( string msg )
{
wxCRIT_SECT_LOCKER(adapter_locker, gs_LogBufferShield);
m_err_cnt++;
do_log_internal("Error: " + msg);
}
void Logger::warning( string msg )
{
wxCRIT_SECT_LOCKER(adapter_locker, gs_LogBufferShield);
m_warn_cnt++;
do_log_internal("Warning: " + msg);
}
void Logger::do_log( string msg )
{
wxCRIT_SECT_LOCKER(adapter_locker, gs_LogBufferShield);
do_log_internal(msg);
}
void Logger::do_log_internal( string msg )
{
m_log_cnt++;
cout << msg << endl;
}
However, for your problem, you may rather use wxMutex
directly, and use the type wxMUTEX_RECURSIVE
when you construct it. This way, a mutex has count. When the mutex is first locked, the count is set to 1. If the same thread grabs the mutex again, it increments a count. Releasing the mutex decrements the count. When the count reaches 0, the mutex is unlocked.