//
// LDASTools AL - A library collection to provide an abstraction layer
//
// Copyright (C) 2018 California Institute of Technology
//
// LDASTools AL is free software; you may redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 (GPLv2) of the
// License or at your discretion, any later version.
//
// LDASTools AL is distributed in the hope that it will be useful, but
// without any warranty or even the implied warranty of merchantability
// or fitness for a particular purpose. See the GNU General Public
// License (GPLv2) for more details.
//
// Neither the names of the California Institute of Technology (Caltech),
// The Massachusetts Institute of Technology (M.I.T), The Laser
// Interferometer Gravitational-Wave Observatory (LIGO), nor the names
// of its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// You should have received a copy of the licensing terms for this
// software included in the file LICENSE located in the top-level
// directory of this package. If you did not, you can view a copy at
// http://dcc.ligo.org/M1500244/LICENSE
//

#include <ldastoolsal_config.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>

#include <cstring>

#include <iomanip>
#include <iostream>
#include <set>
#include <sstream>

#include "ldastoolsal/AtExit.hh"
#include "ldastoolsal/DeadLockDetector.hh"

#if DEAD_LOCK_DETECTOR_ENABLED

using LDASTools::AL::DeadLockDetector;
using LDASTools::AL::MutexLock;
using LDASTools::AL::ReadWriteLock;

extern "C" {
static int DEAD_LOCK_DETECTOR_EXTRA_LOGGING = 0;
}

namespace
{
    typedef struct timeval time_stamp_type;

    enum p_lock_type
    {
        UNKNOWN_LOCK_TYPE,
        MUTEX_LOCK_TYPE,
        RW_LOCK_TYPE
    };
    enum
    {
        DEAD_LOCK_DEBUGGING_OFF = 0,
        DEAD_LOCK_DEBUGGING_TRACK_ACTIVE_LOCKS = 1,
        DEAD_LOCK_DEBUGGING_TRACK_RELEASED_LOCKS = 2,
        DEAD_LOCK_DEBUGGING_VERBOSE_MEMORY = 0,
        DEAD_LOCK_DEBUGGING_VERBOSE_MEMORY_STRING = 1000,
        DEAD_LOCK_DEBUGGING_VERBOSE_FILE = 2000
    };

    bool init( );

    typedef struct
    {
        time_stamp_type s_creation;
        std::string     s_description;
    } known_thread_info_type;

    typedef std::map< DeadLockDetector::thread_type, known_thread_info_type >
        known_thread_container_type;

    static std::ostringstream DeadLockInfo;
    static char*              CDeadLockInfo = (char*)NULL;
    static unsigned int       CDeadLockInfoSize = 0;

    static known_thread_container_type known_threads;

    inline int
    LEVEL_MASK( int X )
    {
        return X % 1000;
    }

    inline int
    VERBOSE_MASK( int X )
    {
        return X / 1000;
    }

    static std::ostream& dump_time_stamp( std::ostream&          Stream,
                                          const time_stamp_type& Time );

    inline void
    DumpExtraLogging( )
    {
        static int debug_counter = 0;

        int verboseness = VERBOSE_MASK( DEAD_LOCK_DETECTOR_EXTRA_LOGGING );
        if ( DEAD_LOCK_DETECTOR_EXTRA_LOGGING )
        {
            DeadLockInfo.str( "" );
            DeadLockDetector::DumpWithoutLocking( DeadLockInfo );
            if ( verboseness >=
                 VERBOSE_MASK( DEAD_LOCK_DEBUGGING_VERBOSE_MEMORY_STRING ) )
            {
                if ( DeadLockInfo.str( ).length( ) > CDeadLockInfoSize )
                {
                    free( CDeadLockInfo );
                    CDeadLockInfo = ::strdup( DeadLockInfo.str( ).c_str( ) );
                    CDeadLockInfoSize = DeadLockInfo.str( ).length( ) + 1;
                }
                else
                {
                    ::strcpy( CDeadLockInfo, DeadLockInfo.str( ).c_str( ) );
                }
                if ( ( verboseness >=
                       VERBOSE_MASK( DEAD_LOCK_DEBUGGING_VERBOSE_FILE ) ) ||
                     ( debug_counter ) )
                {
                    if ( debug_counter )
                        --debug_counter;

                    std::cerr << CDeadLockInfo << std::endl;
                }
            }
        }
    }

