diff options
Diffstat (limited to 'indra/llcommon/llthread.cpp')
-rw-r--r-- | indra/llcommon/llthread.cpp | 330 |
1 files changed, 330 insertions, 0 deletions
diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp new file mode 100644 index 0000000000..bd2dd7c8f5 --- /dev/null +++ b/indra/llcommon/llthread.cpp @@ -0,0 +1,330 @@ +/** + * @file llthread.cpp + * + * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" +#include "llapr.h" + +#include "llthread.h" + +#include "lltimer.h" + +#if LL_LINUX +#include <sched.h> +#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; +// } +// } +// +//---------------------------------------------------------------------------- + +// +// Handed to the APR thread creation function +// +void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap) +{ + LLThread *threadp = (LLThread *)datap; + + // Set thread state to running + threadp->mStatus = RUNNING; + + // Run the user supplied function + threadp->run(); + + llinfos << "LLThread::staticRun() Exiting: " << threadp->mName << llendl; + + // We're done with the run function, this thread is done executing now. + threadp->mStatus = STOPPED; + + return NULL; +} + + +LLThread::LLThread(const std::string& name, apr_pool_t *poolp) : + mPaused(FALSE), + mName(name), + mAPRThreadp(NULL), + mStatus(STOPPED) +{ + // Thread creation probably CAN be paranoid about APR being initialized, if necessary + if (poolp) + { + mIsLocalPool = FALSE; + mAPRPoolp = poolp; + } + else + { + mIsLocalPool = TRUE; + apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread + } + mRunCondition = new LLCondition(mAPRPoolp); +} + + +LLThread::~LLThread() +{ + // Warning! If you somehow call the thread destructor from itself, + // the thread will die in an unclean fashion! + if (mAPRThreadp) + { + if (!isStopped()) + { + // The thread isn't already stopped + // First, set the flag that indicates that we're ready to die + setQuitting(); + + llinfos << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << llendl; + // Now wait a bit for the thread to exit + // It's unclear whether I should even bother doing this - this destructor + // should netver 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 + llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl; + return; + } + mAPRThreadp = NULL; + } + + delete mRunCondition; + + if (mIsLocalPool) + { + apr_pool_destroy(mAPRPoolp); + } +} + + +void LLThread::start() +{ + apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp); + + // We won't bother joining + apr_thread_detach(mAPRThreadp); +} + +//============================================================================ +// 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() +{ + mRunCondition->lock(); + + // This is in a while loop because the pthread API allows for spurious wakeups. + while(shouldSleep()) + { + mRunCondition->wait(); // unlocks mRunCondition + // mRunCondition is locked when the thread wakes up + } + + mRunCondition->unlock(); +} + +//============================================================================ + +bool LLThread::isQuitting() const +{ + return (QUITTING == mStatus); +} + + +bool LLThread::isStopped() const +{ + return (STOPPED == mStatus); +} + + +void LLThread::setQuitting() +{ + mRunCondition->lock(); + if (mStatus == RUNNING) + { + mStatus = QUITTING; + } + mRunCondition->unlock(); + wake(); +} + + +// static +void LLThread::yield() +{ +#if LL_LINUX + sched_yield(); // annoyingly, apr_thread_yield is a noop on linux... +#else + apr_thread_yield(); +#endif +} + +void LLThread::wake() +{ + mRunCondition->lock(); + if(!shouldSleep()) + { + mRunCondition->signal(); + } + mRunCondition->unlock(); +} + +void LLThread::wakeLocked() +{ + if(!shouldSleep()) + { + mRunCondition->signal(); + } +} + +//============================================================================ + +LLMutex::LLMutex(apr_pool_t *poolp) : + mAPRMutexp(NULL) +{ + if (poolp) + { + mIsLocalPool = FALSE; + mAPRPoolp = poolp; + } + else + { + mIsLocalPool = TRUE; + apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread + } + apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_DEFAULT, mAPRPoolp); +} + + +LLMutex::~LLMutex() +{ +#if _DEBUG + llassert(!isLocked()); // better not be locked! +#endif + apr_thread_mutex_destroy(mAPRMutexp); + mAPRMutexp = NULL; + if (mIsLocalPool) + { + apr_pool_destroy(mAPRPoolp); + } +} + + +void LLMutex::lock() +{ + apr_thread_mutex_lock(mAPRMutexp); +} + +void LLMutex::unlock() +{ + apr_thread_mutex_unlock(mAPRMutexp); +} + +bool LLMutex::isLocked() +{ + apr_status_t status = apr_thread_mutex_trylock(mAPRMutexp); + if (APR_STATUS_IS_EBUSY(status)) + { + return true; + } + else + { + apr_thread_mutex_unlock(mAPRMutexp); + return false; + } +} + +//============================================================================ + +LLCondition::LLCondition(apr_pool_t *poolp) : + LLMutex(poolp) +{ + // base class (LLMutex) has already ensured that mAPRPoolp is set up. + + apr_thread_cond_create(&mAPRCondp, mAPRPoolp); +} + + +LLCondition::~LLCondition() +{ + apr_thread_cond_destroy(mAPRCondp); + mAPRCondp = NULL; +} + + +void LLCondition::wait() +{ + apr_thread_cond_wait(mAPRCondp, mAPRMutexp); +} + +void LLCondition::signal() +{ + apr_thread_cond_signal(mAPRCondp); +} + +void LLCondition::broadcast() +{ + apr_thread_cond_broadcast(mAPRCondp); +} + |