/** 
 * @file llthread.cpp
 *
 * $LicenseInfo:firstyear=2004&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010-2013, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "linden_common.h"
#include "llapr.h"

#include "apr_portable.h"

#include "llthread.h"
#include "llmutex.h"

#include "lltimer.h"
#include "lltrace.h"
#include "lltracethreadrecorder.h"
#include "llexception.h"

#if LL_LINUX
#include <sched.h>
#endif


#ifdef LL_WINDOWS
const DWORD MS_VC_EXCEPTION=0x406D1388;

#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
    DWORD dwType; // Must be 0x1000.
    LPCSTR szName; // Pointer to name (in user addr space).
    DWORD dwThreadID; // Thread ID (-1=caller thread).
    DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)

void set_thread_name( DWORD dwThreadID, const char* threadName)
{
    THREADNAME_INFO info;
    info.dwType = 0x1000;
    info.szName = threadName;
    info.dwThreadID = dwThreadID;
    info.dwFlags = 0;

    __try
    {
        ::RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info );
    }
    __except(EXCEPTION_CONTINUE_EXECUTION)
    {
    }
}
#endif


//----------------------------------------------------------------------------
// Usage:
// void run_func(LLThread* thread)
// {
// }
// LLThread* thread = new LLThread();
// thread->run(run_func);
// ...
// thread->setQuitting();
// while(!timeout)
// {
//   if (thread->isStopped())
//   {
//     delete thread;
//     break;
//   }
// }
// 
//----------------------------------------------------------------------------
namespace
{

    LLThread::id_t main_thread()
    {
        // Using a function-static variable to identify the main thread
        // requires that control reach here from the main thread before it
        // reaches here from any other thread. We simply trust that whichever
        // thread gets here first is the main thread.
        static LLThread::id_t s_thread_id = LLThread::currentID();
        return s_thread_id;
    }

} // anonymous namespace

LL_COMMON_API bool on_main_thread()
{
    return (LLThread::currentID() == main_thread());
}

LL_COMMON_API void assert_main_thread()
{
    auto curr = LLThread::currentID();
    auto main = main_thread();
    if (curr != main)
    {
        LL_WARNS() << "Illegal execution from thread id " << curr
            << " outside main thread " << main << LL_ENDL;
    }
}

// this function has become moot
void LLThread::registerThreadID() {}

//
// Handed to the APR thread creation function
//
void LLThread::threadRun()
{
#ifdef LL_WINDOWS
    set_thread_name(-1, mName.c_str());
#endif

    LL_PROFILER_SET_THREAD_NAME( mName.c_str() );

    // this is the first point at which we're actually running in the new thread
    mID = currentID();

    // for now, hard code all LLThreads to report to single master thread recorder, which is known to be running on main thread
    mRecorder = new LLTrace::ThreadRecorder(*LLTrace::get_master_thread_recorder());
    // <FS:Beq> - Add threadnames to telemetry
    LL_INFOS("THREAD") << "Started thread " << mName << LL_ENDL;
    LL_PROFILER_SET_THREAD_NAME( mName.c_str() );
    // </FS:Beq>
    // Run the user supplied function
    do 
    {
        try
        {
            LL_PROFILER_THREAD_BEGIN(mName.c_str())
            run();
            LL_PROFILER_THREAD_END(mName.c_str())
        }
        catch (const LLContinueError &e)
        {
            LL_WARNS("THREAD") << "ContinueException on thread '" << mName <<
                "' reentering run(). Error what is: '" << e.what() << "'" << LL_ENDL;
            //output possible call stacks to log file.
            LLError::LLCallStacks::print();

            LOG_UNHANDLED_EXCEPTION("LLThread");
            continue;
        }
        break;

    } while (true);

    //LL_INFOS() << "LLThread::staticRun() Exiting: " << threadp->mName << LL_ENDL;


    delete mRecorder;
    mRecorder = NULL;

    // We're done with the run function, this thread is done executing now.
    //NB: we are using this flag to sync across threads...we really need memory barriers here
    // Todo: add LLMutex per thread instead of flag?
    // We are using "while (mStatus != STOPPED) {ms_sleep();}" everywhere.
    mStatus = STOPPED;
}

LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
    mPaused(FALSE),
    mName(name),
    mThreadp(NULL),
    mStatus(STOPPED),
    mRecorder(NULL)
{
    mRunCondition = new LLCondition();
    mDataLock = new LLMutex();
    mLocalAPRFilePoolp = NULL ;
}


LLThread::~LLThread()
{
    shutdown();

    if (isCrashed())
    {
        LL_WARNS("THREAD") << "Destroying crashed thread named '" << mName << "'" << LL_ENDL;
    }

    if(mLocalAPRFilePoolp)
    {
        delete mLocalAPRFilePoolp ;
        mLocalAPRFilePoolp = NULL ;
    }
}

void LLThread::shutdown()
{
    if (isCrashed())
    {
        LL_WARNS("THREAD") << "Shutting down crashed thread named '" << mName << "'" << LL_ENDL;
    }

    // Warning!  If you somehow call the thread destructor from itself,
    // the thread will die in an unclean fashion!
    if (mThreadp)
    {
        if (!isStopped())
        {
            // The thread isn't already stopped
            // First, set the flag that indicates that we're ready to die
            setQuitting();

            //LL_INFOS() << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << LL_ENDL;
            // Now wait a bit for the thread to exit
            // It's unclear whether I should even bother doing this - this destructor
            // should never get called unless we're already stopped, really...
            S32 counter = 0;
            const S32 MAX_WAIT = 600;
            while (counter < MAX_WAIT)
            {
                if (isStopped())
                {
                    break;
                }
                // Sleep for a tenth of a second
                ms_sleep(100);
                yield();
                counter++;
            }
        }

        if (!isStopped())
        {
            // This thread just wouldn't stop, even though we gave it time
            //LL_WARNS() << "LLThread::~LLThread() exiting thread before clean exit!" << LL_ENDL;
            // Put a stake in its heart. (A very hostile method to force a thread to quit)
#if		LL_WINDOWS
            TerminateThread(mNativeHandle, 0);
#else
            pthread_cancel(mNativeHandle);
#endif

            delete mRecorder;
            mRecorder = NULL;
            mStatus = STOPPED;
            return;
        }
        mThreadp = NULL;
    }

    delete mRunCondition;
    mRunCondition = NULL;

    delete mDataLock;
    mDataLock = NULL;

    if (mRecorder)
    {
        // missed chance to properly shut down recorder (needs to be done in thread context)
        // probably due to abnormal thread termination
        // so just leak it and remove it from parent
        LLTrace::get_master_thread_recorder()->removeChildRecorder(mRecorder);
    }
}


void LLThread::start()
{
    llassert(isStopped());
    
    // Set thread state to running
    mStatus = RUNNING;

    try
    {
        mThreadp = new std::thread(std::bind(&LLThread::threadRun, this));
        mNativeHandle = mThreadp->native_handle();
    }
    catch (std::system_error& ex)
    {
        mStatus = STOPPED;
        LL_WARNS() << "failed to start thread " << mName << " " << ex.what() << LL_ENDL;
    }

}

//============================================================================
// Called from MAIN THREAD.

// Request that the thread pause/resume.
// The thread will pause when (and if) it calls checkPause()
void LLThread::pause()
{
    if (!mPaused)
    {
        // this will cause the thread to stop execution as soon as checkPause() is called
        mPaused = 1;        // Does not need to be atomic since this is only set/unset from the main thread
    }   
}

void LLThread::unpause()
{
    if (mPaused)
    {
        mPaused = 0;
    }

    wake(); // wake up the thread if necessary
}

// virtual predicate function -- returns true if the thread should wake up, false if it should sleep.
bool LLThread::runCondition(void)
{
    // by default, always run.  Handling of pause/unpause is done regardless of this function's result.
    return true;
}

//============================================================================
// Called from run() (CHILD THREAD).
// Stop thread execution if requested until unpaused.
void LLThread::checkPause()
{
    LL_PROFILER_THREAD_BEGIN(mName.c_str())

    mDataLock->lock();

    // This is in a while loop because the pthread API allows for spurious wakeups.
    while(shouldSleep())
    {
        mDataLock->unlock();
        mRunCondition->wait(); // unlocks mRunCondition
        mDataLock->lock();
        // mRunCondition is locked when the thread wakes up
    }
    
    mDataLock->unlock();

    LL_PROFILER_THREAD_END(mName.c_str())
}

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

void LLThread::setQuitting()
{
    mDataLock->lock();
    if (mStatus == RUNNING)
    {
        mStatus = QUITTING;
    }
    // It's only safe to remove mRunCondition if all locked threads were notified
    mRunCondition->broadcast();
    mDataLock->unlock();
}

// static
// <FS:Beq> give this a better chance to inline 
// LLThread::id_t LLThread::currentID()
// {
//     LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
//     return std::this_thread::get_id();
// }

// static
void LLThread::yield()
{
    LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
    std::this_thread::yield();
}

void LLThread::wake()
{
    LL_PROFILER_THREAD_BEGIN(mName.c_str())

    mDataLock->lock();
    if(!shouldSleep())
    {
        mRunCondition->signal();
    }
    mDataLock->unlock();

    LL_PROFILER_THREAD_END(mName.c_str())
}

void LLThread::wakeLocked()
{
    LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
    if(!shouldSleep())
    {
        mRunCondition->signal();
    }
}

void LLThread::lockData()
{
    LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
    mDataLock->lock();
}

void LLThread::unlockData()
{
    LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
    mDataLock->unlock();
}

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

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

//static
LLMutex* LLThreadSafeRefCount::sMutex = 0;

//static
void LLThreadSafeRefCount::initThreadSafeRefCount()
{
    if (!sMutex)
    {
        sMutex = new LLMutex();
    }
}

//static
void LLThreadSafeRefCount::cleanupThreadSafeRefCount()
{
    delete sMutex;
    sMutex = NULL;
}
    

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

LLThreadSafeRefCount::LLThreadSafeRefCount() :
    mRef(0)
{
}

LLThreadSafeRefCount::LLThreadSafeRefCount(const LLThreadSafeRefCount& src)
{
    mRef = 0;
}

LLThreadSafeRefCount::~LLThreadSafeRefCount()
{ 
    if (mRef != 0)
    {
		LL_ERRS() << "deleting referenced object mRef = " << mRef << LL_ENDL;
    }
}

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

LLResponder::~LLResponder()
{
}

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