|
Boost Users : |
Subject: Re: [Boost-users] Adding a thread to a Singleton class
From: Stephen Torri (storri_at_[hidden])
Date: 2010-01-22 07:57:29
Well I had a chance to sit down and see what was going on with my
logging project. What I found out is that the call to thread::join in
the destructor of my logging class is never returning even though the
thread was signaled to stop running. Below is the code. Since I am new
to boost threads I am sure I am making a fundamental mistake.
I compiled this project with boost 1.35 and gcc 4.3.3.
Stephen
-----------------------
#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 <boost/thread/once.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/type_traits/aligned_storage.hpp>
#include <boost/bind.hpp>
#include <boost/format.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/cstdint.hpp>
#include <fstream>
#include <sstream>
#include <vector>
namespace myproject { namespace interface {
class ITraceable {
public:
typedef boost::shared_ptr<ITraceable> ptr_t;
virtual std::string get_Formatted_String() = 0;
};
} // namespace interface
} // namespace myproject
namespace myproject { namespace trace {
class StringTraceable : public interface::ITraceable
{
public:
StringTraceable ( boost::uint32_t area, boost::uint32_t level, std::string name )
: m_area ( area ),
m_level ( level ),
m_name ( name )
{}
virtual ~StringTraceable(){}
virtual std::string get_Formatted_String()
{
return m_name;
}
private:
boost::uint32_t m_area;
boost::uint32_t m_level;
std::string m_name;
};
template <typename UnsignedType>
class NumberTraceable : public interface::ITraceable
{
public:
NumberTraceable ( boost::uint32_t area, boost::uint32_t level, std::string name, UnsignedType line )
: m_area ( area ),
m_level ( level ),
m_name ( name ),
m_line ( line )
{}
virtual ~NumberTraceable(){}
virtual std::string get_Formatted_String()
{
return m_name;
}
private:
boost::uint32_t m_area;
boost::uint32_t m_level;
std::string m_name;
UnsignedType m_line;
};
template <typename T>
struct storage_for
{
boost::aligned_storage<sizeof(T), boost::alignment_of<T>::value> data;
};
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;
namespace myproject { namespace trace {
/* Idea taken from http://www.codeproject.com/debug/xytrace.asp
* (28 Jan 2002 - Xiangyang Liu)
*
* I have modified this so that we don't use varargs and use constant types
*/
class Trace_State {
public:
typedef boost::shared_ptr<Trace_State> ptr_t;
Trace_State();
virtual ~Trace_State();
Trace_State ( std::string name, std::string prefix );
void set_Trace_File_Name ( std::string name );
void set_Trace_File_Prefix ( std::string name );
void set_Trace_Level ( boost::uint32_t level );
void set_Trace_Area_Mask ( boost::uint32_t mask );
void open_Trace_File ( void );
std::string get_ID_String ( void );
void close_Trace_File ( void );
boost::uint32_t get_Trace_Level ( void ) const;
boost::uint32_t get_Trace_Area_Mask ( void ) const;
bool is_Valid_Level ( boost::uint32_t lvl );
bool is_Valid_Area_Mask ( boost::uint32_t mask );
void write_Message ( boost::uint32_t level, interface::ITraceable::ptr_t trace );
private:
std::string m_file_prefix;
std::string m_file_name;
std::ofstream m_log_stream;
};
} /* namespace trace */
} /* namespace trace */
namespace myproject { namespace trace {
Trace_State::Trace_State()
: m_file_prefix ( "Trace" ),
m_file_name ( "" )
{}
Trace_State::Trace_State( std::string name, std::string prefix )
: m_file_prefix ( prefix ),
m_file_name ( name )
{}
Trace_State::~Trace_State()
{
this->close_Trace_File();
}
void
Trace_State::set_Trace_File_Name ( std::string name )
{
}
void
Trace_State::set_Trace_File_Prefix ( std::string name )
{
}
bool
Trace_State::is_Valid_Level ( boost::uint32_t lvl )
{
return true;
}
bool
Trace_State::is_Valid_Area_Mask ( boost::uint32_t )
{
return true;
}
void
Trace_State::set_Trace_Level ( boost::uint32_t level )
{
}
void
Trace_State::set_Trace_Area_Mask ( boost::uint32_t mask )
{
}
void
Trace_State::open_Trace_File ( void )
{
if ( ! m_log_stream.is_open() )
{
if ( m_file_name.empty() )
{
// Create file name
std::stringstream name;
name << boost::format("%s_%s.txt")
% m_file_prefix
% this->get_ID_String();
m_file_name = name.str();
}
m_log_stream.open ( m_file_name.c_str() );
}
}
std::string
Trace_State::get_ID_String ( void )
{
// Create id string
std::stringstream name;
// Get current time
boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
std::tm tm_ref = boost::posix_time::to_tm ( now );
boost::gregorian::date today = now.date();
name << boost::format ( "%1%_%2%:%3%:%4%" )
% boost::gregorian::to_iso_extended_string ( today )
% tm_ref.tm_hour
% tm_ref.tm_min
% tm_ref.tm_sec;
return name.str();
}
void
Trace_State::close_Trace_File ( void )
{
if ( m_log_stream.is_open() )
{
m_log_stream.close();
}
}
boost::uint32_t
Trace_State::get_Trace_Level ( void ) const
{
return 0;
}
boost::uint32_t
Trace_State::get_Trace_Area_Mask ( void ) const
{
return 0;
}
void
Trace_State::write_Message ( boost::uint32_t level, boost::shared_ptr<interface::ITraceable> trace )
{
// write message
}
} /* namespace trace */
} /* namespace trace */
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:
~Trace();
static void write_Trace ( boost::uint32_t area, boost::uint32_t level, std::string name )
{
interface::ITraceable::ptr_t string_ptr ( new StringTraceable ( area, level, name ) );
Trace::Instance().add_Trace ( string_ptr );
}
template <typename UnsignedType>
static void write_Trace ( boost::uint32_t area, boost::uint32_t level, std::string format, UnsignedType value )
{
interface::ITraceable::ptr_t unsigned_ptr ( new NumberTraceable<UnsignedType> ( area, level, format, value ) );
Trace::Instance().add_Trace ( unsigned_ptr );
}
void add_Trace ( interface::ITraceable::ptr_t trace );
void set_Trace_Level ( boost::uint32_t level );
void set_Trace_Area_Mask ( boost::uint32_t mask );
void open_Trace_File ();
void close_Trace_File();
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 < myproject::interface::ITraceable::ptr_t > QueueType;
QueueType m_queue;
// Thread variables
boost::shared_ptr<boost::thread> m_thread;
bool m_hasWork;
// Logging
Trace_State m_state;
};
} /* namespace trace */
} /* namespace myproject */
namespace myproject { namespace trace {
Trace::Trace ()
: m_hasWork ( true ),
m_state ( "Trace", "Trace" )
{
// start thread
m_thread = boost::shared_ptr<boost::thread> ( new boost::thread ( boost::bind ( &Trace::threadMain, this ) ) );
}
Trace::~Trace()
{
// stop thread
{
boost::lock_guard<boost::mutex> lock ( m_lock );
m_hasWork = false;
}
m_condition.notify_one();
m_thread->join();
}
void Trace::add_Trace ( interface::ITraceable::ptr_t trace )
{
// 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 ( m_queue.begin(), m_queue.end() );
m_queue.clear();
for ( QueueType::iterator pos = local_queue.begin();
pos != local_queue.end();
pos++ )
{
// Write message
}
}
void Trace::set_Trace_Level ( boost::uint32_t level )
{
m_state.set_Trace_Level ( level );
}
void Trace::set_Trace_Area_Mask ( boost::uint32_t mask )
{
m_state.set_Trace_Area_Mask ( mask );
}
void Trace::open_Trace_File()
{
m_state.open_Trace_File();
}
void Trace::close_Trace_File()
{
m_state.close_Trace_File();
}
} /* namespace trace */
} /* namespace myproject */
int main ( int, char** )
{
myproject::trace::Trace::Instance().open_Trace_File();
myproject::interface::ITraceable::ptr_t tmp_ptr ( new myproject::trace::StringTraceable( 0, 0, "" ) );
myproject::trace::Trace::Instance().add_Trace ( tmp_ptr );
myproject::trace::Trace::Instance().close_Trace_File();
}
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