    //---------------------------------------------------------------------

    typedef DeadLockDetector::thread_type     thread_type;
    typedef DeadLockDetector::state_type      state_type;
    typedef DeadLockDetector::lock_class_type lock_class_type;
    typedef struct
    {
        std::string     s_filename;
        int             s_linenum;
        time_stamp_type s_time_stamp;
    } state_change_type;

    struct lock_info_type
    {
        enum
        {
            STATE_CHANGE_INITIALIZED = 0,
            STATE_CHANGE_PENDING = 1,
            STATE_CHANGE_ACQUIRED = 2,
            STATE_CHANGE_RELEASED = 3
        };

        const void*                s_lock_address;
        lock_class_type            s_lock_type;
        state_type                 s_lock_state;
        state_change_type          s_changes[ 3 ];
        int                        s_error;
        mutable std::ostringstream s_text;

        std::ostream& Dump( std::ostream&      Stream,
                            const std::string& Indent ) const;
        std::ostream& DumpStateChange( std::ostream&      Stream,
                                       const std::string& Indent,
                                       int                Offset ) const;

        lock_info_type( ) : s_error( 0 )
        {
        }

        lock_info_type( const lock_info_type& Src )
        {
            if ( this != &Src )
            {
                *this = Src;
            }
        }

        const lock_info_type&
        operator=( const lock_info_type& RHS )
        {
            s_lock_address = RHS.s_lock_address;
            s_lock_type = RHS.s_lock_type;
            s_lock_state = RHS.s_lock_state;
            s_changes[ 0 ] = RHS.s_changes[ 0 ];
            s_changes[ 1 ] = RHS.s_changes[ 1 ];
            s_changes[ 2 ] = RHS.s_changes[ 2 ];
            s_error = RHS.s_error;
            s_text.str( RHS.s_text.str( ) );
            return *this;
        }

        inline std::ostream&
        Dump( const std::string& Indent ) const
        {
            return Dump( s_text, Indent );
        }
    };

    typedef std::list< lock_info_type >             lock_list_type;
    typedef std::map< const void*, lock_info_type > lock_map_type;

    typedef struct
    {
        lock_list_type s_active;
        lock_map_type  s_historical;
    } lock_list_chain_type;

    typedef std::list< thread_type > thread_container_type;

    struct lock_info_search_pred
    {
        lock_info_search_pred( const void* Key ) : s_key( Key ){};

        bool
        operator( )( const lock_info_type& Ref )
        {
            return ( Ref.s_lock_address == s_key );
        };

    private:
        const void* s_key;
    };

    struct mutex_info_type
    {
        void*                 s_lock_addr;
        p_lock_type           s_type;
        thread_type           s_active_thread;
        time_stamp_type       s_active_time_stamp;
        thread_type           s_released_thread;
        time_stamp_type       s_released_time_stamp;
        mutable std::string   s_msg;
        thread_container_type s_threads;

        mutex_info_type( )
            : s_type( UNKNOWN_LOCK_TYPE ), s_active_thread( 0 ),
              s_released_thread( 0 )
        {
            s_active_time_stamp.tv_sec = (time_t)0;
            s_active_time_stamp.tv_usec = (suseconds_t)0;
            s_released_time_stamp.tv_sec = (time_t)0;
            s_released_time_stamp.tv_usec = (suseconds_t)0;
        }

