Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding Thread Safety to a Simple Logging Function?

From what I read, the standard output streams are generally not thread safe. I have a C++ application (Windows-based, using Visual Studio 2005) that has a very simple logging function:

void logText(string text)
{
    if(g_OutputLogEnabled && g_OutputLog.is_open())
    {
        string logDate = getDateStamp("%Y-%m-%d %H:%M:%S");
        g_OutputLog << "[" << logDate << "]: " << text << endl;
    }

    cout << text << endl; // Also echo on stdout
}

In this example, g_OutputLog is an ofstream and g_OutputLogEnabled is a boolean.

I've been using this small function in my main application with no problem, but I now want to extend its use to some child threads. These threads do work and asynchronously print data as that work is done.

Question: How can I add simple, line-level thread-safety to this routine? All that I really care about is that each line that comes in remains intact in my log. Performance isn't a concern in this case, but (as always) faster is nicer.

I'm aware I could use third party logging packages, but I want to do it myself so I can learn how it works. My multi-threading knowledge isn't what it should be, and I'm trying to improve that.

I've heard the term critical sections, and I'm somewhat aware of mutexes and semaphores, but which would I use in this case? Is there a clean, simple solution? Thanks in advance for any advice.

like image 379
Jonah Bishop Avatar asked Dec 14 '25 16:12

Jonah Bishop


1 Answers

Use scoped lock such as:

void logText(string text)
{
    if(g_OutputLogEnabled && g_OutputLog.is_open())
    {
        string logDate = getDateStamp("%Y-%m-%d %H:%M:%S");

        boost::scoped_lock (g_log_mutex);  //lock
        g_OutputLog << "[" << logDate << "]: " << text << endl;

    } //mutex is released automatically here
    
    boost::scoped_lock (g_cout_log_mutex); //lock on different mutex!
    cout << text << endl; // Also echo on stdout
}

Or you could use std::unique_lock if your compiler supports this.


How would you implement scoped_lock if you cannot use Boost and if you don't have std::unique_lock?

First define mutex class:

#include <Windows.h>

class mutex : private CRITICAL_SECTION  //inherit privately!
{
public:
     mutex() 
     {
        ::InitializeCriticalSection(this);
     }
     ~mutex() 
     {
        ::DeleteCriticalSection(this);
     }
private:

     friend class scoped_lock;  //make scoped_lock a friend of mutex

     //disable copy-semantic 
     mutex(mutex const &);           //do not define it!
     void operator=(mutex const &);  //do not define it!

     void lock() 
     {
        ::EnterCriticalSection(this);
     }
     void unlock() 
     {
        ::LeaveCriticalSection(this);
     }
};

Then define scoped_lock as:

class scoped_lock
{
      mutex & m_mutex;
  public:
      scoped_lock(mutex & m) : m_mutex(m) 
      {
          m_mutex.lock();
      }
      ~scoped_lock()
      {
          m_mutex.unlock();
      }
};

Now you can use them.

like image 115
Nawaz Avatar answered Dec 16 '25 10:12

Nawaz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!