Implement boost and stl mutexes and atomics.
This commit is contained in:
@@ -440,7 +440,7 @@ void AIStateMachine::multiplex(event_type event)
|
|||||||
// our need to run (by us having set need_run), so there is no need to run
|
// our need to run (by us having set need_run), so there is no need to run
|
||||||
// ourselves.
|
// ourselves.
|
||||||
llassert(!mMultiplexMutex.isSelfLocked()); // We may never enter recursively!
|
llassert(!mMultiplexMutex.isSelfLocked()); // We may never enter recursively!
|
||||||
if (!mMultiplexMutex.tryLock())
|
if (!mMultiplexMutex.try_lock())
|
||||||
{
|
{
|
||||||
Dout(dc::statemachine(mSMDebug), "Leaving because it is already being run [" << (void*)this << "]");
|
Dout(dc::statemachine(mSMDebug), "Leaving because it is already being run [" << (void*)this << "]");
|
||||||
return;
|
return;
|
||||||
@@ -762,7 +762,7 @@ void AIStateMachine::multiplex(event_type event)
|
|||||||
//=========================================
|
//=========================================
|
||||||
|
|
||||||
// Release the lock on mMultiplexMutex *first*, before releasing the lock on mState,
|
// Release the lock on mMultiplexMutex *first*, before releasing the lock on mState,
|
||||||
// to avoid to ever call the tryLock() and fail, while this thread isn't still
|
// to avoid to ever call the try_lock() and fail, while this thread isn't still
|
||||||
// BEFORE the critical area of mState!
|
// BEFORE the critical area of mState!
|
||||||
|
|
||||||
mMultiplexMutex.unlock();
|
mMultiplexMutex.unlock();
|
||||||
@@ -1262,7 +1262,7 @@ void AIStateMachine::abort(void)
|
|||||||
multiplex(insert_abort);
|
multiplex(insert_abort);
|
||||||
}
|
}
|
||||||
// Block until the current run finished.
|
// Block until the current run finished.
|
||||||
if (!mRunMutex.tryLock())
|
if (!mRunMutex.try_lock())
|
||||||
{
|
{
|
||||||
llwarns << "AIStateMachine::abort() blocks because the statemachine is still executing code in another thread." << llendl;
|
llwarns << "AIStateMachine::abort() blocks because the statemachine is still executing code in another thread." << llendl;
|
||||||
mRunMutex.lock();
|
mRunMutex.lock();
|
||||||
|
|||||||
@@ -284,6 +284,8 @@ target_link_libraries(
|
|||||||
${WINDOWS_LIBRARIES}
|
${WINDOWS_LIBRARIES}
|
||||||
${Boost_CONTEXT_LIBRARY}
|
${Boost_CONTEXT_LIBRARY}
|
||||||
${Boost_REGEX_LIBRARY}
|
${Boost_REGEX_LIBRARY}
|
||||||
|
${Boost_THREAD_LIBRARY}
|
||||||
|
${Boost_SYSTEM_LIBRARY}
|
||||||
${CORESERVICES_LIBRARY}
|
${CORESERVICES_LIBRARY}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ static F64 const NEVER = 1e16; // 317 million years.
|
|||||||
|
|
||||||
F64 AIFrameTimer::sNextExpiration;
|
F64 AIFrameTimer::sNextExpiration;
|
||||||
AIFrameTimer::timer_list_type AIFrameTimer::sTimerList;
|
AIFrameTimer::timer_list_type AIFrameTimer::sTimerList;
|
||||||
LLMutex AIFrameTimer::sMutex;
|
LLGlobalMutex AIFrameTimer::sMutex;
|
||||||
|
|
||||||
// Notes on thread-safety of AIRunningFrameTimer (continued from aiframetimer.h)
|
// Notes on thread-safety of AIRunningFrameTimer (continued from aiframetimer.h)
|
||||||
//
|
//
|
||||||
@@ -80,11 +80,10 @@ LLMutex AIFrameTimer::sMutex;
|
|||||||
void AIFrameTimer::create(F64 expiration, signal_type::slot_type const& slot)
|
void AIFrameTimer::create(F64 expiration, signal_type::slot_type const& slot)
|
||||||
{
|
{
|
||||||
AIRunningFrameTimer new_timer(expiration, this);
|
AIRunningFrameTimer new_timer(expiration, this);
|
||||||
sMutex.lock();
|
LLMutexLock lock(sMutex);
|
||||||
llassert(mHandle.mRunningTimer == sTimerList.end()); // Create may only be called when the timer isn't already running.
|
llassert(mHandle.mRunningTimer == sTimerList.end()); // Create may only be called when the timer isn't already running.
|
||||||
mHandle.init(sTimerList.insert(new_timer), slot);
|
mHandle.init(sTimerList.insert(new_timer), slot);
|
||||||
sNextExpiration = sTimerList.begin()->expiration();
|
sNextExpiration = sTimerList.begin()->expiration();
|
||||||
sMutex.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AIFrameTimer::cancel(void)
|
void AIFrameTimer::cancel(void)
|
||||||
@@ -95,18 +94,19 @@ void AIFrameTimer::cancel(void)
|
|||||||
// mHandle.mMutex lock), we start with trying to obtain
|
// mHandle.mMutex lock), we start with trying to obtain
|
||||||
// it here and as such wait till the callback function
|
// it here and as such wait till the callback function
|
||||||
// returned.
|
// returned.
|
||||||
mHandle.mMutex.lock();
|
mHandle.mMutex.lock();
|
||||||
// Next we have to grab this lock in order to stop
|
// Next we have to grab this lock in order to stop
|
||||||
// AIFrameTimer::handleExpiration from even entering
|
// AIFrameTimer::handleExpiration from even entering
|
||||||
// in the case we manage to get it first.
|
// in the case we manage to get it first.
|
||||||
sMutex.lock();
|
|
||||||
if (mHandle.mRunningTimer != sTimerList.end())
|
|
||||||
{
|
{
|
||||||
sTimerList.erase(mHandle.mRunningTimer);
|
LLMutexLock lock(sMutex);
|
||||||
mHandle.mRunningTimer = sTimerList.end();
|
if (mHandle.mRunningTimer != sTimerList.end())
|
||||||
sNextExpiration = sTimerList.empty() ? NEVER : sTimerList.begin()->expiration();
|
{
|
||||||
|
sTimerList.erase(mHandle.mRunningTimer);
|
||||||
|
mHandle.mRunningTimer = sTimerList.end();
|
||||||
|
sNextExpiration = sTimerList.empty() ? NEVER : sTimerList.begin()->expiration();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sMutex.unlock();
|
|
||||||
mHandle.mMutex.unlock();
|
mHandle.mMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +164,7 @@ void AIFrameTimer::handleExpiration(F64 current_frame_time)
|
|||||||
//
|
//
|
||||||
// Note that if the other thread actually obtained the sMutex then we
|
// Note that if the other thread actually obtained the sMutex then we
|
||||||
// can't be here: this is still inside the critical area of sMutex.
|
// can't be here: this is still inside the critical area of sMutex.
|
||||||
if (handle.mMutex.tryLock()) // If this fails then another thread is in the process of cancelling this timer, so do nothing.
|
if (handle.mMutex.try_lock()) // If this fails then another thread is in the process of cancelling this timer, so do nothing.
|
||||||
{
|
{
|
||||||
sMutex.unlock();
|
sMutex.unlock();
|
||||||
running_timer->do_callback(); // May not throw exceptions.
|
running_timer->do_callback(); // May not throw exceptions.
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ class LL_COMMON_API AIFrameTimer
|
|||||||
|
|
||||||
typedef std::multiset<AIRunningFrameTimer> timer_list_type;
|
typedef std::multiset<AIRunningFrameTimer> timer_list_type;
|
||||||
|
|
||||||
static LLMutex sMutex; // Mutex for the two global variables below.
|
static LLGlobalMutex sMutex; // Mutex for the two global variables below.
|
||||||
static timer_list_type sTimerList; // List with all running timers.
|
static timer_list_type sTimerList; // List with all running timers.
|
||||||
static F64 sNextExpiration; // Cache of smallest value in sTimerList.
|
static F64 sNextExpiration; // Cache of smallest value in sTimerList.
|
||||||
friend class LLFrameTimer; // Access to sNextExpiration.
|
friend class LLFrameTimer; // Access to sNextExpiration.
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ protected:
|
|||||||
|
|
||||||
// For use by AIThreadSafeDC
|
// For use by AIThreadSafeDC
|
||||||
AIThreadSafe(void) { }
|
AIThreadSafe(void) { }
|
||||||
AIThreadSafe(LLAPRPool& parent) : mRWLock(parent) { }
|
MUTEX_POOL(AIThreadSafe(LLAPRPool& parent) : mRWLock(parent){ })
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Only for use by AITHREADSAFE, see below.
|
// Only for use by AITHREADSAFE, see below.
|
||||||
@@ -473,7 +473,7 @@ protected:
|
|||||||
friend struct AIRegisteredStateMachinesList;
|
friend struct AIRegisteredStateMachinesList;
|
||||||
// For use by AIThreadSafeSimpleDC and AIRegisteredStateMachinesList.
|
// For use by AIThreadSafeSimpleDC and AIRegisteredStateMachinesList.
|
||||||
AIThreadSafeSimple(void) { }
|
AIThreadSafeSimple(void) { }
|
||||||
AIThreadSafeSimple(LLAPRPool& parent) : mMutex(parent) { }
|
MUTEX_POOL(AIThreadSafeSimple(LLAPRPool& parent) : mMutex(parent) { })
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Only for use by AITHREADSAFESIMPLE, see below.
|
// Only for use by AITHREADSAFESIMPLE, see below.
|
||||||
@@ -551,7 +551,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
// For use by AIThreadSafeSimpleDCRootPool
|
// For use by AIThreadSafeSimpleDCRootPool
|
||||||
AIThreadSafeSimpleDC(LLAPRRootPool& parent) : AIThreadSafeSimple<T, MUTEX>(parent) { new (AIThreadSafeSimple<T, MUTEX>::ptr()) T; }
|
AIThreadSafeSimpleDC(LLAPRRootPool& parent) : AIThreadSafeSimple<T, MUTEX>(MUTEX_POOL(parent)) { new (AIThreadSafeSimple<T, MUTEX>::ptr()) T; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper class for AIThreadSafeSimpleDCRootPool to assure initialization of
|
// Helper class for AIThreadSafeSimpleDCRootPool to assure initialization of
|
||||||
@@ -585,7 +585,7 @@ public:
|
|||||||
// as opposed to allocated from the current threads root pool.
|
// as opposed to allocated from the current threads root pool.
|
||||||
AIThreadSafeSimpleDCRootPool(void) :
|
AIThreadSafeSimpleDCRootPool(void) :
|
||||||
AIThreadSafeSimpleDCRootPool_pbase(),
|
AIThreadSafeSimpleDCRootPool_pbase(),
|
||||||
AIThreadSafeSimpleDC<T>(mRootPool) { }
|
AIThreadSafeSimpleDC<T>(MUTEX_POOL(mRootPool)) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -30,60 +30,6 @@
|
|||||||
#include "llapr.h"
|
#include "llapr.h"
|
||||||
#include "llscopedvolatileaprpool.h"
|
#include "llscopedvolatileaprpool.h"
|
||||||
|
|
||||||
LLFastTimer::DeclareTimer FT_WAIT_FOR_SCOPEDLOCK("LLScopedLock");
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
|
||||||
//
|
|
||||||
// LLScopedLock
|
|
||||||
//
|
|
||||||
LLScopedLock::LLScopedLock(apr_thread_mutex_t* mutex) : mMutex(mutex)
|
|
||||||
{
|
|
||||||
mLocked = !!mutex;
|
|
||||||
if (LL_LIKELY(mutex))
|
|
||||||
{
|
|
||||||
apr_status_t status = apr_thread_mutex_trylock(mMutex);
|
|
||||||
while (LL_UNLIKELY(status != APR_SUCCESS))
|
|
||||||
{
|
|
||||||
if (APR_STATUS_IS_EBUSY(status))
|
|
||||||
{
|
|
||||||
if (AIThreadID::in_main_thread_inline())
|
|
||||||
{
|
|
||||||
LLFastTimer ft1(FT_WAIT_FOR_SCOPEDLOCK);
|
|
||||||
status = apr_thread_mutex_lock(mMutex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
status = apr_thread_mutex_lock(mMutex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ll_apr_warn_status(status);
|
|
||||||
mLocked = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LLScopedLock::~LLScopedLock()
|
|
||||||
{
|
|
||||||
unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLScopedLock::unlock()
|
|
||||||
{
|
|
||||||
if(mLocked)
|
|
||||||
{
|
|
||||||
if(!ll_apr_warn_status(apr_thread_mutex_unlock(mMutex)))
|
|
||||||
{
|
|
||||||
mLocked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
|
||||||
|
|
||||||
bool ll_apr_warn_status(apr_status_t status)
|
bool ll_apr_warn_status(apr_status_t status)
|
||||||
{
|
{
|
||||||
if(APR_SUCCESS == status) return false;
|
if(APR_SUCCESS == status) return false;
|
||||||
|
|||||||
@@ -36,55 +36,12 @@
|
|||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
|
||||||
#include "apr_thread_proc.h"
|
#include "apr_thread_proc.h"
|
||||||
#include "apr_thread_mutex.h"
|
|
||||||
#include "apr_getopt.h"
|
#include "apr_getopt.h"
|
||||||
#include "apr_signal.h"
|
|
||||||
#include "apr_atomic.h"
|
|
||||||
#include "llstring.h"
|
#include "llstring.h"
|
||||||
|
|
||||||
class LLAPRPool;
|
class LLAPRPool;
|
||||||
class LLVolatileAPRPool;
|
class LLVolatileAPRPool;
|
||||||
|
|
||||||
/**
|
|
||||||
* @class LLScopedLock
|
|
||||||
* @brief Small class to help lock and unlock mutexes.
|
|
||||||
*
|
|
||||||
* This class is used to have a stack level lock once you already have
|
|
||||||
* an apr mutex handy. The constructor handles the lock, and the
|
|
||||||
* destructor handles the unlock. Instances of this class are
|
|
||||||
* <b>not</b> thread safe.
|
|
||||||
*/
|
|
||||||
class LL_COMMON_API LLScopedLock : private boost::noncopyable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* @brief Constructor which accepts a mutex, and locks it.
|
|
||||||
*
|
|
||||||
* @param mutex An allocated APR mutex. If you pass in NULL,
|
|
||||||
* this wrapper will not lock.
|
|
||||||
*/
|
|
||||||
LLScopedLock(apr_thread_mutex_t* mutex);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Destructor which unlocks the mutex if still locked.
|
|
||||||
*/
|
|
||||||
~LLScopedLock();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Check lock.
|
|
||||||
*/
|
|
||||||
bool isLocked() const { return mLocked; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This method unlocks the mutex.
|
|
||||||
*/
|
|
||||||
void unlock();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool mLocked;
|
|
||||||
apr_thread_mutex_t* mMutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
// File IO convenience functions.
|
// File IO convenience functions.
|
||||||
// Returns NULL if the file fails to open, sets *sizep to file size if not NULL
|
// Returns NULL if the file fails to open, sets *sizep to file size if not NULL
|
||||||
// abbreviated flags
|
// abbreviated flags
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
|
|
||||||
#include "llerror.h"
|
#include "llerror.h"
|
||||||
#include "llaprpool.h"
|
#include "llaprpool.h"
|
||||||
|
#include "llatomic.h"
|
||||||
#include "llthread.h"
|
#include "llthread.h"
|
||||||
|
|
||||||
// Create a subpool from parent.
|
// Create a subpool from parent.
|
||||||
@@ -110,23 +111,18 @@ LLAPRInitialization::LLAPRInitialization(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool LLAPRRootPool::sCountInitialized = false;
|
bool LLAPRRootPool::sCountInitialized = false;
|
||||||
apr_uint32_t volatile LLAPRRootPool::sCount;
|
LLAtomicS32 LLAPRRootPool::sCount;
|
||||||
|
|
||||||
apr_thread_mutex_t* gLogMutexp;
|
|
||||||
apr_thread_mutex_t* gCallStacksLogMutexp;
|
|
||||||
|
|
||||||
LLAPRRootPool::LLAPRRootPool(void) : LLAPRInitialization(), LLAPRPool(0)
|
LLAPRRootPool::LLAPRRootPool(void) : LLAPRInitialization(), LLAPRPool(0)
|
||||||
{
|
{
|
||||||
// sCountInitialized don't need locking because when we get here there is still only a single thread.
|
// sCountInitialized don't need locking because when we get here there is still only a single thread.
|
||||||
if (!sCountInitialized)
|
if (!sCountInitialized)
|
||||||
{
|
{
|
||||||
// Initialize the logging mutex
|
#ifdef NEEDS_APR_ATOMICS
|
||||||
apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool);
|
|
||||||
apr_thread_mutex_create(&gCallStacksLogMutexp, APR_THREAD_MUTEX_UNNESTED, mPool);
|
|
||||||
|
|
||||||
apr_status_t status = apr_atomic_init(mPool);
|
apr_status_t status = apr_atomic_init(mPool);
|
||||||
llassert_always(status == APR_SUCCESS);
|
llassert_always(status == APR_SUCCESS);
|
||||||
apr_atomic_set32(&sCount, 1); // Set to 1 to account for the global root pool.
|
#endif
|
||||||
|
sCount = 1; // Set to 1 to account for the global root pool.
|
||||||
sCountInitialized = true;
|
sCountInitialized = true;
|
||||||
|
|
||||||
// Initialize thread-local APR pool support.
|
// Initialize thread-local APR pool support.
|
||||||
@@ -134,33 +130,16 @@ LLAPRRootPool::LLAPRRootPool(void) : LLAPRInitialization(), LLAPRPool(0)
|
|||||||
// it must be done last, so that sCount is already initialized.
|
// it must be done last, so that sCount is already initialized.
|
||||||
LLThreadLocalData::init();
|
LLThreadLocalData::init();
|
||||||
}
|
}
|
||||||
apr_atomic_inc32(&sCount);
|
sCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
LLAPRRootPool::~LLAPRRootPool()
|
LLAPRRootPool::~LLAPRRootPool()
|
||||||
{
|
{
|
||||||
if (!apr_atomic_dec32(&sCount))
|
if (!--sCount)
|
||||||
{
|
{
|
||||||
// The last pool was destructed. Cleanup remainder of APR.
|
// The last pool was destructed. Cleanup remainder of APR.
|
||||||
LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL;
|
LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL;
|
||||||
|
|
||||||
if (gLogMutexp)
|
|
||||||
{
|
|
||||||
// Clean up the logging mutex
|
|
||||||
|
|
||||||
// All other threads NEED to be done before we clean up APR, so this is okay.
|
|
||||||
apr_thread_mutex_destroy(gLogMutexp);
|
|
||||||
gLogMutexp = NULL;
|
|
||||||
}
|
|
||||||
if (gCallStacksLogMutexp)
|
|
||||||
{
|
|
||||||
// Clean up the logging mutex
|
|
||||||
|
|
||||||
// All other threads NEED to be done before we clean up APR, so this is okay.
|
|
||||||
apr_thread_mutex_destroy(gCallStacksLogMutexp);
|
|
||||||
gCallStacksLogMutexp = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must destroy ALL, and therefore this last LLAPRRootPool, before terminating APR.
|
// Must destroy ALL, and therefore this last LLAPRRootPool, before terminating APR.
|
||||||
static_cast<LLAPRRootPool*>(this)->destroy();
|
static_cast<LLAPRRootPool*>(this)->destroy();
|
||||||
|
|
||||||
|
|||||||
@@ -47,9 +47,11 @@
|
|||||||
|
|
||||||
#include "apr_portable.h"
|
#include "apr_portable.h"
|
||||||
#include "apr_pools.h"
|
#include "apr_pools.h"
|
||||||
|
#include "llatomic.h"
|
||||||
#include "llerror.h"
|
#include "llerror.h"
|
||||||
#include "aithreadid.h"
|
#include "aithreadid.h"
|
||||||
|
|
||||||
|
|
||||||
extern void ll_init_apr();
|
extern void ll_init_apr();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,8 +106,11 @@ public:
|
|||||||
// NEVER destroy a pool that is returned by this function!
|
// NEVER destroy a pool that is returned by this function!
|
||||||
apr_pool_t* operator()(void) const
|
apr_pool_t* operator()(void) const
|
||||||
{
|
{
|
||||||
llassert(mPool);
|
if (mParent)
|
||||||
llassert(mOwner.equals_current_thread());
|
{
|
||||||
|
llassert(mPool);
|
||||||
|
llassert(mOwner.equals_current_thread());
|
||||||
|
}
|
||||||
return mPool;
|
return mPool;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +186,7 @@ private:
|
|||||||
private:
|
private:
|
||||||
// Keep track of how many root pools exist and when the last one is destructed.
|
// Keep track of how many root pools exist and when the last one is destructed.
|
||||||
static bool sCountInitialized;
|
static bool sCountInitialized;
|
||||||
static apr_uint32_t volatile sCount;
|
static LLAtomicS32 sCount;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Return a global root pool that is independent of LLThreadLocalData.
|
// Return a global root pool that is independent of LLThreadLocalData.
|
||||||
|
|||||||
@@ -34,8 +34,29 @@
|
|||||||
#ifndef LL_LLATOMIC_H
|
#ifndef LL_LLATOMIC_H
|
||||||
#define LL_LLATOMIC_H
|
#define LL_LLATOMIC_H
|
||||||
|
|
||||||
#include "apr_atomic.h"
|
#define USE_BOOST_ATOMIC
|
||||||
|
|
||||||
|
//Internal definitions
|
||||||
|
#define NEEDS_APR_ATOMICS do_not_define_manually_thanks
|
||||||
|
#undef NEEDS_APR_ATOMICS
|
||||||
|
|
||||||
|
#if defined(USE_BOOST_ATOMIC)
|
||||||
|
#include "boost/version.hpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//Prefer boost over stl over apr.
|
||||||
|
|
||||||
|
#if defined(USE_BOOST_ATOMIC) && (BOOST_VERSION >= 105200)
|
||||||
|
#include "boost/atomic.hpp"
|
||||||
|
template<typename T>
|
||||||
|
struct impl_atomic_type { typedef boost::atomic<T> type; };
|
||||||
|
#elif defined(USE_STD_ATOMIC) && (__cplusplus >= 201103L || _MSC_VER >= 1800)
|
||||||
|
#include <atomic>
|
||||||
|
template<typename T>
|
||||||
|
struct impl_atomic_type { typedef std::atomic<T> type; };
|
||||||
|
#else
|
||||||
|
#include "apr_atomic.h"
|
||||||
|
#define NEEDS_APR_ATOMICS
|
||||||
template <typename Type> class LLAtomic32
|
template <typename Type> class LLAtomic32
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -54,6 +75,27 @@ public:
|
|||||||
private:
|
private:
|
||||||
apr_uint32_t mData;
|
apr_uint32_t mData;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
#if !defined(NEEDS_APR_ATOMICS)
|
||||||
|
template <typename Type> class LLAtomic32
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLAtomic32(void) { }
|
||||||
|
LLAtomic32(LLAtomic32 const& atom) { mData = Type(atom.mData); }
|
||||||
|
LLAtomic32(Type x) { mData = x; }
|
||||||
|
LLAtomic32& operator=(LLAtomic32 const& atom) { mData = Type(atom.mData); return *this; }
|
||||||
|
|
||||||
|
operator Type() const { return mData; }
|
||||||
|
void operator=(Type x) { mData = x; }
|
||||||
|
void operator-=(Type x) { mData -= x; }
|
||||||
|
void operator+=(Type x) { mData += x; }
|
||||||
|
Type operator++(int) { return mData++; } // Type++
|
||||||
|
bool operator--() { return --mData; } // Returns (--Type != 0)
|
||||||
|
|
||||||
|
private:
|
||||||
|
typename impl_atomic_type<Type>::type mData;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef LLAtomic32<U32> LLAtomicU32;
|
typedef LLAtomic32<U32> LLAtomicU32;
|
||||||
typedef LLAtomic32<S32> LLAtomicS32;
|
typedef LLAtomic32<S32> LLAtomicS32;
|
||||||
|
|||||||
@@ -874,8 +874,8 @@ You get:
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern apr_thread_mutex_t* gLogMutexp;
|
LLGlobalMutex gLogMutex;
|
||||||
extern apr_thread_mutex_t* gCallStacksLogMutexp;
|
LLGlobalMutex gCallStacksLogMutex;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
bool checkLevelMap(const LevelMap& map, const std::string& key,
|
bool checkLevelMap(const LevelMap& map, const std::string& key,
|
||||||
@@ -905,17 +905,15 @@ namespace {
|
|||||||
LogLock::LogLock()
|
LogLock::LogLock()
|
||||||
: mLocked(false), mOK(false)
|
: mLocked(false), mOK(false)
|
||||||
{
|
{
|
||||||
if (!gLogMutexp)
|
if (!gLogMutex.isInitalized())
|
||||||
{
|
{
|
||||||
mOK = true;
|
mOK = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int MAX_RETRIES = 5;
|
const int MAX_RETRIES = 5;
|
||||||
for (int attempts = 0; attempts < MAX_RETRIES; ++attempts)
|
for (int attempts = 0; attempts < MAX_RETRIES; ++attempts)
|
||||||
{
|
{
|
||||||
apr_status_t s = apr_thread_mutex_trylock(gLogMutexp);
|
if (gLogMutex.try_lock())
|
||||||
if (!APR_STATUS_IS_EBUSY(s))
|
|
||||||
{
|
{
|
||||||
mLocked = true;
|
mLocked = true;
|
||||||
mOK = true;
|
mOK = true;
|
||||||
@@ -937,7 +935,7 @@ namespace {
|
|||||||
{
|
{
|
||||||
if (mLocked)
|
if (mLocked)
|
||||||
{
|
{
|
||||||
apr_thread_mutex_unlock(gLogMutexp);
|
gLogMutex.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1299,17 +1297,16 @@ namespace LLError
|
|||||||
CallStacksLogLock::CallStacksLogLock()
|
CallStacksLogLock::CallStacksLogLock()
|
||||||
: mLocked(false), mOK(false)
|
: mLocked(false), mOK(false)
|
||||||
{
|
{
|
||||||
if (!gCallStacksLogMutexp)
|
if (!gCallStacksLogMutex.isInitalized())
|
||||||
{
|
{
|
||||||
mOK = true;
|
mOK = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int MAX_RETRIES = 5;
|
const int MAX_RETRIES = 5;
|
||||||
for (int attempts = 0; attempts < MAX_RETRIES; ++attempts)
|
for (int attempts = 0; attempts < MAX_RETRIES; ++attempts)
|
||||||
{
|
{
|
||||||
apr_status_t s = apr_thread_mutex_trylock(gCallStacksLogMutexp);
|
if (gCallStacksLogMutex.try_lock())
|
||||||
if (!APR_STATUS_IS_EBUSY(s))
|
|
||||||
{
|
{
|
||||||
mLocked = true;
|
mLocked = true;
|
||||||
mOK = true;
|
mOK = true;
|
||||||
@@ -1328,7 +1325,7 @@ namespace LLError
|
|||||||
{
|
{
|
||||||
if (mLocked)
|
if (mLocked)
|
||||||
{
|
{
|
||||||
apr_thread_mutex_unlock(gCallStacksLogMutexp);
|
gCallStacksLogMutex.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,12 +47,14 @@ S32 LLFrameTimer::sFrameCount = 0; // Current frame number (number of fr
|
|||||||
U64 LLFrameTimer::sPrevTotalTime = LLFrameTimer::sStartTotalTime; // Previous (frame) time in microseconds since epoch, updated once per frame.
|
U64 LLFrameTimer::sPrevTotalTime = LLFrameTimer::sStartTotalTime; // Previous (frame) time in microseconds since epoch, updated once per frame.
|
||||||
U64 LLFrameTimer::sFrameDeltaTime = 0; // Microseconds between last two calls to LLFrameTimer::updateFrameTimeAndCount.
|
U64 LLFrameTimer::sFrameDeltaTime = 0; // Microseconds between last two calls to LLFrameTimer::updateFrameTimeAndCount.
|
||||||
// Mutex for the above.
|
// Mutex for the above.
|
||||||
apr_thread_mutex_t* LLFrameTimer::sGlobalMutex;
|
LLGlobalMutex LLFrameTimer::sGlobalMutex;
|
||||||
|
|
||||||
|
bool LLFrameTimer::sFirstFrameTimerCreated;
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void LLFrameTimer::global_initialization(void)
|
void LLFrameTimer::global_initialization(void)
|
||||||
{
|
{
|
||||||
apr_thread_mutex_create(&sGlobalMutex, APR_THREAD_MUTEX_UNNESTED, LLAPRRootPool::get()());
|
sFirstFrameTimerCreated = true;
|
||||||
AIFrameTimer::sNextExpiration = NEVER;
|
AIFrameTimer::sNextExpiration = NEVER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,9 +65,9 @@ void LLFrameTimer::updateFrameTime(void)
|
|||||||
sTotalTime = totalTime();
|
sTotalTime = totalTime();
|
||||||
sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64;
|
sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64;
|
||||||
F64 new_frame_time = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64;
|
F64 new_frame_time = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64;
|
||||||
apr_thread_mutex_lock(sGlobalMutex);
|
sGlobalMutex.lock();
|
||||||
sFrameTime = new_frame_time;
|
sFrameTime = new_frame_time;
|
||||||
apr_thread_mutex_unlock(sGlobalMutex);
|
sGlobalMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|||||||
@@ -36,16 +36,13 @@
|
|||||||
|
|
||||||
#include "lltimer.h"
|
#include "lltimer.h"
|
||||||
#include "timing.h"
|
#include "timing.h"
|
||||||
#include <apr_thread_mutex.h>
|
#include "llthread.h"
|
||||||
#ifdef SHOW_ASSERT
|
|
||||||
#include "aithreadid.h" // is_main_thread()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class LL_COMMON_API LLFrameTimer
|
class LL_COMMON_API LLFrameTimer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Create an LLFrameTimer and start it. After creation it is running and in the state expired (hasExpired will return true).
|
// Create an LLFrameTimer and start it. After creation it is running and in the state expired (hasExpired will return true).
|
||||||
LLFrameTimer(void) : mExpiry(0), mRunning(true), mPaused(false) { if (!sGlobalMutex) global_initialization(); setAge(0.0); }
|
LLFrameTimer(void) : mExpiry(0), mRunning(true), mPaused(false) { if (!sFirstFrameTimerCreated) global_initialization(); setAge(0.0); }
|
||||||
|
|
||||||
//<singu>
|
//<singu>
|
||||||
void copy(LLFrameTimer const& timer) { mStartTime = timer.mStartTime; mExpiry = timer.mExpiry; mRunning = timer.mRunning; mPaused = timer.mPaused; }
|
void copy(LLFrameTimer const& timer) { mStartTime = timer.mStartTime; mExpiry = timer.mExpiry; mRunning = timer.mRunning; mPaused = timer.mPaused; }
|
||||||
@@ -57,9 +54,9 @@ public:
|
|||||||
static F64 getElapsedSeconds(void)
|
static F64 getElapsedSeconds(void)
|
||||||
{
|
{
|
||||||
// Loses msec precision after ~4.5 hours...
|
// Loses msec precision after ~4.5 hours...
|
||||||
apr_thread_mutex_lock(sGlobalMutex);
|
sGlobalMutex.lock();
|
||||||
F64 res = sFrameTime;
|
F64 res = sFrameTime;
|
||||||
apr_thread_mutex_unlock(sGlobalMutex);
|
sGlobalMutex.unlock();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,9 +65,9 @@ public:
|
|||||||
{
|
{
|
||||||
// sTotalTime is only accessed by the main thread, so no locking is necessary.
|
// sTotalTime is only accessed by the main thread, so no locking is necessary.
|
||||||
llassert(is_main_thread());
|
llassert(is_main_thread());
|
||||||
//apr_thread_mutex_lock(sGlobalMutex);
|
//sGlobalMutex.lock();
|
||||||
U64 res = sTotalTime;
|
U64 res = sTotalTime;
|
||||||
//apr_thread_mutex_unlock(sGlobalMutex);
|
//sGlobalMutex.unlock();
|
||||||
llassert(res);
|
llassert(res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -80,9 +77,9 @@ public:
|
|||||||
{
|
{
|
||||||
// sTotalSeconds is only accessed by the main thread, so no locking is necessary.
|
// sTotalSeconds is only accessed by the main thread, so no locking is necessary.
|
||||||
llassert(is_main_thread());
|
llassert(is_main_thread());
|
||||||
//apr_thread_mutex_lock(sGlobalMutex);
|
//sGlobalMutex.lock();
|
||||||
F64 res = sTotalSeconds;
|
F64 res = sTotalSeconds;
|
||||||
//apr_thread_mutex_unlock(sGlobalMutex);
|
//sGlobalMutex.unlock();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,9 +88,9 @@ public:
|
|||||||
{
|
{
|
||||||
// sFrameCount is only accessed by the main thread, so no locking is necessary.
|
// sFrameCount is only accessed by the main thread, so no locking is necessary.
|
||||||
llassert(is_main_thread());
|
llassert(is_main_thread());
|
||||||
//apr_thread_mutex_lock(sGlobalMutex);
|
//sGlobalMutex.lock();
|
||||||
U32 res = sFrameCount;
|
U32 res = sFrameCount;
|
||||||
//apr_thread_mutex_unlock(sGlobalMutex);
|
//sGlobalMutex.unlock();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +167,7 @@ protected:
|
|||||||
//
|
//
|
||||||
|
|
||||||
// More than one thread are accessing (some of) these variables, therefore we need locking.
|
// More than one thread are accessing (some of) these variables, therefore we need locking.
|
||||||
static apr_thread_mutex_t* sGlobalMutex;
|
static LLGlobalMutex sGlobalMutex;
|
||||||
|
|
||||||
// Current time in seconds since application start, updated together with sTotalTime.
|
// Current time in seconds since application start, updated together with sTotalTime.
|
||||||
static F64 sFrameTime;
|
static F64 sFrameTime;
|
||||||
@@ -190,6 +187,8 @@ protected:
|
|||||||
// Current frame number (number of frames since application start).
|
// Current frame number (number of frames since application start).
|
||||||
static S32 sFrameCount;
|
static S32 sFrameCount;
|
||||||
|
|
||||||
|
static bool sFirstFrameTimerCreated;
|
||||||
|
|
||||||
//
|
//
|
||||||
// Member data
|
// Member data
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -33,10 +33,10 @@
|
|||||||
|
|
||||||
/// @brief A global registry of all singletons to prevent duplicate allocations
|
/// @brief A global registry of all singletons to prevent duplicate allocations
|
||||||
/// across shared library boundaries
|
/// across shared library boundaries
|
||||||
class LL_COMMON_API LLSingletonRegistry {
|
class LLSingletonRegistry {
|
||||||
private:
|
private:
|
||||||
typedef std::map<std::string, void *> TypeMap;
|
typedef std::map<std::string, void *> TypeMap;
|
||||||
static TypeMap * sSingletonMap;
|
static LL_COMMON_API TypeMap * sSingletonMap;
|
||||||
|
|
||||||
static void checkInit()
|
static void checkInit()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -364,138 +364,133 @@ LLThreadLocalData& LLThreadLocalData::tldata(void)
|
|||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
LLCondition::LLCondition(LLAPRPool& parent) : LLMutex(parent)
|
#if defined(NEEDS_MUTEX_IMPL)
|
||||||
|
#if defined(USE_WIN32_THREAD)
|
||||||
|
LLMutexImpl::LLMutexImpl()
|
||||||
{
|
{
|
||||||
apr_thread_cond_create(&mAPRCondp, mPool());
|
InitializeCriticalSection(&mMutexImpl); //can throw STATUS_NO_MEMORY
|
||||||
|
}
|
||||||
|
LLMutexImpl::~LLMutexImpl()
|
||||||
|
{
|
||||||
|
DeleteCriticalSection(&mMutexImpl); //nothrow
|
||||||
|
}
|
||||||
|
void LLMutexImpl::lock()
|
||||||
|
{
|
||||||
|
EnterCriticalSection(&mMutexImpl); //can throw EXCEPTION_POSSIBLE_DEADLOCK
|
||||||
|
}
|
||||||
|
void LLMutexImpl::unlock()
|
||||||
|
{
|
||||||
|
LeaveCriticalSection(&mMutexImpl); //nothrow
|
||||||
|
}
|
||||||
|
bool LLMutexImpl::try_lock()
|
||||||
|
{
|
||||||
|
return !!TryEnterCriticalSection(&mMutexImpl); //nothrow
|
||||||
|
}
|
||||||
|
LLConditionVariableImpl::LLConditionVariableImpl()
|
||||||
|
{
|
||||||
|
InitializeConditionVariable(&mConditionVariableImpl);
|
||||||
|
}
|
||||||
|
LLConditionVariableImpl::~LLConditionVariableImpl()
|
||||||
|
{
|
||||||
|
//There is no DeleteConditionVariable
|
||||||
|
}
|
||||||
|
void LLConditionVariableImpl::notify_one()
|
||||||
|
{
|
||||||
|
WakeConditionVariable(&mConditionVariableImpl);
|
||||||
|
}
|
||||||
|
void LLConditionVariableImpl::notify_all()
|
||||||
|
{
|
||||||
|
WakeAllConditionVariable(&mConditionVariableImpl);
|
||||||
|
}
|
||||||
|
void LLConditionVariableImpl::wait(LLMutex& lock)
|
||||||
|
{
|
||||||
|
LLMutex::ImplAdoptMutex impl_adopted_mutex(lock);
|
||||||
|
SleepConditionVariableCS(&mConditionVariableImpl, &lock.native_handle(), INFINITE);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
void APRExceptionThrower(apr_status_t status)
|
||||||
|
{
|
||||||
|
if(status != APR_SUCCESS)
|
||||||
|
{
|
||||||
|
static char buf[256];
|
||||||
|
throw std::logic_error(apr_strerror(status,buf,sizeof(buf)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LLMutexImpl::LLMutexImpl(native_pool_type& pool) : mPool(pool), mMutexImpl(NULL)
|
||||||
LLCondition::~LLCondition()
|
|
||||||
{
|
{
|
||||||
apr_thread_cond_destroy(mAPRCondp);
|
APRExceptionThrower(apr_thread_mutex_create(&mMutexImpl, APR_THREAD_MUTEX_UNNESTED, mPool()));
|
||||||
mAPRCondp = NULL;
|
}
|
||||||
|
LLMutexImpl::~LLMutexImpl()
|
||||||
|
{
|
||||||
|
APRExceptionThrower(apr_thread_mutex_destroy(mMutexImpl));
|
||||||
|
mMutexImpl = NULL;
|
||||||
|
}
|
||||||
|
void LLMutexImpl::lock()
|
||||||
|
{
|
||||||
|
APRExceptionThrower(apr_thread_mutex_lock(mMutexImpl));
|
||||||
|
}
|
||||||
|
void LLMutexImpl::unlock()
|
||||||
|
{
|
||||||
|
APRExceptionThrower(apr_thread_mutex_unlock(mMutexImpl));
|
||||||
|
}
|
||||||
|
bool LLMutexImpl::try_lock()
|
||||||
|
{
|
||||||
|
apr_status_t status = apr_thread_mutex_trylock(mMutexImpl);
|
||||||
|
if(APR_STATUS_IS_EBUSY(status))
|
||||||
|
return false;
|
||||||
|
APRExceptionThrower(status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LLConditionVariableImpl::LLConditionVariableImpl(native_pool_type& pool) : mPool(pool), mConditionVariableImpl(NULL)
|
||||||
|
{
|
||||||
|
APRExceptionThrower(apr_thread_cond_create(&mConditionVariableImpl, mPool()));
|
||||||
|
}
|
||||||
|
LLConditionVariableImpl::~LLConditionVariableImpl()
|
||||||
|
{
|
||||||
|
APRExceptionThrower(apr_thread_cond_destroy(mConditionVariableImpl));
|
||||||
|
}
|
||||||
|
void LLConditionVariableImpl::notify_one()
|
||||||
|
{
|
||||||
|
APRExceptionThrower(apr_thread_cond_signal(mConditionVariableImpl));
|
||||||
|
}
|
||||||
|
void LLConditionVariableImpl::notify_all()
|
||||||
|
{
|
||||||
|
APRExceptionThrower(apr_thread_cond_broadcast(mConditionVariableImpl));
|
||||||
|
}
|
||||||
|
void LLConditionVariableImpl::wait(LLMutex& lock)
|
||||||
|
{
|
||||||
|
LLMutex::ImplAdoptMutex impl_adopted_mutex(lock);
|
||||||
|
APRExceptionThrower(apr_thread_cond_wait(mConditionVariableImpl, lock.native_handle()));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LLFastTimer::DeclareTimer FT_WAIT_FOR_MUTEX("LLMutex::lock()");
|
||||||
|
void LLMutex::lock_main(LLFastTimer::DeclareTimer* timer)
|
||||||
|
{
|
||||||
|
llassert(!isSelfLocked());
|
||||||
|
LLFastTimer ft1(timer ? *timer : FT_WAIT_FOR_MUTEX);
|
||||||
|
LLMutexImpl::lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
LLFastTimer::DeclareTimer FT_WAIT_FOR_CONDITION("LLCondition::wait()");
|
LLFastTimer::DeclareTimer FT_WAIT_FOR_CONDITION("LLCondition::wait()");
|
||||||
|
void LLCondition::wait_main()
|
||||||
void LLCondition::wait()
|
|
||||||
{
|
{
|
||||||
if (AIThreadID::in_main_thread_inline())
|
llassert(isSelfLocked());
|
||||||
|
LLFastTimer ft1(FT_WAIT_FOR_CONDITION);
|
||||||
|
LLConditionVariableImpl::wait(*this);
|
||||||
|
llassert(isSelfLocked());
|
||||||
|
}
|
||||||
|
|
||||||
|
LLFastTimer::DeclareTimer FT_WAIT_FOR_MUTEXLOCK("LLMutexLock::lock()");
|
||||||
|
void LLMutexLock::lock()
|
||||||
|
{
|
||||||
|
if (mMutex)
|
||||||
{
|
{
|
||||||
LLFastTimer ft1(FT_WAIT_FOR_CONDITION);
|
mMutex->lock(&FT_WAIT_FOR_MUTEXLOCK);
|
||||||
apr_thread_cond_wait(mAPRCondp, mAPRMutexp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
apr_thread_cond_wait(mAPRCondp, mAPRMutexp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLCondition::signal()
|
//----------------------------------------------------------------------------
|
||||||
{
|
|
||||||
apr_thread_cond_signal(mAPRCondp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLCondition::broadcast()
|
|
||||||
{
|
|
||||||
apr_thread_cond_broadcast(mAPRCondp);
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
LLMutexBase::LLMutexBase() :
|
|
||||||
mCount(0),
|
|
||||||
mLockingThread(AIThreadID::sNone)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LLMutexBase::isSelfLocked() const
|
|
||||||
{
|
|
||||||
return mLockingThread.equals_current_thread_inline();
|
|
||||||
}
|
|
||||||
|
|
||||||
LLFastTimer::DeclareTimer FT_WAIT_FOR_MUTEX("LLMutexBase::lock()");
|
|
||||||
|
|
||||||
void LLMutexBase::lock()
|
|
||||||
{
|
|
||||||
if (mLockingThread.equals_current_thread_inline())
|
|
||||||
{ //redundant lock
|
|
||||||
mCount++;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)))
|
|
||||||
{
|
|
||||||
if (AIThreadID::in_main_thread_inline())
|
|
||||||
{
|
|
||||||
LLFastTimer ft1(FT_WAIT_FOR_MUTEX);
|
|
||||||
apr_thread_mutex_lock(mAPRMutexp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
apr_thread_mutex_lock(mAPRMutexp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mLockingThread.reset_inline();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LLMutexBase::tryLock()
|
|
||||||
{
|
|
||||||
if (mLockingThread.equals_current_thread_inline())
|
|
||||||
{ //redundant lock
|
|
||||||
mCount++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
bool success = !APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp));
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
mLockingThread.reset_inline();
|
|
||||||
}
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
// non-blocking, but does do a lock/unlock so not free
|
|
||||||
bool LLMutexBase::isLocked() const
|
|
||||||
{
|
|
||||||
if (mLockingThread.equals_current_thread_inline())
|
|
||||||
return false; // A call to lock() won't block.
|
|
||||||
if (APR_STATUS_IS_EBUSY(apr_thread_mutex_trylock(mAPRMutexp)))
|
|
||||||
return true;
|
|
||||||
apr_thread_mutex_unlock(mAPRMutexp);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LLMutexBase::unlock()
|
|
||||||
{
|
|
||||||
if (mCount > 0)
|
|
||||||
{ //not the root unlock
|
|
||||||
mCount--;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mLockingThread = AIThreadID::sNone;
|
|
||||||
|
|
||||||
apr_thread_mutex_unlock(mAPRMutexp);
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
LLThreadSafeRefCount::LLThreadSafeRefCount() :
|
|
||||||
mRef(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
LLThreadSafeRefCount::~LLThreadSafeRefCount()
|
|
||||||
{
|
|
||||||
if (mRef != 0)
|
|
||||||
{
|
|
||||||
llerrs << "deleting non-zero reference" << llendl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
|
|
||||||
LLResponder::~LLResponder()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//============================================================================
|
|
||||||
@@ -27,6 +27,8 @@
|
|||||||
#ifndef LL_LLTHREAD_H
|
#ifndef LL_LLTHREAD_H
|
||||||
#define LL_LLTHREAD_H
|
#define LL_LLTHREAD_H
|
||||||
|
|
||||||
|
#define USE_BOOST_MUTEX
|
||||||
|
|
||||||
#if LL_GNUC
|
#if LL_GNUC
|
||||||
// Needed for is_main_thread() when compiling with optimization (relwithdebinfo).
|
// Needed for is_main_thread() when compiling with optimization (relwithdebinfo).
|
||||||
// It doesn't hurt to just always specify it though.
|
// It doesn't hurt to just always specify it though.
|
||||||
@@ -35,10 +37,9 @@
|
|||||||
|
|
||||||
#include "llapp.h"
|
#include "llapp.h"
|
||||||
#include "llapr.h"
|
#include "llapr.h"
|
||||||
#include "llmemory.h"
|
|
||||||
#include "apr_thread_cond.h"
|
|
||||||
#include "llaprpool.h"
|
#include "llaprpool.h"
|
||||||
#include "llatomic.h"
|
#include "llatomic.h"
|
||||||
|
#include "llmemory.h"
|
||||||
#include "aithreadid.h"
|
#include "aithreadid.h"
|
||||||
|
|
||||||
class LLThread;
|
class LLThread;
|
||||||
@@ -177,126 +178,294 @@ protected:
|
|||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
|
|
||||||
#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
|
#define MUTEX_POOL(arg)
|
||||||
|
|
||||||
#ifdef MUTEX_DEBUG
|
//Internal definitions
|
||||||
// We really shouldn't be using recursive locks. Make sure of that in debug mode.
|
#define NEEDS_MUTEX_IMPL do_not_define_manually_thanks
|
||||||
#define MUTEX_FLAG APR_THREAD_MUTEX_UNNESTED
|
#undef NEEDS_MUTEX_IMPL
|
||||||
|
#define NEEDS_MUTEX_RECURSION do_not_define_manually_thanks
|
||||||
|
#undef NEEDS_MUTEX_RECURSION
|
||||||
|
|
||||||
|
//Prefer boost over stl over windows over apr.
|
||||||
|
|
||||||
|
#if defined(USE_BOOST_MUTEX) && (BOOST_VERSION >= 103400) //condition_variable_any was added in boost 1.34
|
||||||
|
//Define BOOST_SYSTEM_NO_DEPRECATED to avoid system_category() and generic_category() dependencies, as those won't be exported.
|
||||||
|
#define BOOST_SYSTEM_NO_DEPRECATED
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#include <boost/thread/recursive_mutex.hpp>
|
||||||
|
#include <boost/thread/locks.hpp>
|
||||||
|
#include <boost/thread/condition_variable.hpp>
|
||||||
|
typedef boost::recursive_mutex LLMutexImpl;
|
||||||
|
typedef boost::condition_variable_any LLConditionVariableImpl;
|
||||||
|
#elif defined(USE_STD_MUTEX) && (__cplusplus >= 201103L || _MSC_VER >= 1800)
|
||||||
|
#include <mutex>
|
||||||
|
typedef std::recursive_mutex LLMutexImpl;
|
||||||
|
typedef std::condition_variable_any LLConditionVariableImpl;
|
||||||
|
#elif defined(USE_WIN32_MUTEX)
|
||||||
|
typedef CRITICAL_SECTION impl_mutex_handle_type;
|
||||||
|
typedef CONDITION_VARIABLE impl_cond_handle_type;
|
||||||
|
#define NEEDS_MUTEX_IMPL
|
||||||
|
#define NEEDS_MUTEX_RECURSION
|
||||||
#else
|
#else
|
||||||
// Use the fastest platform-optimal lock behavior (can be recursive or non-recursive).
|
//----APR specific------
|
||||||
#define MUTEX_FLAG APR_THREAD_MUTEX_DEFAULT
|
#include "apr_thread_cond.h"
|
||||||
|
#include "apr_thread_mutex.h"
|
||||||
|
#include "apr_signal.h"
|
||||||
|
typedef LLAPRPool native_pool_type;
|
||||||
|
typedef apr_thread_mutex_t* impl_mutex_handle_type;
|
||||||
|
typedef apr_thread_cond_t* impl_cond_handle_type;
|
||||||
|
#undef MUTEX_POOL
|
||||||
|
#undef DEFAULT_POOL
|
||||||
|
#define MUTEX_POOL(arg) arg
|
||||||
|
#define NEEDS_MUTEX_IMPL
|
||||||
|
#define NEEDS_MUTEX_RECURSION
|
||||||
|
//END
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class LL_COMMON_API LLMutexBase
|
#ifdef NEEDS_MUTEX_IMPL
|
||||||
|
|
||||||
|
//Impl classes are not meant to be accessed directly. They must be utilized by a parent classes.
|
||||||
|
// They are designed to be 'clones' of their stl counterparts to facilitate simple drop-in
|
||||||
|
// replacement of underlying implementation (boost,std,apr,critical_sections,etc)
|
||||||
|
// Members and member functions are all private.
|
||||||
|
class LL_COMMON_API LLMutexImpl : private boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
friend class LLMutex;
|
||||||
LLMutexBase() ;
|
friend class LLCondition;
|
||||||
|
friend class LLConditionVariableImpl;
|
||||||
void lock(); // blocks
|
|
||||||
|
typedef impl_mutex_handle_type native_handle_type;
|
||||||
|
|
||||||
|
LLMutexImpl(MUTEX_POOL(native_pool_type& pool));
|
||||||
|
virtual ~LLMutexImpl();
|
||||||
|
void lock();
|
||||||
void unlock();
|
void unlock();
|
||||||
// Returns true if lock was obtained successfully.
|
bool try_lock();
|
||||||
bool tryLock();
|
native_handle_type& native_handle() { return mMutexImpl; }
|
||||||
|
|
||||||
// Returns true if a call to lock() would block (returns false if self-locked()).
|
|
||||||
bool isLocked() const;
|
|
||||||
|
|
||||||
// Returns true if locked by this thread.
|
|
||||||
bool isSelfLocked() const;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// mAPRMutexp is initialized and uninitialized in the derived class.
|
|
||||||
apr_thread_mutex_t* mAPRMutexp;
|
|
||||||
mutable U32 mCount;
|
|
||||||
mutable AIThreadID mLockingThread;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Disallow copy construction and assignment.
|
native_handle_type mMutexImpl;
|
||||||
LLMutexBase(LLMutexBase const&);
|
MUTEX_POOL(native_pool_type mPool);
|
||||||
LLMutexBase& operator=(LLMutexBase const&);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class LL_COMMON_API LLMutex : public LLMutexBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LLMutex(LLAPRPool& parent = LLThread::tldata().mRootPool) : mPool(parent)
|
|
||||||
{
|
|
||||||
apr_thread_mutex_create(&mAPRMutexp, MUTEX_FLAG, mPool());
|
|
||||||
}
|
|
||||||
~LLMutex()
|
|
||||||
{
|
|
||||||
//this assertion erroneously triggers whenever an LLCondition is destroyed
|
|
||||||
//llassert(!isLocked()); // better not be locked!
|
|
||||||
apr_thread_mutex_destroy(mAPRMutexp);
|
|
||||||
mAPRMutexp = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
LLAPRPool mPool;
|
|
||||||
};
|
|
||||||
|
|
||||||
#if APR_HAS_THREADS
|
|
||||||
// No need to use a root pool in this case.
|
|
||||||
typedef LLMutex LLMutexRootPool;
|
|
||||||
#else // APR_HAS_THREADS
|
|
||||||
class LL_COMMON_API LLMutexRootPool : public LLMutexBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LLMutexRootPool(void)
|
|
||||||
{
|
|
||||||
apr_thread_mutex_create(&mAPRMutexp, MUTEX_FLAG, mRootPool());
|
|
||||||
}
|
|
||||||
~LLMutexRootPool()
|
|
||||||
{
|
|
||||||
#if APR_POOL_DEBUG
|
|
||||||
// It is allowed to destruct root pools from a different thread.
|
|
||||||
mRootPool.grab_ownership();
|
|
||||||
#endif
|
#endif
|
||||||
llassert(!isLocked()); // better not be locked!
|
|
||||||
apr_thread_mutex_destroy(mAPRMutexp);
|
class LL_COMMON_API LLMutex : public LLMutexImpl
|
||||||
mAPRMutexp = NULL;
|
{
|
||||||
|
#ifdef NEEDS_MUTEX_IMPL
|
||||||
|
friend class LLConditionVariableImpl;
|
||||||
|
#endif
|
||||||
|
public:
|
||||||
|
LLMutex(MUTEX_POOL(native_pool_type& pool = LLThread::tldata().mRootPool)) : LLMutexImpl(MUTEX_POOL(pool)),
|
||||||
|
#ifdef NEEDS_MUTEX_RECURSION
|
||||||
|
mLockDepth(0),
|
||||||
|
#endif
|
||||||
|
mLockingThread(AIThreadID::sNone)
|
||||||
|
{}
|
||||||
|
~LLMutex()
|
||||||
|
{}
|
||||||
|
|
||||||
|
void lock(LLFastTimer::DeclareTimer* timer = NULL) // blocks
|
||||||
|
{
|
||||||
|
if (inc_lock_if_recursive())
|
||||||
|
return;
|
||||||
|
if (AIThreadID::in_main_thread_inline() && LLApp::isRunning())
|
||||||
|
{
|
||||||
|
if (!LLMutexImpl::try_lock())
|
||||||
|
{
|
||||||
|
lock_main(timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LLMutexImpl::lock();
|
||||||
|
}
|
||||||
|
mLockingThread.reset_inline();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
void unlock()
|
||||||
LLAPRRootPool mRootPool;
|
{
|
||||||
|
#ifdef NEEDS_MUTEX_RECURSION
|
||||||
|
if (mLockDepth > 0)
|
||||||
|
{
|
||||||
|
--mLockDepth;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
mLockingThread = AIThreadID::sNone;
|
||||||
|
LLMutexImpl::unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if lock was obtained successfully.
|
||||||
|
bool try_lock()
|
||||||
|
{
|
||||||
|
if (inc_lock_if_recursive())
|
||||||
|
return true;
|
||||||
|
if (!LLMutexImpl::try_lock())
|
||||||
|
return false;
|
||||||
|
mLockingThread.reset_inline();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if locked not by this thread
|
||||||
|
bool isLocked()
|
||||||
|
{
|
||||||
|
if (isSelfLocked())
|
||||||
|
return false;
|
||||||
|
if (LLMutexImpl::try_lock())
|
||||||
|
{
|
||||||
|
LLMutexImpl::unlock();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Returns true if locked by this thread.
|
||||||
|
bool isSelfLocked() const
|
||||||
|
{
|
||||||
|
return mLockingThread.equals_current_thread_inline();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef NEEDS_MUTEX_IMPL
|
||||||
|
//This is important for libraries that we cannot pass LLMutex into.
|
||||||
|
//For example, apr wait. apr wait unlocks and re-locks the thread, however
|
||||||
|
// it has no knowledge of LLMutex::mLockingThread and LLMutex::mLockDepth,
|
||||||
|
// and thus will leave those member variables set even after the wait internally releases the lock.
|
||||||
|
// Leaving those two variables set even when mutex has actually been unlocked via apr is BAD.
|
||||||
|
friend class ImplAdoptMutex;
|
||||||
|
class ImplAdoptMutex
|
||||||
|
{
|
||||||
|
friend class LLConditionVariableImpl;
|
||||||
|
ImplAdoptMutex(LLMutex& mutex) : mMutex(mutex),
|
||||||
|
#ifdef NEEDS_MUTEX_RECURSION
|
||||||
|
mLockDepth(mutex.mLockDepth),
|
||||||
|
#endif
|
||||||
|
mLockingThread(mutex.mLockingThread)
|
||||||
|
|
||||||
|
{
|
||||||
|
mMutex.mLockingThread = AIThreadID::sNone;
|
||||||
|
#ifdef NEEDS_MUTEX_RECURSION
|
||||||
|
mMutex.mLockDepth = 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
~ImplAdoptMutex()
|
||||||
|
{
|
||||||
|
mMutex.mLockingThread = mLockingThread;
|
||||||
|
#ifdef NEEDS_MUTEX_RECURSION
|
||||||
|
mMutex.mLockDepth = mLockDepth;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
LLMutex& mMutex;
|
||||||
|
AIThreadID mLockingThread;
|
||||||
|
#ifdef NEEDS_MUTEX_RECURSION
|
||||||
|
S32 mLockDepth;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
void lock_main(LLFastTimer::DeclareTimer* timer);
|
||||||
|
|
||||||
|
bool inc_lock_if_recursive()
|
||||||
|
{
|
||||||
|
#ifdef NEEDS_MUTEX_RECURSION
|
||||||
|
if (isSelfLocked())
|
||||||
|
{
|
||||||
|
mLockDepth++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutable AIThreadID mLockingThread;
|
||||||
|
#ifdef NEEDS_MUTEX_RECURSION
|
||||||
|
LLAtomicS32 mLockDepth;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
#endif // APR_HAS_THREADS
|
|
||||||
|
class LLGlobalMutex : public LLMutex
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LLGlobalMutex() : LLMutex(MUTEX_POOL(LLAPRRootPool::get())), mbInitalized(true)
|
||||||
|
{}
|
||||||
|
bool isInitalized() const
|
||||||
|
{
|
||||||
|
return mbInitalized;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
bool mbInitalized;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef NEEDS_MUTEX_IMPL
|
||||||
|
class LL_COMMON_API LLConditionVariableImpl : private boost::noncopyable
|
||||||
|
{
|
||||||
|
friend class LLCondition;
|
||||||
|
|
||||||
|
typedef impl_cond_handle_type native_handle_type;
|
||||||
|
|
||||||
|
LLConditionVariableImpl(MUTEX_POOL(native_pool_type& pool));
|
||||||
|
virtual ~LLConditionVariableImpl();
|
||||||
|
void notify_one();
|
||||||
|
void notify_all();
|
||||||
|
void wait(LLMutex& lock);
|
||||||
|
native_handle_type& native_handle() { return mConditionVariableImpl; }
|
||||||
|
|
||||||
|
native_handle_type mConditionVariableImpl;
|
||||||
|
MUTEX_POOL(native_pool_type mPool);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef LLMutex LLMutexRootPool;
|
||||||
|
|
||||||
// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
|
// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
|
||||||
class LL_COMMON_API LLCondition : public LLMutex
|
class LLCondition : public LLConditionVariableImpl, public LLMutex
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LLCondition(LLAPRPool& parent = LLThread::tldata().mRootPool);
|
LLCondition(MUTEX_POOL(native_pool_type& pool = LLThread::tldata().mRootPool)) :
|
||||||
~LLCondition();
|
LLMutex(MUTEX_POOL(pool)),
|
||||||
|
LLConditionVariableImpl(MUTEX_POOL(pool))
|
||||||
void wait(); // blocks
|
{}
|
||||||
void signal();
|
~LLCondition()
|
||||||
void broadcast();
|
{}
|
||||||
|
void LL_COMMON_API wait()
|
||||||
protected:
|
{
|
||||||
apr_thread_cond_t *mAPRCondp;
|
if (AIThreadID::in_main_thread_inline())
|
||||||
|
wait_main();
|
||||||
|
else LLConditionVariableImpl::wait(*this);
|
||||||
|
}
|
||||||
|
void signal() { LLConditionVariableImpl::notify_one(); }
|
||||||
|
void broadcast() { LLConditionVariableImpl::notify_all(); }
|
||||||
|
private:
|
||||||
|
void wait_main(); //Cannot be inline. Uses internal fasttimer.
|
||||||
};
|
};
|
||||||
|
|
||||||
class LL_COMMON_API LLMutexLock
|
class LLMutexLock
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LLMutexLock(LLMutexBase* mutex)
|
LLMutexLock(LLMutex* mutex)
|
||||||
{
|
{
|
||||||
mMutex = mutex;
|
mMutex = mutex;
|
||||||
if(mMutex) mMutex->lock();
|
lock();
|
||||||
|
}
|
||||||
|
LLMutexLock(LLMutex& mutex)
|
||||||
|
{
|
||||||
|
mMutex = &mutex;
|
||||||
|
lock();
|
||||||
}
|
}
|
||||||
~LLMutexLock()
|
~LLMutexLock()
|
||||||
{
|
{
|
||||||
if(mMutex) mMutex->unlock();
|
if (mMutex) mMutex->unlock();
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
LLMutexBase* mMutex;
|
LL_COMMON_API void lock(); //Cannot be inline. Uses internal fasttimer.
|
||||||
|
LLMutex* mMutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LL_COMMON_API AIRWLock
|
class AIRWLock
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AIRWLock(LLAPRPool& parent = LLThread::tldata().mRootPool) :
|
AIRWLock(LLAPRPool& parent = LLThread::tldata().mRootPool) :
|
||||||
mWriterWaitingMutex(parent), mNoHoldersCondition(parent), mHoldersCount(0), mWriterIsWaiting(false) { }
|
mWriterWaitingMutex(MUTEX_POOL(parent)), mNoHoldersCondition(MUTEX_POOL(parent)), mHoldersCount(0), mWriterIsWaiting(false) { }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LLMutex mWriterWaitingMutex; //!< This mutex is locked while some writer is waiting for access.
|
LLMutex mWriterWaitingMutex; //!< This mutex is locked while some writer is waiting for access.
|
||||||
@@ -398,7 +567,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
#if LL_DEBUG
|
#if LL_DEBUG
|
||||||
class LL_COMMON_API AINRLock
|
class AINRLock
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
int read_locked;
|
int read_locked;
|
||||||
@@ -409,15 +578,15 @@ private:
|
|||||||
|
|
||||||
void accessed(void) const
|
void accessed(void) const
|
||||||
{
|
{
|
||||||
if (!mAccessed)
|
if (!mAccessed)
|
||||||
{
|
{
|
||||||
mAccessed = true;
|
mAccessed = true;
|
||||||
mTheadID.reset();
|
mTheadID.reset();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
llassert_always(mTheadID.equals_current_thread());
|
llassert_always(mTheadID.equals_current_thread());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -451,22 +620,29 @@ void LLThread::unlockData()
|
|||||||
|
|
||||||
// see llmemory.h for LLPointer<> definition
|
// see llmemory.h for LLPointer<> definition
|
||||||
|
|
||||||
class LL_COMMON_API LLThreadSafeRefCount
|
class LLThreadSafeRefCount
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented
|
LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented
|
||||||
LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented
|
LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~LLThreadSafeRefCount(); // use unref()
|
virtual ~LLThreadSafeRefCount() // use unref()
|
||||||
|
{
|
||||||
|
if (mRef != 0)
|
||||||
|
{
|
||||||
|
llerrs << "deleting non-zero reference" << llendl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LLThreadSafeRefCount();
|
LLThreadSafeRefCount() : mRef(0)
|
||||||
|
{}
|
||||||
|
|
||||||
void ref()
|
void ref()
|
||||||
{
|
{
|
||||||
mRef++;
|
mRef++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void unref()
|
void unref()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -55,44 +55,53 @@ T max_glue(T a, T b)
|
|||||||
return std::max(a, b);
|
return std::max(a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
struct lazy_pow_
|
struct lazy_pow_
|
||||||
{
|
{
|
||||||
template <typename X, typename Y>
|
template<class> struct result;
|
||||||
struct result { typedef X type; };
|
template<class F>
|
||||||
|
struct result
|
||||||
template <typename X, typename Y>
|
{
|
||||||
X operator()(X x, Y y) const
|
typedef T type;
|
||||||
|
};
|
||||||
|
|
||||||
|
T operator()(T x, T y) const
|
||||||
{
|
{
|
||||||
return std::pow(x, y);
|
return std::pow(x, y);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
struct lazy_ufunc_
|
struct lazy_ufunc_
|
||||||
{
|
{
|
||||||
template <typename F, typename A1>
|
template<class> struct result;
|
||||||
struct result { typedef A1 type; };
|
template<class F>
|
||||||
|
struct result
|
||||||
template <typename F, typename A1>
|
|
||||||
A1 operator()(F f, A1 a1) const
|
|
||||||
{
|
{
|
||||||
return f(a1);
|
typedef T type;
|
||||||
|
};
|
||||||
|
|
||||||
|
T operator()(T(*fn)(T), T x) const
|
||||||
|
{
|
||||||
|
return fn(x);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
struct lazy_bfunc_
|
struct lazy_bfunc_
|
||||||
{
|
{
|
||||||
template <typename F, typename A1, typename A2>
|
template<class> struct result;
|
||||||
struct result { typedef A1 type; };
|
template<class F>
|
||||||
|
struct result
|
||||||
template <typename F, typename A1, typename A2>
|
|
||||||
A1 operator()(F f, A1 a1, A2 a2) const
|
|
||||||
{
|
{
|
||||||
return f(a1, a2);
|
typedef T type;
|
||||||
|
};
|
||||||
|
double operator()(T(*fn)(T, T), T x, T y) const
|
||||||
|
{
|
||||||
|
return fn(x, y);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//} // end namespace anonymous
|
|
||||||
|
|
||||||
template <typename FPT, typename Iterator>
|
template <typename FPT, typename Iterator>
|
||||||
struct grammar
|
struct grammar
|
||||||
: boost::spirit::qi::grammar<
|
: boost::spirit::qi::grammar<
|
||||||
@@ -178,9 +187,9 @@ struct grammar
|
|||||||
using boost::spirit::qi::no_case;
|
using boost::spirit::qi::no_case;
|
||||||
using boost::spirit::qi::_val;
|
using boost::spirit::qi::_val;
|
||||||
|
|
||||||
boost::phoenix::function<lazy_pow_> lazy_pow;
|
boost::phoenix::function< lazy_pow_<FPT> > lazy_pow;
|
||||||
boost::phoenix::function<lazy_ufunc_> lazy_ufunc;
|
boost::phoenix::function< lazy_ufunc_<FPT> > lazy_ufunc;
|
||||||
boost::phoenix::function<lazy_bfunc_> lazy_bfunc;
|
boost::phoenix::function< lazy_bfunc_<FPT> > lazy_bfunc;
|
||||||
|
|
||||||
expression =
|
expression =
|
||||||
term [_val = _1]
|
term [_val = _1]
|
||||||
|
|||||||
@@ -1300,7 +1300,7 @@ static int const HTTP_REDIRECTS_DEFAULT = 16; // Singu note: I've seen up to 10
|
|||||||
// This limit is only here to avoid a redirect loop (infinite redirections).
|
// This limit is only here to avoid a redirect loop (infinite redirections).
|
||||||
|
|
||||||
LLChannelDescriptors const BufferedCurlEasyRequest::sChannels;
|
LLChannelDescriptors const BufferedCurlEasyRequest::sChannels;
|
||||||
LLMutex BufferedCurlEasyRequest::sResponderCallbackMutex;
|
LLGlobalMutex BufferedCurlEasyRequest::sResponderCallbackMutex;
|
||||||
bool BufferedCurlEasyRequest::sShuttingDown = false;
|
bool BufferedCurlEasyRequest::sShuttingDown = false;
|
||||||
AIAverage BufferedCurlEasyRequest::sHTTPBandwidth(25);
|
AIAverage BufferedCurlEasyRequest::sHTTPBandwidth(25);
|
||||||
|
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ class BufferedCurlEasyRequest : public CurlEasyRequest {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
static LLChannelDescriptors const sChannels; // Channel object for mInput (channel out()) and mOutput (channel in()).
|
static LLChannelDescriptors const sChannels; // Channel object for mInput (channel out()) and mOutput (channel in()).
|
||||||
static LLMutex sResponderCallbackMutex; // Locked while calling back any overridden ResponderBase::finished and/or accessing sShuttingDown.
|
static LLGlobalMutex sResponderCallbackMutex; // Locked while calling back any overridden ResponderBase::finished and/or accessing sShuttingDown.
|
||||||
static bool sShuttingDown; // If true, no additional calls to ResponderBase::finished will be made anymore.
|
static bool sShuttingDown; // If true, no additional calls to ResponderBase::finished will be made anymore.
|
||||||
static AIAverage sHTTPBandwidth; // HTTP bandwidth usage of all services combined.
|
static AIAverage sHTTPBandwidth; // HTTP bandwidth usage of all services combined.
|
||||||
|
|
||||||
|
|||||||
@@ -1098,10 +1098,10 @@ void AICurlThread::wakeup_thread(bool stop_thread)
|
|||||||
if (stop_thread)
|
if (stop_thread)
|
||||||
mRunning = false; // Thread-safe because all other threads were already stopped.
|
mRunning = false; // Thread-safe because all other threads were already stopped.
|
||||||
|
|
||||||
// Note, we do not want this function to be blocking the calling thread; therefore we only use tryLock()s.
|
// Note, we do not want this function to be blocking the calling thread; therefore we only use try_lock()s.
|
||||||
|
|
||||||
// Stop two threads running the following code concurrently.
|
// Stop two threads running the following code concurrently.
|
||||||
if (!mWakeUpMutex.tryLock())
|
if (!mWakeUpMutex.try_lock())
|
||||||
{
|
{
|
||||||
// If we failed to obtain mWakeUpMutex then another thread is (or was) in AICurlThread::wakeup_thread,
|
// If we failed to obtain mWakeUpMutex then another thread is (or was) in AICurlThread::wakeup_thread,
|
||||||
// or curl was holding the lock for a micro second at the start of process_commands.
|
// or curl was holding the lock for a micro second at the start of process_commands.
|
||||||
@@ -1115,7 +1115,7 @@ void AICurlThread::wakeup_thread(bool stop_thread)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try if curl thread is still awake and if so, pass the new commands directly.
|
// Try if curl thread is still awake and if so, pass the new commands directly.
|
||||||
if (mWakeUpFlagMutex.tryLock())
|
if (mWakeUpFlagMutex.try_lock())
|
||||||
{
|
{
|
||||||
mWakeUpFlag = true;
|
mWakeUpFlag = true;
|
||||||
mWakeUpFlagMutex.unlock();
|
mWakeUpFlagMutex.unlock();
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
|
|
||||||
// constants for poll timeout. if we are threading, we want to have a
|
// constants for poll timeout. if we are threading, we want to have a
|
||||||
// longer poll timeout.
|
// longer poll timeout.
|
||||||
#if LL_THREADS_APR
|
#if LL_THREADS_PUMPIO
|
||||||
static const S32 DEFAULT_POLL_TIMEOUT = 1000;
|
static const S32 DEFAULT_POLL_TIMEOUT = 1000;
|
||||||
#else
|
#else
|
||||||
static const S32 DEFAULT_POLL_TIMEOUT = 0;
|
static const S32 DEFAULT_POLL_TIMEOUT = 0;
|
||||||
@@ -168,24 +168,21 @@ LLPumpIO::LLPumpIO(void) :
|
|||||||
mPollset(NULL),
|
mPollset(NULL),
|
||||||
mPollsetClientID(0),
|
mPollsetClientID(0),
|
||||||
mNextLock(0),
|
mNextLock(0),
|
||||||
|
#if LL_THREADS_PUMPIO
|
||||||
|
mPool(),
|
||||||
|
LLMutex mChainsMutex(initPool()),
|
||||||
|
LLMutex mCallbackMutex(initPool()),
|
||||||
|
#endif
|
||||||
mCurrentPoolReallocCount(0),
|
mCurrentPoolReallocCount(0),
|
||||||
mChainsMutex(NULL),
|
|
||||||
mCallbackMutex(NULL),
|
|
||||||
mCurrentChain(mRunningChains.end())
|
mCurrentChain(mRunningChains.end())
|
||||||
{
|
{
|
||||||
mCurrentChain = mRunningChains.end();
|
#if !LL_THREADS_PUMPIO
|
||||||
|
initPool();
|
||||||
initialize();
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
LLPumpIO::~LLPumpIO()
|
LLPumpIO::~LLPumpIO()
|
||||||
{
|
{
|
||||||
#if LL_THREADS_APR
|
|
||||||
if (mChainsMutex) apr_thread_mutex_destroy(mChainsMutex);
|
|
||||||
if (mCallbackMutex) apr_thread_mutex_destroy(mCallbackMutex);
|
|
||||||
#endif
|
|
||||||
mChainsMutex = NULL;
|
|
||||||
mCallbackMutex = NULL;
|
|
||||||
if(mPollset)
|
if(mPollset)
|
||||||
{
|
{
|
||||||
// lldebugs << "cleaning up pollset" << llendl;
|
// lldebugs << "cleaning up pollset" << llendl;
|
||||||
@@ -218,8 +215,8 @@ bool LLPumpIO::addChain(chain_t const& chain, F32 timeout)
|
|||||||
info.mChainLinks.push_back(link);
|
info.mChainLinks.push_back(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LL_THREADS_APR
|
#if LL_THREADS_PUMPIO
|
||||||
LLScopedLock lock(mChainsMutex);
|
LLMutexLock lock(mChainsMutex);
|
||||||
#endif
|
#endif
|
||||||
mPendingChains.push_back(info);
|
mPendingChains.push_back(info);
|
||||||
return true;
|
return true;
|
||||||
@@ -258,8 +255,8 @@ bool LLPumpIO::addChain(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if LL_THREADS_APR
|
#if LL_THREADS_PUMPIO
|
||||||
LLScopedLock lock(mChainsMutex);
|
LLMutexLock lock(mChainsMutex);
|
||||||
#endif
|
#endif
|
||||||
mPendingChains.push_back(info);
|
mPendingChains.push_back(info);
|
||||||
return true;
|
return true;
|
||||||
@@ -403,8 +400,8 @@ void LLPumpIO::clearLock(S32 key)
|
|||||||
// therefore won't be treading into deleted memory. I think we can
|
// therefore won't be treading into deleted memory. I think we can
|
||||||
// also clear the lock on the chain safely since the pump only
|
// also clear the lock on the chain safely since the pump only
|
||||||
// reads that value.
|
// reads that value.
|
||||||
#if LL_THREADS_APR
|
#if LL_THREADS_PUMPIO
|
||||||
LLScopedLock lock(mChainsMutex);
|
LLMutexLock lock(mChainsMutex);
|
||||||
#endif
|
#endif
|
||||||
mClearLocks.insert(key);
|
mClearLocks.insert(key);
|
||||||
}
|
}
|
||||||
@@ -469,8 +466,8 @@ void LLPumpIO::pump(const S32& poll_timeout)
|
|||||||
PUMP_DEBUG;
|
PUMP_DEBUG;
|
||||||
if(true)
|
if(true)
|
||||||
{
|
{
|
||||||
#if LL_THREADS_APR
|
#if LL_THREADS_PUMPIO
|
||||||
LLScopedLock lock(mChainsMutex);
|
LLMutexLock lock(mChainsMutex);
|
||||||
#endif
|
#endif
|
||||||
// bail if this pump is paused.
|
// bail if this pump is paused.
|
||||||
if(PAUSING == mState)
|
if(PAUSING == mState)
|
||||||
@@ -738,8 +735,8 @@ void LLPumpIO::pump(const S32& poll_timeout)
|
|||||||
|
|
||||||
//bool LLPumpIO::respond(const chain_t& pipes)
|
//bool LLPumpIO::respond(const chain_t& pipes)
|
||||||
//{
|
//{
|
||||||
//#if LL_THREADS_APR
|
//#if LL_THREADS_PUMPIO
|
||||||
// LLScopedLock lock(mCallbackMutex);
|
// LLMutexLock lock(mCallbackMutex);
|
||||||
//#endif
|
//#endif
|
||||||
// LLChainInfo info;
|
// LLChainInfo info;
|
||||||
// links_t links;
|
// links_t links;
|
||||||
@@ -752,8 +749,8 @@ bool LLPumpIO::respond(LLIOPipe* pipe)
|
|||||||
{
|
{
|
||||||
if(NULL == pipe) return false;
|
if(NULL == pipe) return false;
|
||||||
|
|
||||||
#if LL_THREADS_APR
|
#if LL_THREADS_PUMPIO
|
||||||
LLScopedLock lock(mCallbackMutex);
|
LLMutexLock lock(mCallbackMutex);
|
||||||
#endif
|
#endif
|
||||||
LLChainInfo info;
|
LLChainInfo info;
|
||||||
LLLinkInfo link;
|
LLLinkInfo link;
|
||||||
@@ -773,8 +770,8 @@ bool LLPumpIO::respond(
|
|||||||
if(!data) return false;
|
if(!data) return false;
|
||||||
if(links.empty()) return false;
|
if(links.empty()) return false;
|
||||||
|
|
||||||
#if LL_THREADS_APR
|
#if LL_THREADS_PUMPIO
|
||||||
LLScopedLock lock(mCallbackMutex);
|
LLMutexLock lock(mCallbackMutex);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Add the callback response
|
// Add the callback response
|
||||||
@@ -793,8 +790,8 @@ void LLPumpIO::callback()
|
|||||||
//llinfos << "LLPumpIO::callback()" << llendl;
|
//llinfos << "LLPumpIO::callback()" << llendl;
|
||||||
if(true)
|
if(true)
|
||||||
{
|
{
|
||||||
#if LL_THREADS_APR
|
#if LL_THREADS_PUMPIO
|
||||||
LLScopedLock lock(mCallbackMutex);
|
LLMutexLock lock(mCallbackMutex);
|
||||||
#endif
|
#endif
|
||||||
std::copy(
|
std::copy(
|
||||||
mPendingCallbacks.begin(),
|
mPendingCallbacks.begin(),
|
||||||
@@ -821,8 +818,8 @@ void LLPumpIO::callback()
|
|||||||
|
|
||||||
void LLPumpIO::control(LLPumpIO::EControl op)
|
void LLPumpIO::control(LLPumpIO::EControl op)
|
||||||
{
|
{
|
||||||
#if LL_THREADS_APR
|
#if LL_THREADS_PUMPIO
|
||||||
LLScopedLock lock(mChainsMutex);
|
LLMutexLock lock(mChainsMutex);
|
||||||
#endif
|
#endif
|
||||||
switch(op)
|
switch(op)
|
||||||
{
|
{
|
||||||
@@ -838,14 +835,11 @@ void LLPumpIO::control(LLPumpIO::EControl op)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLPumpIO::initialize(void)
|
LLAPRPool& LLPumpIO::initPool()
|
||||||
{
|
{
|
||||||
mPool.create();
|
if (!mPool)
|
||||||
#if LL_THREADS_APR
|
mPool.create();
|
||||||
// SJB: Windows defaults to NESTED and OSX defaults to UNNESTED, so use UNNESTED explicitly.
|
return mPool;
|
||||||
apr_thread_mutex_create(&mChainsMutex, APR_THREAD_MUTEX_UNNESTED, mPool());
|
|
||||||
apr_thread_mutex_create(&mCallbackMutex, APR_THREAD_MUTEX_UNNESTED, mPool());
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLPumpIO::rebuildPollset()
|
void LLPumpIO::rebuildPollset()
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
#include "llrun.h"
|
#include "llrun.h"
|
||||||
|
|
||||||
// Define this to enable use with the APR thread library.
|
// Define this to enable use with the APR thread library.
|
||||||
//#define LL_THREADS_APR 1
|
//#define LL_THREADS_PUMPIO 1
|
||||||
|
|
||||||
// some simple constants to help with timeouts
|
// some simple constants to help with timeouts
|
||||||
extern const F32 DEFAULT_CHAIN_EXPIRY_SECS;
|
extern const F32 DEFAULT_CHAIN_EXPIRY_SECS;
|
||||||
@@ -382,16 +382,13 @@ protected:
|
|||||||
LLAPRPool mCurrentPool;
|
LLAPRPool mCurrentPool;
|
||||||
S32 mCurrentPoolReallocCount;
|
S32 mCurrentPoolReallocCount;
|
||||||
|
|
||||||
#if LL_THREADS_APR
|
#if LL_THREADS_PUMPIO
|
||||||
apr_thread_mutex_t* mChainsMutex;
|
LLMutex mChainsMutex;
|
||||||
apr_thread_mutex_t* mCallbackMutex;
|
LLMutex mCallbackMutex;
|
||||||
#else
|
|
||||||
int* mChainsMutex;
|
|
||||||
int* mCallbackMutex;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void initialize();
|
LLAPRPool& initPool();
|
||||||
|
|
||||||
current_chain_t removeRunningChain(current_chain_t& chain) ;
|
current_chain_t removeRunningChain(current_chain_t& chain) ;
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user