//
// 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 <pthread.h>
#include <unistd.h>
#include <signal.h> // pthread_kill

#include <cassert>

#include <iostream>
#include <sstream>
#include <stdexcept>
#include <unordered_map>

#include "ldastoolsal/MemChecker.hh"
#include "ldastoolsal/IOLock.hh"
/* #include "ldastoolsal/unordered_map.hh" */
#include "ldastoolsal/ldasexception.hh"
#include "ldastoolsal/ReadWriteLock.hh"
#include "ldastoolsal/Thread.hh"
#include "ldastoolsal/SignalHandler.hh"
#include "ldastoolsal/Singleton.hh"

#include "Thread.icc"

#if ALT_PTHREAD_STACK_MIN
#undef PTHREAD_STACK_MIN
#define PTHREAD_STACK_MIN ALT_PTHREAD_STACK_MIN
#endif /* ALT_PTHREAD_STACK_MIN */

using LDASTools::AL::MemChecker;
using LDASTools::AL::MutexLock;
using LDASTools::AL::ReadWriteLock;
using LDASTools::AL::SignalHandler;
using LDASTools::AL::Thread;
using std::unordered_map;

#define LOCAL_VERBOSE_DEBUGGING 0

#if LOCAL_VERBOSE_DEBUGGING > 0
#define VERBOSE_DEBUGGING( b ) std::cerr << b
#else
#define VERBOSE_DEBUGGING( b )
#endif

#if LOCAL_VERBOSE_DEBUGGING >= 10
#define VERBOSE_DEBUGGING_10( b ) VERBOSE_DEBUGGING( b )
#else
#define VERBOSE_DEBUGGING_10( b )
#endif
#if LOCAL_VERBOSE_DEBUGGING >= 20
#define VERBOSE_DEBUGGING_20( b ) VERBOSE_DEBUGGING( b )
#else
#define VERBOSE_DEBUGGING_20( b )
#endif
#if LOCAL_VERBOSE_DEBUGGING >= 30
#define VERBOSE_DEBUGGING_30( b ) VERBOSE_DEBUGGING( b )
#else
#define VERBOSE_DEBUGGING_30( b )
#endif

namespace
{
    static size_t stack_size = PTHREAD_STACK_MIN + ( 64 * 1024 );

    static MutexLock::baton_type
    stack_size_baton( )
    {
        static MutexLock::baton_type baton;

        return baton;
    }

    static class Wakeup_ : public SignalHandler::Callback
    {
    public:
        typedef SignalHandler::signal_type signal_type;

        virtual void
        SignalCallback( signal_type Signal )
        {
        }
    } wakeup;
} // namespace

class thread_registry : public LDASTools::AL::Singleton< thread_registry >
{
public:
    typedef Thread::thread_type::handle_type key_type;
    typedef Thread*                          value_type;

    static void Add( key_type Key, value_type Value );

    static value_type Get( key_type Key );

    static void Remove( key_type Key );

    static key_type Self( );

private:
    //  friend class Thread::Self;

    typedef unordered_map< key_type, value_type, std::hash< key_type > >
        thread_registry_type;

    static ReadWriteLock::baton_type baton_;
    static thread_registry_type      storage_;

    void add( key_type Key, value_type Value );

    value_type get( key_type Key );

    void remove( key_type Key );
}; // class thread_registry

SINGLETON_INSTANCE_DEFINITION(
    LDASTools::AL::SingletonHolder< ::thread_registry > )

ReadWriteLock::baton_type             thread_registry::baton_;
thread_registry::thread_registry_type thread_registry::storage_;

void
thread_registry::Add( key_type Key, value_type Value )
{
    Instance( ).add( Key, Value );
}

thread_registry::value_type
thread_registry::Get( key_type Key )
{
    return Instance( ).get( Key );
}

void
thread_registry::Remove( key_type Key )
{
    Instance( ).remove( Key );
}

inline thread_registry::key_type
thread_registry::Self( )
{
    return pthread_self( );
}

void
thread_registry::add( thread_registry::key_type   Key,
                      thread_registry::value_type Value )
{
    ReadWriteLock l( baton_, ReadWriteLock::WRITE, __FILE__, __LINE__ );

    storage_[ Key ] = Value;
}

