/** \file
 *
 *  Contains the ThreadHandler class implementation.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */

#include <pthread.h>


#include "ThreadHandler.h"
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>

struct WaitData
{
    pthread_t waitID_;
    Logger& logger_;
    int done_;
};

void
sleep_msecs( int msecs )
{
    struct timeval tv;

    tv.tv_sec = msecs / 1000;
    tv.tv_usec = ( msecs % 1000 ) * 1000;
    select( 0, NULL, NULL, NULL, &tv );
}
unsigned int
get_ticks()
{
    struct timeval tv;

    gettimeofday( &tv, NULL );
    return ( tv.tv_usec / 1000 + tv.tv_sec * 1000 );
}

void *
join_timeout_helper( void *arg )
{
    WaitData *data = ( WaitData* )arg;
    data->logger_.syslog( "Join timeout helper Thread ID is ", ( int )syscall( SYS_gettid ), Syslog::INFO );

    pthread_join( data->waitID_, NULL );
    data->done_ = true;
    return ( void * )0;
}

#define PTHREAD_JOIN_POLL_INTERVAL 20

int
ThreadHandler::PthreadJoinTimeout( pthread_t wid, unsigned int msecs, Component &component )
{
    pthread_t id;
    WaitData data = { wid, component.getLogger(), false };
    unsigned int start = get_ticks();
    int timedOut = false;

    if( pthread_create( &id, NULL, join_timeout_helper, &data ) != 0 )
        return ( -1 );
    do
    {
        if( data.done_ )
            break;
        /* you could also yield to your message loop here... */
        sleep_msecs( PTHREAD_JOIN_POLL_INTERVAL );
    }
    while( ( get_ticks() - start ) < msecs );
    if( !data.done_ )
    {
        //pthread_cancel( wid );
        //pthread_join( wid, NULL );
        printf( "Failed to join Handler thread for %s\n", component.getName().cStr() );
        printf( "Segfault\n" );
        component.segfault(); // Temporary till we figure out how to really kill the thread!
        printf( "Exit\n" );
        exit( 1 );          // Temporary till we figure out how to really kill the thread!
        printf( "Abort\n" );
        abort();
        timedOut = true;
    }
    /* free helper thread resources */
    pthread_join( id, NULL );
    return ( timedOut );
}

/// Constructor
ThreadHandler::ThreadHandler( Component &inComponent, const Str& name, bool idleThread, bool controlThread )
    : Handler( inComponent, name, idleThread, controlThread ),
      cancelled_( false ), threadID_( 0 )
{
    pthread_attr_init( &threadAttributes_ );
}

/// Desctructor
ThreadHandler::~ThreadHandler()
{
    // This needs to be done, no?  And the destructor will always be called in the
    // parent thread?

    // If it gets this far, hard kill (pthread_cancel)
    killThread();
    joinThread();

    pthread_attr_destroy( &threadAttributes_ );
}


/// Launch thread, run component in that thread
void ThreadHandler::run()
{
    int errCode;
    /// Create thread, execute component in it.

    errCode = pthread_create( &threadID_, &threadAttributes_, ThreadHandler::ThreadEntryPoint, this );
    if( errCode )
    {
        // Error handling
        threadID_ = 0;
    }
}

/// This is my static thread entry point.
void *ThreadHandler::ThreadEntryPoint( void *me )
{
    pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL );
    pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL );
    if( me != NULL )
    {
        // This makes me cry just a little bit.
        ( ( ThreadHandler * ) me ) ->runInThread();
    }

    // Should actually never make it here, redundant with exitThread() in runInThread()
    //pthread_exit( NULL );

    return NULL;
}

/// This is essentially "run", but it happens in the thread.
void ThreadHandler::runInThread( void )
{
    logger_.syslog( "Handler Thread ID is ", ( int )syscall( SYS_gettid ), Syslog::INFO );

    // If the thread terminates abnormally, run this
    pthread_cleanup_push( ThreadCleanup, ( void * ) this );

    // runs until the thread is done
    Handler::run();

    // In case we were killed by a signal handler
    cancelThread();

    // This will pop cleanup() from pthread but not run it.
    // If we got this far, component.uninitialize() was called by Handler.
    pthread_cleanup_pop( 0 );

    //exitThread();
}

//== Virtuals ==
// Relevant virtuals
/// Supplemental end conditions
bool ThreadHandler::continueLoop( void )
{
    //printf("In continueLoop, Handler::continueLoop() = %d, and cancelled_ = %d\n",Handler::continueLoop(), cancelled_);
    return Handler::continueLoop() && ( cancelled_ != true );
}


//== Cleanup functions ==
void ThreadHandler::ThreadCleanup( void *me )
{
    if( me != NULL )
    {
        ( ( ThreadHandler * ) me ) ->cleanup();
    }
}

void ThreadHandler::cleanup( void )
{
    // Should be only one paths here,
    //  through the threadCleanup callback
    component_.uninitialize();
}

/// Generic call, inherited from Handler to stop the component.
///
/// In this case, means "stop and reap the thread"
void ThreadHandler::shutdown( void )
{
    Handler::shutdown();
    if( pthread_equal( pthread_self(), threadID_ ) )
    {
        exitThread();
    }
    else
    {
        // Start with a "soft kill"
        cancelThread();
        joinThread();

        // possible to escalate to a "hard kill?" i.e. killThread()?
        // I really need to make this more "bulletproof", with timeouts,
        // then hard kill/joins.
    }
}

//=== Functions run primarily _within_ the thread ===

/// Exit this thread
void ThreadHandler::exitThread( void )
{

    if( pthread_equal( pthread_self(), threadID_ ) )
    {
        //    runningMutex.lock();
        //            running = false;
        //          runningMutex.unlock();
        pthread_exit( NULL );
    }
    else
    {
        //ignore
    }
}

//=== Functions primarily for running _outside_ the thread ===

/// End this thread
void ThreadHandler::cancelThread( void )
{
    // Tried doing this with pthread_cancel and it didn't seem effective....
    logger_.syslog( "Thread cancelled.", Syslog::INFO );
    cancelled_ = true;
}

/// Join the thread
void ThreadHandler::joinThread( void )
{
    if( pthread_equal( pthread_self(), threadID_ ) )
    {
        // Doesn't make sense to join self
    }
    else
    {
        // Not sure this is the safest behavior.  Is it thread safe?
        if( threadID_ != 0 )
        {

            PthreadJoinTimeout( threadID_, 30000, component_ );

            threadID_ = 0;

        }
    }
}

/// Send a kill signal to thread.
///
/// exitThread waits for the component to yield before ending the thread.
/// killThread interrupts the thread.
void ThreadHandler::killThread( void )
{
    if( pthread_equal( pthread_self(), threadID_ ) )
    {
        //ignore
    }
    else
    {
        if( threadID_ != 0 )
        {
            pthread_cancel( threadID_ );
        }
    }
}