        void
        SetLockType( lock_class_type Type )
        {
            switch ( Type )
            {
            case DeadLockDetector::MUTEX:
                s_type = MUTEX_LOCK_TYPE;
                break;
            case DeadLockDetector::RW_READ:
            case DeadLockDetector::RW_WRITE:
                s_type = RW_LOCK_TYPE;
                break;
            default:
                s_type = UNKNOWN_LOCK_TYPE;
            }
        }

        void
        Dump( ) const
        {
            std::ostringstream msg;

            msg << "Lock Addr: " << s_lock_addr << " Type: "
                << ( ( s_type == MUTEX_LOCK_TYPE )
                         ? "MUTEX_LOCK_TYPE"
                         : ( ( s_type == RW_LOCK_TYPE )
                                 ? "RW_LOCK_TYPE"
                                 : "UNKNOWN_LOCK_TYPE" ) )
                << " Thread of Lock Holder: " << s_active_thread << " at: ";
            dump_time_stamp( msg, s_active_time_stamp );
            msg << " Released: " << s_released_thread << " at: ";
            dump_time_stamp( msg, s_released_time_stamp );
            msg << " Threads:";
            for ( thread_container_type::const_iterator
                      cur_thread = s_threads.begin( ),
                      last_thread = s_threads.end( );
                  cur_thread != last_thread;
                  ++cur_thread )
            {
                msg << " " << *cur_thread;
            }
            msg << std::endl;
            s_msg = msg.str( );
        }
    };

#if DEAD_LOCK_DETECTOR_USES_UNORDERED_MAP
    struct void_ptr_hash : std::unary_function< const void*, size_t >
    {
        size_t
        operator( )( const void* Key ) const
        {
#if SIZEOF_VOIDP == SIZEOF_LONG_LONG
            typedef unsigned long long hash_type;
#elif SIZEOF_VOIDP == SIZEOF_LONG
            typedef unsigned long hash_type;
#endif
            union
            {
                const void* s_ptr;
                hash_type   s_hash_key;
            } hk;

            LDASTools::AL::hash< hash_type > h;

            hk.s_ptr = Key;
            hk.s_hash_key >>=
                3; // Shift down since data should be byte aligned.
            return h( hk.s_hash_key );
        }
    };

    struct thread_type_hash : std::unary_function< thread_type, size_t >
    {
        size_t
        operator( )( thread_type Key ) const
        {
#if SIZEOF_VOIDP == SIZEOF_LONG_LONG
            typedef unsigned long long hash_type;
#elif SIZEOF_VOIDP == SIZEOF_LONG
            typedef unsigned long hash_type;
#endif
            union
            {
                thread_type s_thread;
                hash_type   s_hash_key;
            } hk;

            LDASTools::AL::hash< hash_type > h;

            hk.s_thread = Key;
            return h( hk.s_hash_key );
        }
    };

    typedef LDASTools::AL::
        unordered_map< thread_type, lock_list_type, thread_type_hash >
            threads_container_type;
    typedef LDASTools::AL::
        unordered_map< void*, std::set< thread_type >, void_ptr_hash >
            locks_container_type;

#else /* DEAD_LOCK_DETECTOR_USES_UNORDERED_MAP */
    typedef std::map< thread_type, lock_list_chain_type >
                                               threads_container_type;
    typedef std::map< void*, mutex_info_type > locks_container_type;

#endif /* DEAD_LOCK_DETECTOR_USES_UNORDERED_MAP */

    static MutexLock::baton_type  m_lock( __FILE__, __LINE__, false );
    static threads_container_type m_thread_list;
    static locks_container_type   m_lock_list;

    static std::ostream& dump_known_thread_data( std::ostream& Stream,
                                                 thread_type   Thread );

    static const char* const lock_type_name( lock_class_type Type );

    static const char* const lock_state_name( state_type Name );

    static void now( time_stamp_type& Time );

    static void thread_purge( thread_type Thread, bool WarnNonEmpty = false );

