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

#include <sstream>

#include "ldastoolsal/AtExit.hh"
#include "ldastoolsal/ErrorLog.hh"
#include "ldastoolsal/SignalHandler.hh"
#include "ldastoolsal/TimerThread.hh"

#define CONDITIONAL_WAIT 0
#define USE_NANOSLEEP 1
#define USE_ALARM 0

using LDASTools::AL::ErrorLog;
using LDASTools::AL::GPSTime;
using LDASTools::AL::SignalHandler;
using LDASTools::AL::StdErrLog;

static const SignalHandler::signal_type WAITER_SIGNAL =
    SignalHandler::SIGNAL_USER_1;

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

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

    inline void
    debug_lock_acquire( const char* Proc,
                        const char* LockName,
                        const char* File,
                        int         Line )
    {
        if ( StdErrLog.IsOpen( ) )
        {
            std::ostringstream msg;

            msg << Proc << ": thread: " << pthread_self( )
                << " lock: " << LockName << " Acquire lock";
            StdErrLog( ErrorLog::DEBUG, File, Line, msg.str( ) );
        }
    }

    inline void
    debug_lock_release( const char* Proc,
                        const char* LockName,
                        const char* File,
                        int         Line )
    {
        if ( StdErrLog.IsOpen( ) )
        {
            std::ostringstream msg;

            msg << Proc << ": thread: " << pthread_self( )
                << " lock: " << LockName << " Release lock";
            StdErrLog( ErrorLog::DEBUG, File, Line, msg.str( ) );
        }
    }

    inline void
    debug_lock_release_request( const char* Proc,
                                const char* LockName,
                                const char* File,
                                int         Line )
    {
        if ( StdErrLog.IsOpen( ) )
        {
            std::ostringstream msg;

            msg << Proc << ": thread: " << pthread_self( )
                << " lock: " << LockName << " Release lock request";
            StdErrLog( ErrorLog::DEBUG, File, Line, msg.str( ) );
        }
    }

    inline void
    debug_lock_request( const char* Proc,
                        const char* LockName,
                        const char* File,
                        int         Line )
    {
        if ( StdErrLog.IsOpen( ) )
        {
            std::ostringstream msg;

            msg << Proc << ": thread: " << pthread_self( )
                << " lock: " << LockName << " Request lock";
            StdErrLog( ErrorLog::DEBUG, File, Line, msg.str( ) );
        }
    }
} // namespace

namespace LDASTools
{
    namespace AL
    {
        struct TimerThread::p_type
        {
        public:
            struct timer_info
            {
                key_t                      s_key;
                GPSTime                    s_when;
                SignalHandler::signal_type s_signal;
                pthread_t                  s_thread;
            };

            typedef std::list< timer_info > timers_type;

            bool                  m_waiter_done;
            pthread_t             m_waiter_thread;
            pthread_mutex_t       m_waiter_busy_lock;
            MutexLock::baton_type m_lock;
            timers_type           m_timers;
            key_t                 m_current_key;
            GPSTime               m_next_event;
#if 0
      pthread_cond_t		m_next_event_condition;
#endif /* 0 */
            MutexLock::baton_type m_next_event_lock;

            timer_info   peek_head_timer( );
            timer_info   pop_timer( const GPSTime& Time );
            void         reset_timer( );
            void*        waiter( );
            static void* swaiter( void* Data );

            inline p_type( )
                : m_waiter_done( true ), m_waiter_thread( (pthread_t)NULL ),
                  m_current_key( 1 )
            {
            }
        };

