//
// 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 <list>
#include <sstream>
#include <typeinfo>

#include "ldastoolsal/ErrorLog.hh"
#include "ldastoolsal/TaskThread.hh"
#include "ldastoolsal/ThreadPool.hh"
#include "ldastoolsal/Timeout.hh"

namespace LDASTools
{
    namespace AL
    {

        //=====================================================================
        //=====================================================================
        TimeoutException::TimeoutException( const std::string& msg )
            : std::runtime_error( msg.c_str( ) )
        {
        }

        //=====================================================================
        //=====================================================================
        int
        Timeout( Task* CallerTask,
                 int   Seconds,
                 int&  Status,
                 bool  ThrowTimeoutException )
        {
            bool        timedout( false );
            std::string task_name( ( CallerTask )
                                       ? typeid( *CallerTask ).name( )
                                       : "null_timed_task" );

            Status = TIMEOUT_COMPLETED;

            if ( CallerTask )
            {
                TaskThread* slave = ThreadPool::Acquire( );

                if ( slave )
                {
                    bool joinable = true;

                    slave->AddTask( CallerTask );

                    //---------------------------------------------------------------
                    // Wait for task to complete
                    //---------------------------------------------------------------
                    slave->TimedWait( Seconds );
                    timedout = slave->TimedOut( );
                    joinable = slave->Halt( );

                    if ( joinable )
                    {
                        // Return the resouce to the pool
                        ThreadPool::Relinquish( slave );
                    } // if joinable
                    else
                    {
                        Status &= ~TIMEOUT_COMPLETED;
                    }
                } // if slave
            } // if CallerTask
            if ( timedout )
            {
                Status |= TIMEOUT_TIMED_OUT;
            }
            if ( timedout && ThrowTimeoutException )
            {
                std::ostringstream msg;

                msg << "Unable to finish " << task_name << " within " << Seconds
                    << " seconds";

                throw TimeoutException( msg.str( ) );
            }
            return ( ( timedout == true ) ? -1 : 0 );
        } // Timeout()
    } // namespace AL
} // namespace LDASTools
