//===-- MIUtilThreadBaseStd.cpp ---------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// Third Party Headers:
#include <assert.h>

// In-house headers:
#include "MIUtilThreadBaseStd.h"
#include "MICmnThreadMgrStd.h"

//++ ------------------------------------------------------------------------------------
// Details: Constructor.
// Type:    None.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMIUtilThreadActiveObjBase::CMIUtilThreadActiveObjBase()
    : m_references(0)
    , m_bHasBeenKilled(false)
{
}

//++ ------------------------------------------------------------------------------------
// Details: Destructor.
// Type:    None.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMIUtilThreadActiveObjBase::~CMIUtilThreadActiveObjBase()
{
    // Make sure our thread is not alive before we die
    m_thread.Join();
}

//++ ------------------------------------------------------------------------------------
// Details: Check if an object is already running.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIUtilThreadActiveObjBase::ThreadIsActive()
{
    // Create a new thread to occupy this threads Run() function
    return m_thread.IsActive();
}

//++ ------------------------------------------------------------------------------------
// Details: Set up *this thread.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIUtilThreadActiveObjBase::ThreadExecute()
{
    // Create a new thread to occupy this threads Run() function
    return m_thread.Start(ThreadEntry, this);
}

//++ ------------------------------------------------------------------------------------
// Details: Acquire a reference to CMIUtilThreadActiveObjBase.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIUtilThreadActiveObjBase::Acquire()
{
    // Access to this function is serial
    CMIUtilThreadLock serial(m_mutex);

    // >0 == *this thread is alive
    m_references++;

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Release a reference to CMIUtilThreadActiveObjBase.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIUtilThreadActiveObjBase::Release()
{
    // Access to this function is serial
    CMIUtilThreadLock serial(m_mutex);

    // 0 == kill off *this thread
    m_references--;

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Force this thread to stop, regardless of references
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIUtilThreadActiveObjBase::ThreadKill()
{
    // Access to this function is serial
    CMIUtilThreadLock serial(m_mutex);

    // Set this thread to killed status
    m_bHasBeenKilled = true;

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Proxy to thread join.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIUtilThreadActiveObjBase::ThreadJoin()
{
    return m_thread.Join();
}

//++ ------------------------------------------------------------------------------------
// Details: This function is the entry point of this object thread.
//          It is a trampoline to an instances operation manager.
// Type:    Static method.
// Args:    vpThisClass - (R) From the system (our CMIUtilThreadActiveObjBase from the ctor).
// Return:  MIuint - 0 = success.
// Throws:  None.
//--
MIuint
CMIUtilThreadActiveObjBase::ThreadEntry(void *vpThisClass)
{
    // The argument is a pointer to a CMIUtilThreadActiveObjBase class
    // as passed from the initialize function, so we can safely cast it.
    assert(vpThisClass != nullptr);
    CMIUtilThreadActiveObjBase *pActive = reinterpret_cast<CMIUtilThreadActiveObjBase *>(vpThisClass);

    // Start the management routine of this object
    pActive->ThreadManage();

    // Thread death
    return 0;
}

//++ ------------------------------------------------------------------------------------
// Details: This function forms a small management routine, to handle the thread's running.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
void
CMIUtilThreadActiveObjBase::ThreadManage()
{
    bool bAlive = true;

    // Infinite loop
    while (bAlive)
    {
        // Scope the lock while we access m_isDying
        {
            // Lock down access to the interface
            CMIUtilThreadLock serial(m_mutex);

            // Quit the run loop if we are dying
            if (m_references == 0)
                break;
        }
        // Execute the run routine
        if (!ThreadRun(bAlive))
            // Thread's run function failed (MIstatus::failure)
            break;

        // We will die if we have been signaled to die
        bAlive &= !m_bHasBeenKilled;
    }

    // Execute the finish routine just before we die
    // to give the object a chance to clean up
    ThreadFinish();

    m_thread.Finish();
}

//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------

//
CMIUtilThread::CMIUtilThread()
    : m_pThread(nullptr)
    , m_bIsActive(false)
{
}

//++ ------------------------------------------------------------------------------------
// Details: CMIUtilThread destructor.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
CMIUtilThread::~CMIUtilThread()
{
    Join();
}

//++ ------------------------------------------------------------------------------------
// Details: Wait for thread to stop.
// Type:    Method.
// Args:    None.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIUtilThread::Join()
{
    if (m_pThread != nullptr)
    {
        // Wait for this thread to die
        m_pThread->join();

        // Scope the thread lock while we modify the pointer
        {
            CMIUtilThreadLock _lock(m_mutex);
            delete m_pThread;
            m_pThread = nullptr;
        }
    }

    return MIstatus::success;
}

//++ ------------------------------------------------------------------------------------
// Details: Is the thread doing work.
// Type:    Method.
// Args:    None.
// Return:  bool - True = Yes active, false = not active.
// Throws:  None.
//--
bool
CMIUtilThread::IsActive()
{
    // Lock while we access the thread status
    CMIUtilThreadLock _lock(m_mutex);
    return m_bIsActive;
}

//++ ------------------------------------------------------------------------------------
// Details: Finish this thread
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
void
CMIUtilThread::Finish()
{
    // Lock while we access the thread status
    CMIUtilThreadLock _lock(m_mutex);
    m_bIsActive = false;
}

//++ ------------------------------------------------------------------------------------
// Details: Set up *this thread.
// Type:    Method.
// Args:    vpFn    (R) - Function pointer to thread's main function.
//          vpArg   (R) - Pointer arguments to pass to the thread.
// Return:  MIstatus::success - Functional succeeded.
//          MIstatus::failure - Functional failed.
// Throws:  None.
//--
bool
CMIUtilThread::Start(FnThreadProc vpFn, void *vpArg)
{
    // Lock while we access the thread pointer and status
    CMIUtilThreadLock _lock(m_mutex);

    // Create the std thread, which starts immediately and update its status
    m_pThread = new std::thread(vpFn, vpArg);
    m_bIsActive = true;

    // We expect to always be able to create one
    assert(m_pThread != nullptr);

    return MIstatus::success;
}

//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------

//++ ------------------------------------------------------------------------------------
// Details: Take resource.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
void
CMIUtilThreadMutex::Lock()
{
    m_mutex.lock();
}

//++ ------------------------------------------------------------------------------------
// Details: Release resource.
// Type:    Method.
// Args:    None.
// Return:  None.
// Throws:  None.
//--
void
CMIUtilThreadMutex::Unlock()
{
    m_mutex.unlock();
}

//++ ------------------------------------------------------------------------------------
// Details: Take resource if available. Immediately return in either case.
// Type:    Method.
// Args:    None.
// Return:  True    - mutex has been locked.
//          False   - mutex could not be locked.
// Throws:  None.
//--
bool
CMIUtilThreadMutex::TryLock()
{
    return m_mutex.try_lock();
}