thread_registry::value_type
thread_registry::get( thread_registry::key_type Key )
{
    ReadWriteLock l( baton_, ReadWriteLock ::READ, __FILE__, __LINE__ );

    thread_registry_type::const_iterator pos( storage_.find( Key ) );
    if ( pos == storage_.end( ) )
    {
        std::ostringstream msg;
        msg << "There is no managed thread with key: " << Key;

        throw std::range_error( msg.str( ) );
    }
    return pos->second;
}

void
thread_registry::remove( thread_registry::key_type Key )
{
    ReadWriteLock l( baton_, ReadWriteLock::WRITE, __FILE__, __LINE__ );

    storage_.erase( Key );
}

namespace LDASTools
{
    namespace AL
    {
        //-------------------------------------------------------------------
        // Thread::thread_type class
        //-------------------------------------------------------------------
#ifdef constexpr
        constexpr Thread::thread_type::handle_type
                  Thread::thread_type::NULL_THREAD =
                (Thread::thread_type::handle_type)0;
#endif /* constexpr */

        //-------------------------------------------------------------------
        // Thread::Self class
        //-------------------------------------------------------------------

        void
        Thread::Self::Cancel( )
        {
            thread_registry::value_type v = NULL;
            try
            {
                v = thread_registry::Get( thread_registry::Self( ) );
            }
            catch ( ... )
            {
            }
            if ( v )
            {
                v->Cancel( );
            }
        }

        void
        Thread::Self::CancellationCheck( const std::string& Header,
                                         const char*        File,
                                         const int          Line )
        {
            thread_registry::value_type v = NULL;
            try
            {
                v = thread_registry::Get( thread_registry::Self( ) );
            }
            catch ( ... )
            {
            }
            if ( v )
            {
                v->CancellationCheck( Header, File, Line );
            }
        }

        Thread::cancel_type
        Thread::Self::CancellationType( signal_type& Signal )
        {
            thread_registry::value_type v = NULL;

            try
            {
                v = thread_registry::Get( thread_registry::Self( ) );
            }
            catch ( ... )
            {
            }
            if ( v )
            {
                return v->CancellationType( Signal );
            }
            return CANCEL_EXCEPTION;
        }

        void
        Thread::Self::CancellationType( cancel_type Type, signal_type Signal )
        {
            thread_registry::value_type v = NULL;

            try
            {
                v = thread_registry::Get( thread_registry::Self( ) );
            }
            catch ( ... )
            {
            }
            if ( v )
            {
                v->CancellationType( Type, Signal );
                v->CancellationEnable( ( Type != CANCEL_IGNORE ) );
            }
        }

        Thread::thread_type
        Thread::Self::Id( )
        {
            return thread_registry::Self( );
        }
        //---------------------------------------------------------------------
        // Thread class
        //---------------------------------------------------------------------
        Thread::Thread( )
            : p( new impl ),
              m_cancel_via_signal( SignalHandler::SIGNAL_UNKNOWN ),
              m_cancel_state( false ), m_cancel_thread( false ),
              m_cancellation_type( CANCEL_IGNORE ), detached( false )
        {
        }

        Thread::~Thread( )
        {
            try
            {
                Join( );
            }
            catch ( ... )
            {
                Detach( );
            }
        }

#if WORKING
        bool
        Thread::operator==( const Thread& Source ) const
        {
            return ( Source.m_tid = m_tid );
        }
#endif /* WORKING */

        void
        Thread::Cancel( )
        {
            if ( p->tid )
            {
                MutexLock l( p->key, __FILE__, __LINE__ );

                m_cancel_thread = true;
                switch ( m_cancellation_type )
                {
                case CANCEL_ABANDON:
                    Detach( );
                    break;
                case CANCEL_EXCEPTION:
                case CANCEL_IGNORE:
                    break;
                case CANCEL_BY_SIGNAL:
                {
                    if ( m_cancel_via_signal != SignalHandler::SIGNAL_UNKNOWN )
                    {
                        l.Release( __FILE__, __LINE__ );
                        Kill( m_cancel_via_signal );
                    }
                }
                break;
                case CANCEL_ASYNCHRONOUS:
                case CANCEL_DEFERRED:
                {
                    thread_type t( p->tid );

                    l.Release( __FILE__, __LINE__ );
                    pthread_cancel( t );
                }
                break;
                case CANCEL_UNKNOWN:
                    // Should never happen
                    break;
                }
            }
        }

