|
Boost Users : |
Subject: Re: [Boost-users] Tracking down a race condition
From: Stephen Torri (storri_at_[hidden])
Date: 2010-01-25 18:44:33
On Mon, 2010-01-25 at 19:38 +0100, Roland Bock wrote:
> You can see what is happening by making the writing of logs slower:
>
> void Trace::threadMain()
> {
> boost::this_thread::sleep(boost::posix_time::seconds(1)); //
> Insert this
> boost::unique_lock<boost::mutex> lock ( m_lock );
> ...
I made it slower adding in the line. I did not see anything in the
output. How did you observe the program after adding that line? Did you
use a debugger or just execute it?
I ran it in gdb with the following commands:
(gdb) b myproject::trace::Trace::threadMain
(gdb) r
This got me into the threadMain when it is started for the first time.
Where I noticed that m_hasWork was set to false. I was expecting the
initalizers to be executed before calling the thread creation. I moved
the initialization of m_hasWork to just before the thread creation but
it appears to be still false when I ran it again. So I put it inside the
threadMain to set m_hasWork to true. Well it appears that there is a one
copy of m_hasWork in the thread memory space which is different from the
main program memory space.
I moved the file opening to just before the creating the thread and
closed if after the join in the destructor.
Now it writes but fails to exit. I believe this is related problem to
the m_hasWork since the thread never exits because its version of
m_hasWork is always true.
Am I creating the thread incorrectly? The following code shows the
problem.
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 api {
/*!
* \brief Trace level constants
* \date 2003
* \author Stephen Torri
*/
class TraceLevel {
public:
enum Values {
NONE = 0,
WARN = 10,
ERROR = 20,
INFO = 30,
DEBUG = 40,
DETAIL = 50,
DATA = 60
};
};
} // namespace api
} // namespace myproject
namespace myproject { namespace interface {
class ITraceable {
public:
typedef boost::shared_ptr<ITraceable> ptr_t;
virtual std::string get_Formatted_String() = 0;
virtual boost::uint32_t getLevel() = 0;
virtual boost::uint32_t getPID() = 0;
virtual boost::thread::id getThreadID() = 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 )
{
#ifndef WIN32
m_pid = getpid();
#else
m_pid = GetCurrentProcessId();
#endif /* WIN32 */
m_thread_id = boost::this_thread::get_id();
}
virtual ~StringTraceable(){}
virtual std::string get_Formatted_String()
{
return m_name;
}
virtual boost::uint32_t getLevel()
{
return m_level;
}
virtual boost::uint32_t getPID()
{
return m_pid;
}
virtual boost::thread::id getThreadID()
{
return m_thread_id;
}
private:
boost::uint32_t m_area;
boost::uint32_t m_level;
boost::uint32_t m_pid;
boost::thread::id m_thread_id;
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 )
{
#ifndef WIN32
m_pid = getpid();
#else
m_pid = GetCurrentProcessId();
#endif /* WIN32 */
m_thread_id = boost::this_thread::get_id();
}
virtual ~NumberTraceable(){}
virtual std::string get_Formatted_String()
{
return m_name;
}
virtual boost::uint32_t getLevel()
{
return m_level;
}
virtual boost::uint32_t getPID()
{
return m_pid;
}
virtual boost::thread::id getThreadID()
{
return m_thread_id;
}
private:
boost::uint32_t m_area;
boost::uint32_t m_level;
boost::uint32_t m_pid;
boost::thread::id m_thread_id;
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 {
class Trace_State_Utils {
public:
static std::string get_Level_String ( boost::uint32_t level )
{
// Write message prefix
if ( level == api::TraceLevel::WARN )
{
return "(WW) ";
}
else if ( level == api::TraceLevel::ERROR )
{
return "(EE) ";
}
else if ( level == api::TraceLevel::INFO )
{
return "(II) ";
}
else if ( level == api::TraceLevel::DEBUG )
{
return "(DEBUG) ";
}
else if ( level == api::TraceLevel::DETAIL )
{
return "(DETAIL) ";
}
else if ( level == api::TraceLevel::DATA )
{
return "(DATA) ";
}
else
{
// We should not be here
abort();
}
}
};
/* 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()
: m_file_prefix ( "Trace" )
{}
virtual ~Trace_State();
Trace_State ( std::string prefix );
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 ( interface::ITraceable::ptr_t trace )
{
// Write ID
m_log_stream << boost::format("%s (pid=%d thread_id=" )
% this->get_ID_String()
% trace->getPID()
<< trace->getThreadID()
<< ") "
<< std::flush;
m_log_stream << Trace_State_Utils::get_Level_String(trace->getLevel()) << trace->get_Formatted_String() << std::endl << std::flush;
}
private:
std::string m_file_prefix;
std::ofstream m_log_stream;
};
} /* namespace trace */
} /* namespace trace */
namespace myproject { namespace trace {
Trace_State::Trace_State( std::string prefix )
: m_file_prefix ( prefix )
{}
Trace_State::~Trace_State()
{
this->close_Trace_File();
}
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() )
{
// Create file name
std::stringstream name;
name << boost::format("%s_%s.txt")
% m_file_prefix
% this->get_ID_String();
std::string file_name = name.str();
m_log_stream.open ( 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 << std::flush;
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;
}
} /* 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 );
private:
Trace();
// Worker thread functions
void threadMain();
void processTraces(boost::unique_lock<boost::mutex>& lock);
// 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" )
{
m_state.open_Trace_File();
// 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();
m_state.close_Trace_File();
}
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 );
m_hasWork = true;
while ( m_hasWork )
{
if ( m_queue.size() == 0 )
{
m_condition.wait ( lock );
}
else
{
this->processTraces(lock);
}
}
}
void Trace::processTraces(boost::unique_lock<boost::mutex>& lock)
{
assert ( lock.owns_lock() );
QueueType local_queue ( m_queue.begin(), m_queue.end() );
m_queue.clear();
lock.unlock();
for ( QueueType::iterator pos = local_queue.begin();
pos != local_queue.end();
pos++ )
{
myproject::interface::ITraceable::ptr_t trace_obj = (*pos);
m_state.write_Message ( trace_obj );
}
lock.lock();
}
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 );
}
} /* namespace trace */
} /* namespace myproject */
int main ( int, char** )
{
namespace trace = myproject::trace;
namespace api = myproject::api;
myproject::interface::ITraceable::ptr_t tmp_ptr ( new trace::StringTraceable( 0, api::TraceLevel::INFO, "funky" ) );
trace::Trace::Instance().add_Trace ( tmp_ptr );
}
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