Listing G
// Solution 2
// (note: compiles with gcc 3.2, VC6)
#include "CriticalSection.h"
#include "message_handler_log.h"
#include <queue>
 
 
// the object to be started - on a given thread
struct win32_thread_obj
{
    virtual ~win32_thread_obj() {}
    virtual void operator()() = 0;
};
 
 
struct win32_thread_manager
{
    typedef win32_thread_obj thread_obj_base;
    static void sleep( int nMillisecs) { Sleep( nMillisecs); }
    static void create_thread( win32_thread_obj & obj)
    {
        DWORD dwThreadID;
        CreateThread( 0, 0,
            win32_thread_manager::ThreadProc, &obj, 0, &dwThreadID);
    }
 
 
private:
    static DWORD WINAPI ThreadProc( LPVOID lpData)
    {
        win32_thread_obj * pThread = ( win32_thread_obj *)lpData;
        ( *pThread)();
        return 0;
    }
};
 
 
 
 
// allows thread-safe writing
template< class char_type, class traits_type = std::char_traits< char_type> >
    class thread_safe_log_writer
{
    typedef thread_safe_log_writer< char_type, traits_type> this_class;
    typedef std::basic_ostream< char_type, traits_type> ostream_type;
    typedef std::basic_string< char_type, traits_type> string_type;
 
 
    // forward declaration
    struct thread_info;
    friend struct thread_info;
 
 
    // thread-related definitions
    typedef win32_thread_manager thread_manager;
    typedef typename thread_manager::thread_obj_base thread_obj_base;
    // so that from our thread we know the object we're manipulating
    struct thread_info : public thread_obj_base
    {
        thread_info()
            : m_bHasFinished( false),
              m_pThis( NULL)
        {}
 
 
        /* virtual */ void operator()()
        {
            while ( true)
            {
                std::string * pstr = NULL;
                {
                    CAutoLockUnlock locker( m_pThis->m_cs);
                    // get the string
                    if ( m_pThis->m_astrMessages.size() > 0)
                    {
                        pstr = m_pThis->m_astrMessages.front();
                        m_pThis->m_astrMessages.pop();
                    }
                    // ... only when there are no more messages,
                    //    will we ask if we should be destructed
                    else if ( m_pThis->m_bShouldBeDestructed)
                    {
                        // signal to the other thread we've finished
                        m_bHasFinished = true;
                        return;
                    }
                }
                // write the string
                if ( pstr)
                {
                    m_pThis->m_underlyingLog << *pstr;
                    delete pstr;
                }
                else
                    // nothing to write - wait
                    thread_manager::sleep( 1);              
            }
        }
 
 
        this_class * m_pThis;
        volatile bool m_bHasFinished;
    };
 
 
public:
    void add_message( const string_type & str)
    {
        CAutoLockUnlock locker( m_cs);
        m_astrMessages.push( new string_type( str));
    }
 
 
    thread_safe_log_writer( ostream_type & underlyingLog)
        : m_underlyingLog( underlyingLog),
          m_bShouldBeDestructed( false)
    {
        m_info.m_pThis = this;
        thread_manager::create_thread( m_info);
    }
 
 
    ~thread_safe_log_writer()
    {
        // signal to the other thread we're about to be
        // destructed
        {
            CAutoLockUnlock locker( m_cs);
            m_bShouldBeDestructed = true;
        }
        // wait while the other thread writes all messages
        while ( true)
        {
            CAutoLockUnlock locker( m_cs);
            if ( m_info.m_bHasFinished)
                // the other thread has finished
                break;
        }
    }
 
 
    CCriticalSection & cs() const { return m_cs; }
private:
    // the critical section used for thread-safe locking
    mutable CCriticalSection m_cs;
 
 
    // needed to create the other thread
    thread_info m_info;
    volatile bool m_bShouldBeDestructed;
 
 
    ostream_type & m_underlyingLog;
    std::queue< string_type*> m_astrMessages;
};
 
 
 
 
// forward declaration
template< class char_type, class traits_type = std::char_traits< char_type> >
    class basic_thread_safe_log;
 
 
 
 