        /// This class keeps track of multiple timers.
        /// The active timers are stored in an ordered list with the one to be
        /// executed next being at the head of the list.
        TimerThread::TimerThread( ) : p_( new p_type )
        {
#if 0
      m_next_event_condition = PTHREAD_COND_INITIALIZER;
      m_next_event_lock = PTHREAD_MUTEX_INITIALIZER;
#endif /* 0 */

            // :TODO: LDASTools::AL::AtExit::Append( singleton_suicide );
            StdErrLog( ErrorLog::DEBUG,
                       __FILE__,
                       __LINE__,
                       "Initializing TimerThead class" );
#if USE_NANOSLEEP
            SignalHandler::Register( &wakeup, WAITER_SIGNAL );
#elif USE_ALARM
            // Setup to be watching for SIGALRM signal
            sigset_t sigs_to_block;
            sigemptyset( &sigs_to_block );
            sigaddset( &sigs_to_block, SIGALRM );
            pthread_sigmask( SIG_BLOCK, &sigs_to_block, NULL );
#endif /* 0 */
            p_->m_waiter_done = false;
            pthread_create(
                &( p_->m_waiter_thread ), NULL, p_type::swaiter, this );
            if ( StdErrLog.IsOpen( ) )
            {
                std::ostringstream msg;

                msg << "TimerThread: created waiter thread: "
                    << p_->m_waiter_thread;

                StdErrLog( ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
            }
        }

        /// Carefully destroy resources.
        TimerThread::~TimerThread( )
        {
            //-------------------------------------------------------------------
            // Let the worker thread know that its day is done
            //-------------------------------------------------------------------
            p_->m_waiter_done = true;
            //-------------------------------------------------------------------
            // Wakeup thread if it is sleeping
            //-------------------------------------------------------------------
            pthread_mutex_lock( &( p_->m_waiter_busy_lock ) );
            pthread_kill( p_->m_waiter_thread,
                          SignalHandler::OSSignal( WAITER_SIGNAL ) );
            pthread_mutex_unlock( &( p_->m_waiter_busy_lock ) );
            //-------------------------------------------------------------------
            // Wait for the thread to complete
            //-------------------------------------------------------------------
            pthread_join( p_->m_waiter_thread, NULL );
        }

        /// Add a timer to the list of timers.
        /// The list is maintained in assending order.
        TimerThread::key_t
        TimerThread::AddTimer( int Wakeup, SignalHandler::signal_type Signal )
        {
            StdErrLog( ErrorLog::DEBUG, __FILE__, __LINE__, "Adding Timer" );
            debug_lock_request( "AddTimer", "m_lock", __FILE__, __LINE__ );
            MutexLock lock( p_->m_lock, __FILE__, __LINE__ );
            debug_lock_acquire( "AddTimer", "p_->m_lock", __FILE__, __LINE__ );
            p_type::timer_info ringer;

            ringer.s_key = p_->m_current_key++;
            ringer.s_when = GPSTime::NowGPSTime( ) + Wakeup;
            ringer.s_signal = Signal;
            ringer.s_thread = pthread_self( );

            bool inserted = false;
            for ( p_type::timers_type::iterator cur = p_->m_timers.begin( ),
                                                last = p_->m_timers.end( );
                  cur != last;
                  ++cur )
            {
                if ( cur->s_when > ringer.s_when )
                {
                    p_->m_timers.insert( cur, ringer );
                    inserted = true;
                    break;
                }
            }
            if ( !inserted )
            {
                p_->m_timers.push_back( ringer );
            }
            std::ostringstream msg;

            msg << "AddTimer: Added timer: key: " << ringer.s_key
                << " when: " << ringer.s_when;
            StdErrLog( ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
            debug_lock_release_request(
                "AddTimer", "m_lock", __FILE__, __LINE__ );
            lock.Release( __FILE__, __LINE__ );
            debug_lock_release( "AddTimer", "m_lock", __FILE__, __LINE__ );
            p_->reset_timer( );

            return ringer.s_key;
        }

        // Remove a timer from the list of active timers.
        bool
        TimerThread::RemoveTimer( key_t Key )
        {
            bool retval = false;
            debug_lock_request( "RemoveTimer", "m_lock", __FILE__, __LINE__ );
            MutexLock lock( p_->m_lock, __FILE__, __LINE__ );
            debug_lock_acquire( "RemoveTimer", "m_lock", __FILE__, __LINE__ );

            for ( p_type::timers_type::iterator cur = p_->m_timers.begin( ),
                                                last = p_->m_timers.end( );
                  cur != last;
                  ++cur )
            {
                if ( cur->s_key == Key )
                {
                    std::ostringstream msg;

                    msg << "Removing Timer: thread: " << pthread_self( )
                        << " key: " << cur->s_key;
                    StdErrLog(
                        ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                    p_->m_timers.erase( cur );
                    retval = true;
                    break;
                }
            }
            debug_lock_release_request(
                "RemoveTimer", "m_lock", __FILE__, __LINE__ );
            lock.Release( __FILE__, __LINE__ );
            debug_lock_release( "RemoveTimer", "m_lock", __FILE__, __LINE__ );
            p_->reset_timer( );
            return retval;
        }

        void
        TimerThread::p_type::reset_timer( )
        {
            try
            {
                p_type::timer_info info = peek_head_timer( );
#if USE_NANOSLEEP
                //-----------------------------------------------------------------
                // Reset the timer to the head of the queue
                //-----------------------------------------------------------------
                {
                    debug_lock_request( "reset_timer",
                                        "m_next_event_lock",
                                        __FILE__,
                                        __LINE__ );
                    MutexLock lock( m_next_event_lock, __FILE__, __LINE__ );
                    debug_lock_acquire( "reset_timer",
                                        "m_next_event_lock",
                                        __FILE__,
                                        __LINE__ );
                    std::ostringstream msg;

                    msg << "reset_timer: thread: " << pthread_self( )
                        << " setting m_next_event: next: " << info.s_when;
                    m_next_event = info.s_when;
                    StdErrLog(
                        ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                    debug_lock_release_request( "reset_timer",
                                                "m_next_event_lock",
                                                __FILE__,
                                                __LINE__ );
                }
                debug_lock_release(
                    "reset_timer", "m_next_event_lock", __FILE__, __LINE__ );
#elif CONDITIONAL_WAIT
                //-----------------------------------------------------------------
                // Reset the timer to the head of the queue
                //-----------------------------------------------------------------
                debug_lock_request(
                    "reset_timer", "m_next_event_lock", __FILE__, __LINE__ );
                pthread_mutex_lock( &( m_next_event_lock ) );
                debug_lock_acquire(
                    "reset_timer", "m_next_event_lock", __FILE__, __LINE__ );
                if ( m_next_event != info.s_when )
                {
                    std::ostringstream msg;

                    msg << "reset_timer: thread: " << pthread_self( )
                        << " setting m_next_event: next: " << info.s_when;
                    m_next_event = info.s_when;
                    StdErrLog(
                        ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                    pthread_cond_signal( &( m_next_event_condition ) );
                }
                debug_lock_release_request(
                    "reset_timer", "m_next_event_lock", __FILE__, __LINE__ );
                pthread_mutex_unlock( &( m_next_event_lock ) );
                debug_lock_release(
                    "reset_timer", "m_next_event_lock", __FILE__, __LINE__ );
#elif USE_ALARM
                GPSTime now( GPSTime::NowGPSTime( ) );

                if ( info.s_when > now )
                {
                    //---------------------------------------------------------------
                    // Setting the timer to ring in the future
                    //---------------------------------------------------------------
                    int next = int( info.s_when - now );
                    if ( next <= 0 )
                    {
                        next = 1;
                    }
                    StdErrLog( ErrorLog::DEBUG,
                               __FILE__,
                               __LINE__,
                               "reset_timer: setting alarm()" );
                    alarm( next );
                }
                else
                {
                    //---------------------------------------------------------------
                    // This is the case where an expired timer is in the queue.
                    //---------------------------------------------------------------
                    pthread_kill( pthread_self( ),
                                  SignalHandler::OSSignal(
                                      SignalHandler::SIGNAL_ALARM ) );
                }
#endif
            }
            catch ( ... )
            {
                if ( StdErrLog.IsOpen( ) )
                {
                    std::ostringstream msg;

                    msg << "reset_timer: thread: " << pthread_self( )
                        << " timer queue is empty";

                    StdErrLog(
                        ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                }
                // Case where the queue is empty. Next timer will be set with
                // AddTimer
#if CONDITIONAL_WAIT
                //-----------------------------------------------------------------
                // Reset the timer to the head of the queue
                //-----------------------------------------------------------------
                pthread_mutex_lock( &( m_next_event_lock ) );
                m_next_event = GPSTime( );
                StdErrLog(
                    ErrorLog::DEBUG,
                    __FILE__,
                    __LINE__,
                    "reset_timer: setting m_next_event to GPSTime(0,0)" );
                pthread_cond_signal( &( m_next_event_condition ) );
                pthread_mutex_unlock( &( m_next_event_lock ) );
#elif USE_NANOSLEEP
                //-----------------------------------------------------------------
                // Wait an hour for the next event to come in
                //-----------------------------------------------------------------
                {
                    MutexLock lock( m_next_event_lock, __FILE__, __LINE__ );
                    if ( StdErrLog.IsOpen( ) )
                    {
                        std::ostringstream msg;
                        msg << "reset_timer: thread: " << pthread_self( )
                            << " Waiting an hour for next event";
                        StdErrLog(
                            ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                    }
                    m_next_event.Now( );
                    m_next_event += 3600.0;
                }
#endif /* CONDITIONAL_WAIT */
            }
#if USE_NANOSLEEP
            if ( pthread_self( ) != m_waiter_thread )
            {
                if ( StdErrLog.IsOpen( ) )
                {
                    std::ostringstream msg;

                    msg << "reset_timer: thread; " << pthread_self( )
                        << " Signalling waiter thread: " << m_waiter_thread;
                    StdErrLog(
                        ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                }
                debug_lock_request(
                    "reset_timer", "m_waiter_busy_lock", __FILE__, __LINE__ );
                pthread_mutex_lock( &( m_waiter_busy_lock ) );
                debug_lock_acquire(
                    "reset_timer", "m_waiter_busy_lock", __FILE__, __LINE__ );
                pthread_kill( m_waiter_thread,
                              SignalHandler::OSSignal( WAITER_SIGNAL ) );
                debug_lock_release_request(
                    "reset_timer", "m_waiter_busy_lock", __FILE__, __LINE__ );
                pthread_mutex_unlock( &( m_waiter_busy_lock ) );
                debug_lock_release(
                    "reset_timer", "m_waiter_busy_lock", __FILE__, __LINE__ );
            }
#endif /* USE_NANOSLEEP */
        }

        TimerThread::p_type::timer_info
        TimerThread::p_type::peek_head_timer( )
        {
            // Mutex lock queue
            debug_lock_request(
                "peek_head_timer", "m_lock", __FILE__, __LINE__ );
            MutexLock lock( m_lock, __FILE__, __LINE__ );
            debug_lock_acquire(
                "peek_head_timer", "m_lock", __FILE__, __LINE__ );

            if ( m_timers.size( ) > 0 )
            {
                debug_lock_release(
                    "peek_head_timer", "m_lock", __FILE__, __LINE__ );
                return m_timers.front( );
            }
            debug_lock_release_request(
                "peek_head_timer", "m_lock", __FILE__, __LINE__ );
            lock.Release( __FILE__, __LINE__ );
            debug_lock_release(
                "peek_head_timer", "m_lock", __FILE__, __LINE__ );
            StdErrLog(
                ErrorLog::DEBUG, __FILE__, __LINE__, "peek_head_timer: empty" );
            throw std::underflow_error( "Timer queue is empty" );
        }

        TimerThread::p_type::timer_info
        TimerThread::p_type::pop_timer( const GPSTime& Time )
        {
            // Mutex lock queue
            debug_lock_request( "pop_timer", "m_lock", __FILE__, __LINE__ );
            MutexLock lock( m_lock, __FILE__, __LINE__ );
            debug_lock_acquire( "pop_timer", "m_lock", __FILE__, __LINE__ );

            if ( StdErrLog.IsOpen( ) )
            {
                std::ostringstream msg;

                msg.str( "" );
                msg << "pop_timer: Time: " << Time
                    << " m_timers.size( ): " << m_timers.size( );
                if ( m_timers.size( ) > 0 )
                {
                    msg << " front().s_when: " << m_timers.front( ).s_when
                        << " front( ).s_key: " << m_timers.front( ).s_key;
                }
                StdErrLog( ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
            }
            if ( ( m_timers.size( ) > 0 ) &&
                 ( m_timers.front( ).s_when <= Time ) )
            {
                timer_info retval = m_timers.front( );
                (void)m_timers.pop_front( );
                debug_lock_release( "pop_timer", "m_lock", __FILE__, __LINE__ );
                return retval;
            }
            debug_lock_release_request(
                "pop_timer", "m_lock", __FILE__, __LINE__ );
            lock.Release( __FILE__, __LINE__ );
            debug_lock_release( "pop_timer", "m_lock", __FILE__, __LINE__ );
            StdErrLog( ErrorLog::DEBUG,
                       __FILE__,
                       __LINE__,
                       "pop_timer: Timer queue has no events for the time" );
            throw std::underflow_error( "Timer queue is empty" );
        }

        void*
        TimerThread::p_type::waiter( )
        {
            debug_lock_request(
                "waiter", "m_waiter_busy_lock", __FILE__, __LINE__ );
            pthread_mutex_lock( &( m_waiter_busy_lock ) );
            debug_lock_acquire(
                "waiter", "m_waiter_busy_lock", __FILE__, __LINE__ );
            std::ostringstream msg;
#if USE_ALARM
            int      sig;
            sigset_t set;
            sigset_t oset;

            sigemptyset( &set );
            sigaddset( &set, SIGALRM );

            pthread_sigmask( SIG_BLOCK, &set, &oset );
#endif /* USE_ALARM */

            msg.str( "" );
            msg << "waiter setup is complete: ";
            StdErrLog( ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
            while ( !m_waiter_done )
            {
                //-----------------------------------------------------------------
                // Wait for the timer signal to come through
                //-----------------------------------------------------------------
#if USE_NANOSLEEP
                GPSTime now( GPSTime( ).NowGPSTime( ) );
                StdErrLog( ErrorLog::DEBUG,
                           __FILE__,
                           __LINE__,
                           "waiter checking sleep duration" );
                {
                    std::ostringstream msg;

                    msg << "waiter: thread: " << pthread_self( )
                        << " Request lock - m_next_event_lock";
                    StdErrLog(
                        ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                }
                MutexLock lock( m_next_event_lock, __FILE__, __LINE__ );
                {
                    std::ostringstream msg;

                    msg << "waiter: thread: " << pthread_self( )
                        << " Aquired lock - m_next_event_lock";
                    StdErrLog(
                        ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                }
                if ( m_next_event > now )
                {
                    //---------------------------------------------------------------
                    // Calculate the duration to sleep
                    //---------------------------------------------------------------
                    GPSTime         time_left;
                    struct timespec sleep_duration;

                    time_left = m_next_event - now.GetTime( );
                    sleep_duration.tv_sec = time_left.GetSeconds( );
                    sleep_duration.tv_nsec = time_left.GetNanoseconds( );
                    //---------------------------------------------------------------
                    // Suspend execution
                    //---------------------------------------------------------------
                    msg.str( "" );
                    msg << "waiter paused on next: " << m_next_event
                        << " time_left: " << time_left;
                    StdErrLog(
                        ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                    {
                        std::ostringstream msg;

                        msg << "waiter: thread: " << pthread_self( )
                            << " Request lock release - m_next_event_lock";
                        StdErrLog(
                            ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                    }
                    lock.Release( __FILE__, __LINE__ );
                    {
                        std::ostringstream msg;

                        msg << "waiter: thread: " << pthread_self( )
                            << " Released lock - m_next_event_lock";
                        StdErrLog(
                            ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                    }
                    debug_lock_release_request(
                        "waiter", "m_waiter_busy_lock", __FILE__, __LINE__ );
                    pthread_mutex_unlock( &( m_waiter_busy_lock ) );
                    debug_lock_release(
                        "waiter", "m_waiter_busy_lock", __FILE__, __LINE__ );
                    nanosleep( &sleep_duration, NULL );
                    debug_lock_request(
                        "waiter", "m_waiter_busy_lock", __FILE__, __LINE__ );
                    pthread_mutex_lock( &( m_waiter_busy_lock ) );
                    debug_lock_acquire(
                        "waiter", "m_waiter_busy_lock", __FILE__, __LINE__ );
                    StdErrLog( ErrorLog::DEBUG,
                               __FILE__,
                               __LINE__,
                               "waiter: Awoke from nanosleep()" );
                }
                debug_lock_release_request(
                    "waiter", "m_lock", __FILE__, __LINE__ );
                lock.Release( __FILE__, __LINE__ );
                debug_lock_release( "waiter", "m_lock", __FILE__, __LINE__ );

#elif CONDITIONAL_WAIT
                int err = 0;
                pthread_mutex_lock( &( m_next_event_lock ) );
                if ( ( m_next_event.GetSeconds( ) == 0 ) &&
                     ( m_next_event.GetNanoseconds( ) == 0 ) )
                {
                    //---------------------------------------------------------------
                    // No next event scheduled
                    //---------------------------------------------------------------
                    msg.str( "" );
                    msg << "waiter paused on pthread_cond_wait: next: "
                        << m_next_event;
                    StdErrLog(
                        ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                    err = pthread_cond_wait( &( m_next_event_condition ),
                                             &( m_next_event_lock ) );
                    msg.str( "" );
                    msg << "waiter woke up pthread_cond_wait: ";
                    StdErrLog(
                        ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                }
                else
                {
                    //---------------------------------------------------------------
                    // There is an event coming so we need to wake up for when
                    // it
                    //   arrives.
                    //---------------------------------------------------------------
                    struct timespec timeout;
                    struct timeval  now;
                    GPSTime         time_left;
                    if ( m_next_event > GPSTime( ).NowGPSTime( ) )
                    {
                        time_left =
                            m_next_event - GPSTime( ).NowGPSTime( ).GetTime( );
                    }

                    //...............................................................
                    // Calculate the absolute time when the ringer should go
                    // off.
                    //   The calculation is in Unix time.
                    //...............................................................
                    gettimeofday( &now, NULL );
                    timeout.tv_sec += now.tv_sec + time_left.GetSeconds( );
                    timeout.tv_nsec +=
                        ( now.tv_usec * 1000 ) + time_left.GetNanoseconds( );
                    if ( timeout.tv_nsec >= NANOSECOND_MULTIPLIER )
                    {
                        ++timeout.tv_sec;
                        timeout.tv_nsec -= INT_4U( NANOSECOND_MULTIPLIER );
                    }

                    msg.str( "" );
                    msg << "waiter paused on pthread_cond_timedwait: next: "
                        << m_next_event;
                    StdErrLog(
                        ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                    err = pthread_cond_timedwait( &( m_next_event_condition ),
                                                  &( m_next_event_lock ),
                                                  &timeout );
                    msg.str( "" );
                    msg << "waiter woke up pthread_cond_timedwait: ";
                    StdErrLog(
                        ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                }
                pthread_mutex_unlock( &( m_next_event_lock ) );
                msg.str( "" );
                msg << "pthread_cond_(time)wait returned: err: " << err;
                switch ( err )
                {
                case ETIMEDOUT:
                    msg << " (TIMEDOUT)";
                    break;
                case EINVAL:
                    msg << " (EINVAL)";
                    break;
                case EPERM:
                    msg << " (EPERM)";
                    break;
                default:
                    msg << " (unknown)";
                    break;
                }
                StdErrLog( ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
#elif USE_ALARM
                int err = sigwait( &set, &sig );
                msg.str( "" );
                msg << "sigwait returned: err: " << err << " sig: " << sig;
                StdErrLog( ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
#endif /* CONDITIONAL_WAIT */
                //-----------------------------------------------------------------
                // Loop through all processes that are waiting on alarms
                //-----------------------------------------------------------------
                while ( 1 )
                {
                    GPSTime now( GPSTime::NowGPSTime( ) );

                    try
                    {
                        //-------------------------------------------------------------
                        // Get an event off the queue
                        //-------------------------------------------------------------
                        timer_info info;
                        info = pop_timer( now );
                        msg.str( "" );
                        msg << "Processing Timer Event: " << info.s_key
                            << "Now: " << now;
                        StdErrLog(
                            ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                        //-------------------------------------------------------------
                        // Signal the requesting thread
                        //-------------------------------------------------------------
                        pthread_kill(
                            info.s_thread,
                            SignalHandler::OSSignal( info.s_signal ) );
                    }
                    catch ( ... )
                    {
                        //-------------------------------------------------------------
                        // There are no more events
                        //-------------------------------------------------------------
                        msg.str( "" );
                        msg << "Done processing Timer Events for time: " << now;
                        StdErrLog(
                            ErrorLog::DEBUG, __FILE__, __LINE__, msg.str( ) );
                        break;
                    }
                }
                //-----------------------------------------------------------------
                // Set up for the next alarm
                //-----------------------------------------------------------------
                StdErrLog( ErrorLog::DEBUG,
                           __FILE__,
                           __LINE__,
                           "waiter: About to reset_timer" );
                reset_timer( );
                StdErrLog( ErrorLog::DEBUG,
                           __FILE__,
                           __LINE__,
                           "waiter: Returned from reset_timer" );
            }
            pthread_mutex_unlock( &( m_waiter_busy_lock ) );
            StdErrLog( ErrorLog::DEBUG,
                       __FILE__,
                       __LINE__,
                       "waiter: thread work completed, need to join" );
            return (void*)NULL;
        }

        void*
        TimerThread::p_type::swaiter( void* Data )
        {
            TimerThread* self = static_cast< TimerThread* >( Data );

            return self->p_->waiter( );
        }
    } // namespace AL
} // namespace LDASTools

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