    static bool would_deadlock( );

} // namespace

extern "C" {
void
PrintExtraLogging( )
{
    int old = DEAD_LOCK_DETECTOR_EXTRA_LOGGING;

    DEAD_LOCK_DETECTOR_EXTRA_LOGGING =
        DEAD_LOCK_DEBUGGING_VERBOSE_FILE | DEAD_LOCK_DETECTOR_EXTRA_LOGGING;
    DumpExtraLogging( );
    DEAD_LOCK_DETECTOR_EXTRA_LOGGING = old;
}
}

namespace LDASTools
{
    namespace AL
    {
        //---------------------------------------------------------------------
        //---------------------------------------------------------------------
        void
        DeadLockDetector::Dump( )
        {
            try
            {
                m_lock.Lock( __FILE__, __LINE__ );
                DumpWithoutLocking( );
                m_lock.Unlock( __FILE__, __LINE__ );
            }
            catch ( ... )
            {
                m_lock.Unlock( __FILE__, __LINE__ );
                throw;
            }
        }

        std::ostream&
        DeadLockDetector::DumpWithoutLocking( std::ostream& Stream )
        {
            Stream << "==== THREAD LIST ====" << std::endl;
            for ( threads_container_type::const_iterator
                      cur = m_thread_list.begin( ),
                      last = m_thread_list.end( );
                  cur != last;
                  cur++ )
            {
                if ( ( cur->second.s_active.size( ) > 0 ) ||
                     ( cur->second.s_historical.size( ) > 0 ) )
                {
                    Stream << "Thread: " << cur->first;
                    dump_known_thread_data( Stream, cur->first );
                    Stream << std::endl;
                    if ( cur->second.s_active.size( ) > 0 )
                    {
                        Stream << "  --- Active ---" << std::endl;
                        for ( lock_list_type::const_iterator
                                  cur_lock = cur->second.s_active.begin( ),
                                  last_lock = cur->second.s_active.end( );
                              cur_lock != last_lock;
                              ++cur_lock )
                        {
                            Stream << cur_lock->s_text.str( );
                        }
                    }
                    if ( cur->second.s_historical.size( ) > 0 )
                    {
                        Stream << "  --- Historical ---" << std::endl;
                        for ( lock_map_type::const_iterator
                                  cur_lock = cur->second.s_historical.begin( ),
                                  last_lock = cur->second.s_historical.end( );
                              cur_lock != last_lock;
                              ++cur_lock )
                        {
                            Stream << cur_lock->second.s_text.str( );
                        }
                    }
                }
            }
            Stream << "==== LOCK LIST ====" << std::endl;
            for ( locks_container_type::const_iterator
                      cur = m_lock_list.begin( ),
                      last = m_lock_list.end( );
                  cur != last;
                  ++cur )
            {
                if ( cur->second.s_threads.size( ) <= 0 )
                {
                    continue;
                }
                Stream << cur->second.s_msg;
            }
            return Stream;
        }