        //---------------------------------------------------------------------
        /// Deliver any pending cancellation requests to the calling thread.
        /// If cancellation is to be done via exception,
        /// then a cancellation exception is throw.
        /// If not, then a call to the appropriate thread library's
        /// cancellation routine is made.
        ///
        /// \note
        ///     This call must only be made by a child thread.
        //---------------------------------------------------------------------
        void
        Thread::CancellationCheck( const std::string& Header,
                                   const char*        File,
                                   const int          Line ) const
        {
            MutexLock l( p->key, __FILE__, __LINE__ );

            if ( !pthread_equal( p->tid, pthread_self( ) ) )
            {
                return;
            }
            if ( m_cancel_thread )
            {
                switch ( m_cancellation_type )
                {
                case CANCEL_ABANDON:
                case CANCEL_IGNORE:
                case CANCEL_UNKNOWN:
                    break;
                case CANCEL_BY_SIGNAL:
                case CANCEL_EXCEPTION:
                    throw cancellation( Header, File, Line );
                    break;
                case CANCEL_ASYNCHRONOUS:
                case CANCEL_DEFERRED:
                    pthread_testcancel( );
                    break;
                }
            }
        }

        void
        Thread::CancellationEnable( bool Value )
        {
            MutexLock l( p->key, __FILE__, __LINE__ );

            cancellation_enable( Value );
        }

        Thread::cancel_type
        Thread::CancellationType( signal_type& Signal ) const
        {
            MutexLock l( p->key, __FILE__, __LINE__ );

            switch ( m_cancellation_type )
            {
            case CANCEL_BY_SIGNAL:
                Signal = m_cancel_via_signal;
                break;
            default:
                Signal = SignalHandler::SIGNAL_UNKNOWN;
                break;
            }
            return m_cancellation_type;
        }

        bool
        Thread::IsCancelled( ) const
        {
            MutexLock l( p->key, __FILE__, __LINE__ );

            return m_cancel_thread;
        }

        void
        Thread::SignalCapture( SignalHandler::signal_type Sig )
        {
            SignalHandler::ThreadCaptureSignal( Sig );
        }

        void
        Thread::SignalIgnore( SignalHandler::signal_type Sig )
        {
            SignalHandler::ThreadIgnoreSignal( Sig );
        }

        inline size_t
        Thread::StackSizeDefault( )
        {
            MutexLock l( stack_size_baton( ), __FILE__, __LINE__ );

            return stack_size;
        }

        //---------------------------------------------------------------------
        /// Establish the cancellability of a thread.
        /// If a Value of true is passed, then the thread will allow
        /// cancellation as determined by the call to CancellationType.
        ///
        /// \note
        ///     This call must only be made by a child thread.
        ///
        /// \see
        ///     CancellationType
        //---------------------------------------------------------------------
        void
        Thread::cancellation_enable( bool Value )
        {
            if ( !pthread_equal( p->tid, pthread_self( ) ) )
            {
                return;
            }
            int oldstate;

            pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
            m_cancel_state = Value;
            if ( m_cancel_state )
            {
                switch ( m_cancellation_type )
                {
                case CANCEL_ABANDON:
                case CANCEL_IGNORE:
                case CANCEL_EXCEPTION:
                case CANCEL_UNKNOWN:
                    // Nothing to do to setup for this one
                    break;
                case CANCEL_BY_SIGNAL:
                    if ( m_cancel_via_signal > 0 )
                    {
                        //---------------------------------------------------------
                        // Make sure the signal handler is registered
                        //---------------------------------------------------------
                        SignalHandler::ThreadCaptureSignal(
                            m_cancel_via_signal );
                        SignalHandler::Register( &wakeup, m_cancel_via_signal );
                    }
                    break;
                case CANCEL_ASYNCHRONOUS:
                case CANCEL_DEFERRED:
                    pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, &oldstate );
                    break;
                }
            }
            else
            {
                switch ( m_cancellation_type )
                {
                case CANCEL_ABANDON:
                case CANCEL_IGNORE:
                case CANCEL_EXCEPTION:
                case CANCEL_UNKNOWN:
                    // Nothing to do to setup for this one
                    break;
                case CANCEL_BY_SIGNAL:
                    if ( m_cancel_via_signal > 0 )
                    {
                        //-------------------------------------------------------------
                        // Make sure the signal handler is unregistered
                        //-------------------------------------------------------------
                        SignalHandler::ThreadIgnoreSignal(
                            m_cancel_via_signal );
                        SignalHandler::Unregister( &wakeup,
                                                   m_cancel_via_signal );
                    }
                    break;
                case CANCEL_ASYNCHRONOUS:
                case CANCEL_DEFERRED:
                    pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
                    break;
                }
            }
        }

