Boost logo

Boost Users :

Subject: [Boost-users] Adding a thread to a Singleton class
From: Stephen Torri (storri_at_[hidden])
Date: 2010-01-10 17:29:39


I would like to have a logging class, called Trace, that I can off load
the string formatting of logging the status of my program to a separate
thread. Right now my logging class is written as a singleton object.
This code is located within a shared library and used extensively in it
to write tracing information to a file. Right now all the string
formatting occurs on the main thread which degrades performance.

QUESTION #1: I was wondering if the way in which I am attempting to have
a central point for logging information via a Singleton without passing
around a variable is a practical solution to this problem?

I followed the Singleton guideline from Boost cookbook
( http://www.boostcookbook.com/Recipe:/1235044 ) of how to implement my
singleton. I was thinking that I could add a boost::thread to my logging
class, called Trace, and use a queue to pass objects over to be logged.
A boost::condition would be used to signal the boost::thread to wake up
and lock the queue, copy the contents then start writing the string
formatted to a file.

Below is my initial code. I would appreciate comments on the design,
ideas for better designs and suggestions.

Stephen Torri

---------------- SINGLETON class ----------------------------

#ifndef SINGLETON_H
#define SINGELTON_H

#include <boost/utility.hpp>
#include <boost/thread/once.hpp>
#include <boost/scoped_ptr.hpp>

namespace myproject { namespace trace {

    template <typename T>
    class Singleton : boost::noncopyable
    {
    public:

      static T& Instance()
      {
        boost::call_once ( init, m_flag );
        return *m_obj;
      }

      static void init ()
      {
        m_obj.reset ( new T () );
      }

    protected:

      ~Singleton(){}
      Singleton(){}

    private:

      static boost::scoped_ptr<T> m_obj;
      static boost::once_flag m_flag;
    };

  } // namespace trace
} // namespace trace

template <typename T> boost::scoped_ptr<T> myproject::trace::Singleton<T>::m_obj ( 0 );
template <typename T> boost::once_flag myproject::trace::Singleton<T>::m_flag = BOOST_ONCE_INIT;

#endif // define SINGELTON_H

---------------- Trace class -----------------------

#ifndef myproject_TRACE_H
#define myproject_TRACE_H

#include <boost/cstdint.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/thread.hpp>
#include <boost/utility.hpp>

#include <vector>

#include "Trace_State.h"

namespace myproject { namespace trace {

    //
    // Turn Trace into a singleton class that contains a worker thread
    // - Wait for data
    // - If data exists then process
    // - Else wait
    // Add method for adding a ITraceable object. This will lock the logging vector, add
    // element then signal the boost condition variable
    class Trace : public Singleton<Trace> {

      friend class Singleton<Trace>;

    public:

      void add_Trace ( interface::ITraceable::ptr_t trace );

    private:

      Trace();

      // Worker thread functions
      void threadMain();
      void processTraces();

      // Synchronization variables
      boost::mutex m_lock;
      boost::condition_variable m_condition;

      // Data variables
      typedef std::vector < interface::ITraceable::ptr_t > QueueType;
      QueueType m_queue;

      // Thread variables
      boost::shared_ptr<boost::thread> m_thread;
      volatile bool m_hasWork;

      // Logging
      Trace_State m_state;
    };

  } /* namespace trace */
} /* namespace myproject */

#endif /* myproject_TRACE_H */

----------------- Trace Source -----------------------

#include "Trace.h"
#include "myproject/errors/Internal_Exception.h"

#include <boost/bind.hpp>

namespace myproject { namespace trace {

    Trace::Trace ()
      : m_hasWork ( true ),
        m_state ( "Trace", "Trace" )
    {
      m_thread = boost::shared_ptr<boost::thread> ( new boost::thread ( boost::bind ( &Trace::threadMain, this ) ) );
    }

    Trace::~Trace()
    {
      // stop thread
      m_hasWork = false;
    }

    void Trace::add_Trace ( boost::shared_ptr<interface::ITraceable> trace )
    {
        // Add ITraceable object to the queue
        {
          // lock queue
          {
            boost::lock_guard<boost::mutex> lock ( m_lock );
            m_queue.push_back ( trace );
          }
            
          m_condition.notify_one();
        }
    }

    void Trace::threadMain()
    {
      boost::unique_lock<boost::mutex> lock ( m_lock );

      while ( m_hasWork )
        {
          while ( m_queue.size() == 0 )
            {
              m_condition.wait ( lock );
            }

          this->processTraces();
        }
    }

    void Trace::processTraces()
    {
      QueueType local_queue;

      // Lock queue and copy
      {
        boost::lock_guard<boost::mutex> lock ( m_lock );
        std::copy ( m_queue.begin(), m_queue.end(), local_queue.begin() );
      }

      for ( QueueType::iterator pos = local_queue.begin();
            pos != local_queue.end();
            pos++ )
        {
                // Process ITraceable object and write to a file
        }
    }

  } /* namespace trace */
} /* namespace myproject */


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net