        int
        DeadLockDetector::SetDebugging( int Value )
        {
            try
            {
                m_lock.Lock( __FILE__, __LINE__ );
                int retval = DEAD_LOCK_DETECTOR_EXTRA_LOGGING;

                if ( Value < 0 )
                {
                    Value = 0;
                }

                DEAD_LOCK_DETECTOR_EXTRA_LOGGING = Value;
                int level = LEVEL_MASK( Value );

                if ( level < LEVEL_MASK( retval ) )
                {
                    //---------------------------------------------------------------
                    // Remove excessive debugging information
                    //---------------------------------------------------------------
                    if ( level < DEAD_LOCK_DEBUGGING_TRACK_RELEASED_LOCKS )
                    {
                        for ( threads_container_type::iterator
                                  cur_thread = m_thread_list.begin( ),
                                  last_thread = m_thread_list.end( );
                              cur_thread != last_thread;
                              ++cur_thread )
                        {
                            cur_thread->second.s_historical.erase(
                                cur_thread->second.s_historical.begin( ),
                                cur_thread->second.s_historical.end( ) );
                        }
                    }
                    if ( level == DEAD_LOCK_DEBUGGING_OFF )
                    {
                        //---------------------------------------------------------------
                        // Need to purge existing debugging information.
                        //---------------------------------------------------------------
                        m_thread_list.erase( m_thread_list.begin( ),
                                             m_thread_list.end( ) );
                        m_lock_list.erase( m_lock_list.begin( ),
                                           m_lock_list.end( ) );
                        if ( CDeadLockInfo )
                        {
                            free( CDeadLockInfo );
                            CDeadLockInfo = (char*)NULL;
                        }
                    }
                }
                m_lock.Unlock( __FILE__, __LINE__ );
                return retval;
            }
            catch ( ... )
            {
                m_lock.Unlock( __FILE__, __LINE__ );
                throw;
            }
        }

        void
        DeadLockDetector::SetState( lock_class_type       Lock,
                                    MutexLock::baton_type LockPtr,
                                    state_type            State,
                                    const char*           Filename,
                                    unsigned int          Linenum )
        {
            state_info_type info(
                LockPtr.Handle( ), Lock, State, Filename, Linenum );

            set_state( info );
        }

#if HAVE_PTHREAD_RW_LOCK
        inline void
        DeadLockDetector::SetState( lock_class_type           Lock,
                                    ReadWriteLock::baton_type LockPtr,
                                    state_type                State,
                                    const char*               Filename,
                                    unsigned int              Linenum )
        {
            state_info_type info(
                LockPtr.Handle( ), Lock, State, Filename, Linenum );

            set_state( info );
        }
#endif /* HAVE_PTHREAD_RW_LOCK */

        void
        DeadLockDetector::ThreadRegister( thread_type        Thread,
                                          const std::string& Desc )
        {
            try
            {
                m_lock.Lock( __FILE__, __LINE__ );

                known_thread_container_type::iterator i =
                    known_threads.find( Thread );

                if ( i != known_threads.end( ) )
                {
                    thread_purge( Thread );
                }
                known_thread_container_type::mapped_type& n =
                    known_threads[ Thread ];
                now( n.s_creation );
                n.s_description = Desc;

                m_lock.Unlock( __FILE__, __LINE__ );
            }
            catch ( ... )
            {
                m_lock.Unlock( __FILE__, __LINE__ );
                throw;
            }
        }

        void
        DeadLockDetector::ThreadUnregister( thread_type Thread )
        {
            try
            {
                m_lock.Lock( __FILE__, __LINE__ );

                thread_purge( Thread, true );

                m_lock.Unlock( __FILE__, __LINE__ );
            }
            catch ( ... )
            {
                m_lock.Unlock( __FILE__, __LINE__ );
                throw;
            }
        }

