//
// 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
//

//=======================================================================
void* check_rwlock_thread( void* Args );

void
check_rwlock( ReadWriteLock::lock_mode Mode )
{
    int             status;
    pthread_t       handle;
    pthread_attr_t  attr;
    lock_check_info info;
    void*           res;

    info.err = (std::exception*)0;
    info.mode = Mode;

    pthread_attr_init( &attr );
    status = pthread_create( &handle, &attr, check_rwlock_thread, &info );

    if ( status != 0 )
    {
        throw std::runtime_error( "Unable to create thread" );
    }

    pthread_attr_init( &attr );
    pthread_join( handle, &res );
    if ( info.err )
    {
        if ( reinterpret_cast< ReadWriteLock::BusyError* >( info.err ) )
        {
            delete info.err;
            info.err = (std::exception*)0;
            throw ReadWriteLock::BusyError( );
        }
        if ( reinterpret_cast< ReadWriteLock::DeadLockError* >( info.err ) )
        {
            delete info.err;
            info.err = (std::exception*)0;
            throw ReadWriteLock::DeadLockError( );
        }
        delete info.err;
        info.err = (std::exception*)0;
    }
}

void*
check_rwlock_thread( void* Args )
{
    lock_check_info* info = static_cast< lock_check_info* >( Args );

    try
    {
        rwlock_baton.TryLock( info->mode, __FILE__, __LINE__ );
        rwlock_baton.Unlock( __FILE__, __LINE__ );
    }
    catch ( const ReadWriteLock::BusyError& Exception )
    {
        info->err = new ReadWriteLock::BusyError;
    }
    catch ( const ReadWriteLock::DeadLockError& Exception )
    {
        info->err = new ReadWriteLock::DeadLockError;
    }
    catch ( std::exception& Exception )
    {
        info->err = new std::exception( Exception );
    }
    return (void*)NULL;
}

//=======================================================================

static void
test_rw_lock_baton( )
{
    const char* leader = "ReadWriteLock::baton_type:";

    bool status = true;
    ;

    //---------------------------------------------------------------------
    // Check the initial state
    //---------------------------------------------------------------------
    status = true;
    try
    {
        check_rwlock( ReadWriteLock::WRITE );
    }
    catch ( const std::exception& Exception )
    {
        status = false;
    }
    Test.Check( status ) << leader
                         << " Verify initially lock state of "
                            "ReadWriteLock::baton_type is unlocked"
                         << std::endl;
    //---------------------------------------------------------------------
    // Verify that locking works
    //---------------------------------------------------------------------
    status = false;
    try
    {
        rwlock_baton.Lock( ReadWriteLock::WRITE, __FILE__, __LINE__ );
        check_rwlock( ReadWriteLock::WRITE );
    }
    catch ( const ReadWriteLock::BusyError& Exception )
    {
        status = true;
    }
    catch ( const std::exception& Exception )
    {
        status = false;
    }
    Test.Check( status ) << leader
                         << " Lock acquisition for ReadWriteLock::baton_type"
                         << std::endl;
    //---------------------------------------------------------------------
    // Verify that unlocking works
    //---------------------------------------------------------------------
    try
    {
        status = true;
        rwlock_baton.Unlock( __FILE__, __LINE__ );
        check_rwlock( ReadWriteLock::WRITE );
    }
    catch ( const std::exception& Exception )
    {
        status = false;
    }
    Test.Check( status ) << leader
                         << " Lock release for ReadWriteLock::baton_type"
                         << std::endl;
}

static void
test_rw_lock_baton_locking( )
{
    const char* leader = "test_rw_lock_baton_locking:";

    bool status = true;
    ;

    try
    {
        //-------------------------------------------------------------------
        // Verify that locking works
        //-------------------------------------------------------------------
        rwlock_baton.Lock( ReadWriteLock::WRITE, __FILE__, __LINE__ );
        try
        {
            status = false;
            check_rwlock( ReadWriteLock::WRITE );
        }
        catch ( const ReadWriteLock::BusyError& Exception )
        {
            status = true;
        }
        catch ( const std::exception& Exception )
        {
            status = false;
        }
        Test.Check( status )
            << leader << " WRITE lock blocks other WRITE lock requests"
            << std::endl;
        try
        {
            status = false;
            check_rwlock( ReadWriteLock::READ );
        }
        catch ( const ReadWriteLock::BusyError& Exception )
        {
            status = true;
        }
        catch ( const std::exception& Exception )
        {
            status = false;
        }
        Test.Check( status )
            << leader << " WRITE lock blocks other READ lock requests"
            << std::endl;
        //---------------------------------------------------------------------
        // Verify that unlocking works
        //---------------------------------------------------------------------
        try
        {
            status = true;
            rwlock_baton.Unlock( __FILE__, __LINE__ );
            check_rwlock( ReadWriteLock::WRITE );
        }
        catch ( const std::exception& Exception )
        {
            status = false;
        }
        Test.Check( status )
            << leader << " Lock release for ReadWriteLock::baton_type"
            << std::endl;
    }
    catch ( const std::exception& Exception )
    {
        Test.Check( false ) << leader << " Caught an unexpected exception: "
                            << Exception.what( ) << std::endl;
    }
}