        void
        Thread::set_cancelled_state( )
        {
            MutexLock l( p->key, __FILE__, __LINE__ );

            m_cancel_thread = true;
        }

        inline Thread::thread_type
        Thread::threadId( ) const
        {
            MutexLock l( p->key, __FILE__, __LINE__ );

            return p->tid;
        }

        //---------------------------------------------------------------------
        /// Sets a how a thread can be cancelled.
        /// <ul>
        ///   <li><b>CANCEL_ASYNCHRONOUS</b>
        ///       A cancellation request is immediated delivered to the
        ///       thread.
        ///   <li><b>CANCEL_DEFERRED</b>
        ///       A cancellation request is marked pending for the thread
        ///       and the thread is cancelled when it gets to a system
        ///       cancellation point or when CancellationCheck is called.
        ///   <li><b>CANCEL_EXCEPTION</b>
        ///       A cancellation request is marked pending for the thread
        ///       and the thread throws a cancellation exception upon
        ///       calling CancellationCheck.
        /// </ul>
        ///
        /// \note
        ///     This call must only be made by a chivld thread.
        ///
        /// \see
        ///     CancellationEnable, CancellationCheck
        //---------------------------------------------------------------------
        void
        Thread::CancellationType( cancel_type Type, signal_type Signal )
        {
            MutexLock l( p->key, __FILE__, __LINE__ );

            if ( ( !pthread_equal( p->tid, pthread_self( ) ) ) ||
                 ( ( Type != CANCEL_BY_SIGNAL ) &&
                   ( m_cancellation_type == Type ) ) ||
                 ( ( Type == CANCEL_BY_SIGNAL ) &&
                   ( m_cancellation_type == Type ) &&
                   ( Signal == m_cancel_via_signal ) ) )
            {
                return;
            }
            int oldtype;
            int oldstate;

            pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, &oldstate );
            m_cancel_via_signal = SignalHandler::SIGNAL_UNKNOWN;
            m_cancellation_type = Type;
            switch ( m_cancellation_type )
            {
            case CANCEL_ASYNCHRONOUS:
                pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype );
                break;
            case CANCEL_DEFERRED:
                pthread_setcanceltype( PTHREAD_CANCEL_DEFERRED, &oldtype );
                break;
            case CANCEL_BY_SIGNAL:
                m_cancel_via_signal = Signal;
                break;
            case CANCEL_ABANDON:
            case CANCEL_EXCEPTION:
            case CANCEL_IGNORE:
            case CANCEL_UNKNOWN:
                return;
                break;
            }
            //-------------------------------------------------------------------
            // Restore the state of cancellation
            //-------------------------------------------------------------------
            cancellation_enable( m_cancel_state );
        }

        bool
        Thread::IsAlive( ) const
        {
            //-------------------------------------------------------------------
            // Preliminary checks of the thread.
            //-------------------------------------------------------------------
            if ( IsCancelled( ) && IsParentThread( ) )
            {
                try
                {
                    //---------------------------------------------------------------
                    // Thread has been cancelled; make sure the thread has been
                    // reaped.
                    //---------------------------------------------------------------
                    const_cast< Thread* >( this )->Join( );
                }
                catch ( ... )
                {
                }
            }
            //-------------------------------------------------------------------
            // Check status by trying to send it signal zero.
            // This signal does not disturb the process in any way.
            // Just want to determine if the process is still there.
            //-------------------------------------------------------------------
            return ( Kill( SignalHandler::SIGNAL_ISALIVE ) == 0 );
        }

        bool
        Thread::IsDetached( ) const
        {
            MutexLock l( p->key, __FILE__, __LINE__ );

            return detached;
        }

        bool
        Thread::IsParentThread( ) const
        {
            MutexLock l( p->key, __FILE__, __LINE__ );

            return ( pthread_equal( pthread_self( ), p->parent_tid ) );
        }

        void
        Thread::Detach( ) const
        {
            if ( p->tid )
            {
                // Detach the current thread (makes it non-joinable).
                volatile Thread::thread_type tmp = p->tid;

                int status = pthread_detach( tmp.handle );
                if ( status == 0 )
                {
                    MutexLock l( p->key, __FILE__, __LINE__ );

                    detached = true;
                }
            }
        }

        void
        Thread::Join( )
        {
            if ( p->tid )
            {
                bool release( true );

                int status = pthread_join( p->tid, NULL );
                try
                {
                    switch ( status )
                    {
                    case ESRCH:
                        throw range_error( "Thread::Join", __FILE__, __LINE__ );
                        break;
                    case EINVAL:
                        throw invalid_argument(
                            "Thread::Join", __FILE__, __LINE__ );
                        break;
                    case EDEADLK:
                        release = false;
                        throw deadlock( "Thead::Join", __FILE__, __LINE__ );
                        break;
                    }
                }
                catch ( ... )
                {
                    if ( release )
                    {
                        // get_thread_registry( ).erase( p->tid );

                        MutexLock l( p->key, __FILE__, __LINE__ );
                        p->tid = Thread::thread_type::NULL_THREAD;
                    }
                    throw; // rethrow the exception
                }
                {
                    // get_thread_registry( ).erase( p->tid );

                    MutexLock l( p->key, __FILE__, __LINE__ );
                    p->tid = Thread::thread_type::NULL_THREAD;
                }
            }
        }

        int
        Thread::Kill( signal_type Signal ) const
        {
            MutexLock l( p->key, __FILE__, __LINE__ );

            if ( p->tid )
            {
                return pthread_kill( p->tid,
                                     SignalHandler::OSSignal( Signal ) );
            }
            // Process could not be found since it is null
            return ESRCH;
        }

        inline Thread::thread_type
        Thread::ParentThread( ) const
        {
            return p->parent_tid;
        }

        int
        Thread::Spawn( )
        {
            return spawn( );
        }

        void
        Thread::cancelCleanup( Thread* Source )
        {
            if ( Source )
            {
                Source->set_cancelled_state( );
            }
        }

        //-------------------------------------------------------------
        /// Establish the default value of the stack size used when
        /// creating new threads.
        /// This routine does validate the requested value..
        //-------------------------------------------------------------
        void
        Thread::StackSizeDefault( size_t StackSize )
        {
            int            ret = 0;
            pthread_attr_t attr;

            pthread_attr_init( &attr );
            if ( StackSize > 0 )
            {
                (void)pthread_attr_setstacksize( &attr, StackSize );
                ret = pthread_attr_getstacksize( &attr, &StackSize );
            }
            else
            {
                StackSize = 0;
            }
            pthread_attr_destroy( &attr );

            if ( ret == 0 )
            {
                MutexLock l( stack_size_baton( ), __FILE__, __LINE__ );

                stack_size = StackSize;
            }
        }

        int
        Thread::spawn( start_function_type StartFunction )
        {
            int retval = 0;

            VERBOSE_DEBUGGING_10( "spawn called with p->tid: "
                                  << (unsigned long)( p->tid )
                                  << " IsDetached( ): " << IsDetached( )
                                  << std::endl );
            if ( ( !p->tid ) || IsDetached( ) )
            {
                VERBOSE_DEBUGGING_10( "Creating new thread with stack size: "
                                      << StackSizeDefault( ) << std::endl );
                {
                    MutexLock l( p->key, __FILE__, __LINE__ );

                    p->tid = 0;
                    detached = 0;
                }

                pthread_attr_t attr;
                pthread_attr_init( &attr );
                if ( StackSizeDefault( ) > 0 )
                {
                    pthread_attr_setstacksize( &attr, StackSizeDefault( ) );
                }

                retval = pthread_create( p->tid, &attr, StartFunction, this );
                pthread_attr_destroy( &attr );

                if ( retval != 0 )
                {
                    throw std::runtime_error(
                        "Thread::Start: failed to start thread" );
                }
            }
            VERBOSE_DEBUGGING_10(
                "Completed spawn with retval: " << retval << std::endl );
            return retval;
        }

        Thread*
        Thread::start_routine( Thread* ThreadSource )
        {
            if ( MemChecker::IsExiting( ) == false )
            {
                try
                {
                    thread_registry::Add( thread_registry::Self( ),
                                          ThreadSource );

                    try
                    {
                        ThreadSource->CancellationType( CANCEL_EXCEPTION );
                        ThreadSource->CancellationEnable( true );
                        VERBOSE_DEBUGGING_30( "start_routine: before action"
                                              << std::endl );
                        ThreadSource->action( );
                        VERBOSE_DEBUGGING_30( "start_routine: after action"
                                              << std::endl );
                    }
                    catch ( const std::exception& exception )
                    {
                        VERBOSE_DEBUGGING_30(
                            "start_routine: caught exception: "
                            << exception.what( ) << std::endl );
                        if ( ThreadSource->IsCancelled( ) )
                        {
                            signal_type sig;

                            switch ( ThreadSource->CancellationType( sig ) )
                            {
                            case CANCEL_ASYNCHRONOUS:
                            case CANCEL_DEFERRED:
                                thread_registry::Remove(
                                    thread_registry::Self( ) );
                                throw; // Rethrow the cancelation exception
                                break;
                            default:
                                break;
                            }
                        }
                    }

                    thread_registry::Remove( thread_registry::Self( ) );
                    return ThreadSource;
                }
                catch ( const std::exception& Exception )
                {
                    std::cerr << "Thread Exception: " << Exception.what( )
                              << std::endl;
                    assert( 0 );
                }
            }
            VERBOSE_DEBUGGING_30( "start_routine: return" << std::endl );
            return (Thread*)NULL;
        }

        Thread::cancellation::cancellation( const std::string& Header,
                                            const char*        File,
                                            const int          Line )
            : std::runtime_error( format( Header, File, Line ) )
        {
        }

        std::string
        Thread::cancellation::format( const std::string& Header,
                                      const char*        File,
                                      const int          Line )
        {
            std::ostringstream msg;

            msg << Header << ": thread cancellation."
                << " (File: " << File << " Line: " << Line << ")";
            return msg.str( );
        }

        Thread::deadlock::deadlock( const std::string& Header,
                                    const char*        File,
                                    const int          Line )
            : std::logic_error( format( Header, File, Line ) )
        {
        }

        std::string
        Thread::deadlock::format( const std::string& Header,
                                  const char*        File,
                                  const int          Line )
        {
            std::ostringstream msg;

            msg << Header << ": attempt to join with self."
                << " (File: " << File << " Line: " << Line << ")";
            return msg.str( );
        }

        Thread::invalid_argument::invalid_argument( const std::string& Header,
                                                    const char*        File,
                                                    const int          Line )
            : std::invalid_argument( format( Header, File, Line ) )
        {
        }

        std::string
        Thread::invalid_argument::format( const std::string& Header,
                                          const char*        File,
                                          const int          Line )
        {
            std::ostringstream msg;

            msg << Header << ": invalid argument."
                << " (File: " << File << " Line: " << Line << ")";
            return msg.str( );
        }

        Thread::range_error::range_error( const std::string& Header,
                                          const char*        File,
                                          const int          Line )
            : std::range_error( format( Header, File, Line ) )
        {
        }

        std::string
        Thread::range_error::format( const std::string& Header,
                                     const char*        File,
                                     const int          Line )
        {
            std::ostringstream msg;

            msg << Header << ": no thread could be found."
                << " (File: " << File << " Line: " << Line << ")";
            return msg.str( );
        }
    } // namespace AL
} // namespace LDASTools