template< class char_type, class traits_type = std::char_traits< char_type> >
    class basic_internal_thread_safe_log
{
    typedef std::basic_ostream< char_type, traits_type> ostream_type;
    friend class basic_thread_safe_log< char_type, traits_type>;
 
 
    // non-copyiable
    typedef basic_internal_thread_safe_log< char_type, traits_type> this_class;
    basic_internal_thread_safe_log( const this_class &);
    this_class & operator=( this_class &);
 
 
 
 
public:
    basic_internal_thread_safe_log( ostream_type & underlyingLog)
        : m_underlyingLog( underlyingLog),
          m_writer( underlyingLog)
    {}
 
 
    ~basic_internal_thread_safe_log()
    {}
 
 
    void write_message( const std::basic_string< char_type, traits_type> & str)
    { m_writer.add_message( str); }
 
 
    void copy_state_to( ostream_type & dest) const
    {
        CAutoLockUnlock locker( m_writer.cs());
       dest.copyfmt( m_underlyingLog);
        dest.setstate( m_underlyingLog.rdstate());
    }
 
 
    void copy_state_from( const ostream_type & src)
    {
        CAutoLockUnlock locker( m_writer.cs());
        m_underlyingLog.copyfmt( src);
        m_underlyingLog.setstate( m_underlyingLog.rdstate());
    }
 
 
private:
    ostream_type & m_underlyingLog;
    thread_safe_log_writer< char_type, traits_type> m_writer;
};
 
 
typedef basic_internal_thread_safe_log< char> internal_thread_safe_log;
typedef basic_internal_thread_safe_log< wchar_t> winternal_thread_safe_log;
 
 
 
 
 
 
template< class char_type, class traits_type>
    class basic_thread_safe_log
    // *** protected, not public !!!
    : protected basic_message_handler_log< char_type, traits_type>
{
    typedef std::basic_ostream< char_type, traits_type> ostream_type;
    typedef basic_internal_thread_safe_log< char_type, traits_type> internal_type;
public:
    basic_thread_safe_log( internal_type & tsLog)
        : m_tsLog( tsLog)
    {
        // get underlying stream state
        tsLog.copy_state_to( ts() );
    }
  
    basic_thread_safe_log( const basic_thread_safe_log< char_type, traits_type> & from)
        : m_tsLog( from.m_tsLog),
          // ... on some platforms, a std::ostream base copy-constructor
          //    might be defined as private...
          basic_message_handler_log< char_type, traits_type>()
    {
        // get underlying stream state
        m_tsLog.copy_state_to( ts() );
    }
 
 
    ~basic_thread_safe_log()
    {
        // copy state to underlying stream
        m_tsLog.copy_state_from( ts() );
    }
 
 
    // get base class - to which we can write
    std::basic_ostream< char_type, traits_type> & ts()
    { return *this; }
 
 
protected:
    virtual void on_new_message( const string_type & str)
    { m_tsLog.write_message( str); }
 
 
private:
    internal_type  & m_tsLog;
};
 
 
 
 
 
 
typedef basic_thread_safe_log< char> thread_safe_log;
typedef basic_thread_safe_log< wchar_t> wthread_safe_log;
 
 
 
 
 
 
//////////////////////////////////////////////////////////
// Test
 
 
#include <iostream>
#include <fstream>
#include <iomanip>
 
 
const int THREADS_COUNT = 200;
const int WRITES_PER_THREAD = 500;
 
 
thread_safe_log get_log()
{
    static std::ofstream out( "out.txt");
    static internal_thread_safe_log log( out);
    return thread_safe_log( log);
}
 
 
LONG nRemainingThreads = THREADS_COUNT;
DWORD WINAPI WriteToLog( LPVOID lpData)
{
    int *pnThreadID = ( int *)lpData;
    // wait for all threads to be created, so that
    // we write at about the same time (stress it ;-))
    Sleep( 500);
 
 
    for ( int idx = 0; idx < WRITES_PER_THREAD; idx++)
    {
        get_log().ts() << "writing double: " << 5.23 << std::endl;
        get_log().ts() << "message " << idx << " from thread " << *pnThreadID << std::endl;
        // ... get other threads a chance to write
        Sleep( 1);
 
 
        if ( ( idx == 10) && ( *pnThreadID == 10))
        {
            // from now on, '5.23' will be written as '5,23'
            // (german locale)
            std::locale loc = std::locale( "german");
            get_log().ts().imbue( loc);
        }
  
    }
 
 
    InterlockedDecrement( &nRemainingThreads);
    delete pnThreadID;
    return 0;
}
 
 
int main(int argc, char* argv[])
{
    // make sure the statics are initialized
    get_log();
    for ( int idx = 0; idx < THREADS_COUNT; ++idx)
    {
        DWORD dwThreadID;
        CreateThread( 0, 0, WriteToLog, new int( idx), 0, &dwThreadID);
    }
 
 
    // wait for all threads to end
   while ( true)
    {
        InterlockedIncrement( &nRemainingThreads);
        if ( InterlockedDecrement( &nRemainingThreads) == 0)
            break;
        Sleep( 100);
    }
    return 0;
}