        void
        DeadLockDetector::set_state( state_info_type& Info )
        {
            if ( LEVEL_MASK( DEAD_LOCK_DETECTOR_EXTRA_LOGGING ) ==
                 DEAD_LOCK_DEBUGGING_OFF )
            {
                return;
            }
            try
            {
                m_lock.Lock( __FILE__, __LINE__ );

                static int is_initialized = false;
                bool       logged = false;

                if ( is_initialized == false )
                {
                    is_initialized = init( );
                }

                if ( ( Info.s_state == PENDING ) && ( would_deadlock( ) ) )
                {
                    // throw DeadLockException( );
                }
                //-------------------------------------------------------------------
                // Modify lists with information about the lock
                //-------------------------------------------------------------------
                thread_type self = pthread_self( );
                void*       lock = Info.s_lock;

                switch ( Info.s_state )
                {
                case INITIALIZED:
                    break;
                case PENDING:
                {
                    //-------------------------------------------------------------
                    // Remove from historical list
                    //-------------------------------------------------------------
                    if ( LEVEL_MASK( DEAD_LOCK_DETECTOR_EXTRA_LOGGING ) >=
                         DEAD_LOCK_DEBUGGING_TRACK_RELEASED_LOCKS )
                    {
                        lock_map_type::iterator lmi =
                            m_thread_list[ self ].s_historical.find( lock );
                        if ( lmi != m_thread_list[ self ].s_historical.end( ) )
                        {
                            m_thread_list[ self ].s_historical.erase( lmi );
                        }
                    }
                    //-------------------------------------------------------------
                    // Add to active list
                    //-------------------------------------------------------------
                    lock_info_type li;

                    li.s_lock_address = lock;
                    li.s_lock_type = Info.s_lock_type;
                    li.s_lock_state = Info.s_state;
                    li.s_changes[ lock_info_type::STATE_CHANGE_PENDING ]
                        .s_filename = Info.s_filename;
                    li.s_changes[ lock_info_type::STATE_CHANGE_PENDING ]
                        .s_linenum = Info.s_linenumber;
                    now( li.s_changes[ lock_info_type::STATE_CHANGE_PENDING ]
                             .s_time_stamp );
                    li.Dump( "  " );
                    m_thread_list[ self ].s_active.push_back( li );

                    //-------------------------------------------------------------
                    // Add to cross reference list
                    //-------------------------------------------------------------
                    m_lock_list[ lock ].s_lock_addr = lock;
                    m_lock_list[ lock ].s_threads.push_back( self );
                    m_lock_list[ lock ].SetLockType( Info.s_lock_type );
                    m_lock_list[ lock ].Dump( );
                }
                break;
                case ACQUIRED:
                {
                    if ( ( m_thread_list[ self ].s_active.size( ) > 0 ) &&
                         ( m_thread_list[ self ]
                               .s_active.back( )
                               .s_lock_address == lock ) )
                    {
                        lock_list_type::reference ref =
                            m_thread_list[ self ].s_active.back( );

                        ref.s_lock_state = Info.s_state;
                        ref.s_changes[ lock_info_type::STATE_CHANGE_ACQUIRED ]
                            .s_filename = Info.s_filename;
                        ref.s_changes[ lock_info_type::STATE_CHANGE_ACQUIRED ]
                            .s_linenum = Info.s_linenumber;
                        now( ref.s_changes
                                 [ lock_info_type::STATE_CHANGE_ACQUIRED ]
                                     .s_time_stamp );
                        ref.Dump( "  " );
                    }
                    m_lock_list[ lock ].s_active_thread = self;
                    now( m_lock_list[ lock ].s_active_time_stamp );
                    m_lock_list[ lock ].Dump( );
                }
                break;
                case ACQUISITION_ERROR:
                case RELEASED:
                {
                    if ( ( m_thread_list[ self ].s_active.size( ) > 0 ) &&
                         ( m_thread_list[ self ]
                               .s_active.back( )
                               .s_lock_address == lock ) )
                    {
                        //-----------------------------------------------------------
                        // Fill in information
                        //-----------------------------------------------------------
                        lock_list_type::reference ref =
                            m_thread_list[ self ].s_active.back( );

                        ref.s_lock_state = Info.s_state;
                        ref.s_changes[ lock_info_type::STATE_CHANGE_RELEASED ]
                            .s_filename = Info.s_filename;
                        ref.s_changes[ lock_info_type::STATE_CHANGE_RELEASED ]
                            .s_linenum = Info.s_linenumber;
                        now( ref.s_changes
                                 [ lock_info_type::STATE_CHANGE_RELEASED ]
                                     .s_time_stamp );
                        if ( Info.s_state == ACQUISITION_ERROR )
                        {
                            //---------------------------------------------------------
                            // Take care of what is unique to ACQUISITION_ERROR
                            //---------------------------------------------------------
                            ref.s_error = Info.s_error;
                        }
                        ref.Dump( "  " );
                        DumpExtraLogging( );
                        logged = true;

                        if ( LEVEL_MASK( DEAD_LOCK_DETECTOR_EXTRA_LOGGING ) >=
                             DEAD_LOCK_DEBUGGING_TRACK_RELEASED_LOCKS )
                        {
                            //-----------------------------------------------------------
                            // Move to historical list
                            //-----------------------------------------------------------
                            m_thread_list[ self ].s_historical[ lock ] = ref;
                        }

                        //-----------------------------------------------------------
                        // Remove from active list
                        //-----------------------------------------------------------
                        m_thread_list[ self ].s_active.pop_back( );
                    }

                    //-------------------------------------------------------------
                    // Update information about active cross references
                    //-------------------------------------------------------------
                    locks_container_type::iterator il =
                        m_lock_list.find( lock );
                    if ( il != m_lock_list.end( ) )
                    {
                        il->second.s_released_thread = self;
                        now( il->second.s_released_time_stamp );
                        il->second.s_threads.remove( self );
                        if ( il->second.s_threads.size( ) == 0 )
                        {
                            //---------------------------------------------------------
                            // No threads are holding this lock, just get rid of
                            //   the information.
                            //---------------------------------------------------------
                            m_lock_list.erase( il );
                        }
                        else
                        {
                            //---------------------------------------------------------
                            // State has changed. Update string representation
                            //---------------------------------------------------------
                            il->second.Dump( );
                        }
                    }
                }
                break;
                } // switch
                if ( !logged )
                {
                    DumpExtraLogging( );
                }
                m_lock.Unlock( __FILE__, __LINE__ );
            }
            catch ( ... )
            {
                m_lock.Unlock( __FILE__, __LINE__ );
                throw;
            }
        }
    } // namespace AL

} // namespace LDASTools

namespace
{
    static std::ostream&
    dump_known_thread_data( std::ostream& Stream, thread_type Thread )
    {
        known_thread_container_type::iterator i = known_threads.find( Thread );

        if ( i == known_threads.end( ) )
        {
            Stream << " Thread not being tracked";
        }
        else
        {
            Stream << " Creation: ";
            dump_time_stamp( Stream, i->second.s_creation );
            Stream << " Desc: " << i->second.s_description;
        }

        return Stream;
    }

    static std::ostream&
    dump_time_stamp( std::ostream& Stream, const time_stamp_type& Time )
    {
        std::ostringstream msg;

        msg << Time.tv_sec << "." << std::setfill( '0' ) << std::setw( 6 )
            << Time.tv_usec;
        Stream << msg.str( );

        return Stream;
    }

    const char* const
    lock_type_name( lock_class_type Type )
    {
        switch ( Type )
        {
        case DeadLockDetector::MUTEX:
            return "MUTEX";
            break;
        case DeadLockDetector::RW_READ:
            return "READ_WRITE(READ)";
            break;
        case DeadLockDetector::RW_WRITE:
            return "READ_WRITE(WRITE)";
            break;
        case DeadLockDetector::UNSPECIFIED:
            return "UNSPECIFIED";
            break;
        }
        return "UNKNOWN";
    }

    const char* const
    lock_state_name( state_type Name )
    {
        switch ( Name )
        {
        case DeadLockDetector::INITIALIZED:
            return "INITIALIZED";
            break;
        case DeadLockDetector::PENDING:
            return "PENDING";
            break;
        case DeadLockDetector::ACQUIRED:
            return "ACQUIRED";
            break;
        case DeadLockDetector::RELEASED:
            return "RELEASED";
            break;
        case DeadLockDetector::ACQUISITION_ERROR:
            return "ACQUISITION_ERROR";
            break;
        }
        return "UNKNOWN";
    }

    static void
    now( time_stamp_type& TimeStamp )
    {
        gettimeofday( &TimeStamp, 0 );
    }

    void
    thread_purge( thread_type Thread, bool WarnNonEmpty )
    {
        known_thread_container_type::iterator kti =
            known_threads.find( Thread );
        threads_container_type::iterator tli = m_thread_list.find( Thread );

        //---------------------------------------------------------------------
        // Erase information about locks associated with a thread
        //---------------------------------------------------------------------
        if ( tli != m_thread_list.end( ) )
        {
            //-----------------------------------------------------------------
            // Remove thread info from lock lists
            //-----------------------------------------------------------------
            threads_container_type::mapped_type& l = tli->second;

            if ( ( WarnNonEmpty ) && ( l.s_active.size( ) > 0 ) )
            {
                //---------------------------------------------------------------
                // Head of report about locks being held
                //---------------------------------------------------------------
                std::cerr << "ERROR: Thread " << Thread
                          << " is exiting while holding locks!!!" << std::endl;
                DeadLockDetector::DumpWithoutLocking( std::cerr )
                    << std::endl
                    << "  *** End of Thread error" << std::endl;
            }
            for ( lock_list_type::iterator cur = l.s_active.begin( ),
                                           last = l.s_active.end( );
                  cur != last;
                  ++cur )
            {
                locks_container_type::iterator l =
                    m_lock_list.find( (void*)cur->s_lock_address );
                if ( l != m_lock_list.end( ) )
                {
                    if ( WarnNonEmpty )
                    {
                        std::cerr << "  " << l->second.s_msg;
                    }
                    l->second.s_threads.remove( Thread );
                }
            }
            //-----------------------------------------------------------------
            // Release the thread list information
            //-----------------------------------------------------------------
            m_thread_list.erase( tli );
        }
        //-------------------------------------------------------------------
        // Remove detailed information about the thread
        //-------------------------------------------------------------------
        if ( kti != known_threads.end( ) )
        {
            known_threads.erase( kti );
        }
    }

    bool
    would_deadlock( )
    {
        return false;
    }

    bool
    init( )
    {
        static bool ready = false;
        if ( !ready )
        {
            known_thread_container_type::mapped_type& n =
                known_threads[ pthread_self( ) ];
            n.s_description = "Main Thread";
            now( n.s_creation );
            ready = true;
        }

        return ready;
    }

    std::ostream&
    lock_info_type::Dump( std::ostream&      Stream,
                          const std::string& Indent ) const
    {
        Stream << Indent << "Mutex: " << s_lock_address
               << " Type: " << lock_type_name( s_lock_type )
               << " State: " << lock_state_name( s_lock_state ) << std::endl;
        if ( s_lock_state == DeadLockDetector::ACQUISITION_ERROR )
        {
            Stream << Indent << Indent << "Error Info: "
                   << " Error: " << s_error
                   << " Error Message: " << ::strerror( s_error ) << std::endl;
            DumpStateChange( Stream, Indent, STATE_CHANGE_PENDING );
            DumpStateChange( Stream, Indent, STATE_CHANGE_RELEASED );
        }
        else
        {
            for ( int x = 0; x <= s_lock_state; ++x )
            {
                DumpStateChange( Stream, Indent, x );
            }
        }
        return Stream;
    }

    std::ostream&
    lock_info_type::DumpStateChange( std::ostream&      Stream,
                                     const std::string& Indent,
                                     int                Offset ) const
    {
        Stream << Indent << Indent
               << "State Info: " << lock_state_name( (state_type)Offset )
               << std::endl
               << Indent << Indent << Indent << "TimeStamp: ";
        dump_time_stamp( Stream, s_changes[ Offset ].s_time_stamp );
        Stream << std::endl
               << Indent << Indent << Indent
               << "File: " << s_changes[ Offset ].s_filename
               << " Line: " << s_changes[ Offset ].s_linenum << std::endl;
        return Stream;
    }
} // namespace
#endif /* DEAD_LOCK_DETECTOR_ENABLED */
