diff options
author | Oz Linden <oz@lindenlab.com> | 2014-03-04 13:15:43 -0500 |
---|---|---|
committer | Oz Linden <oz@lindenlab.com> | 2014-03-04 13:15:43 -0500 |
commit | a373a7442c244712ab17d793072699ef82684816 (patch) | |
tree | 2ed20e24993c4bfbb4f943ed7794fcd06eeb8a23 /indra/llcommon | |
parent | b0c255f4e6141246f3575cb3d5b671af19966de9 (diff) | |
parent | de8fea13627cc5978b8a6135802a52864a11c39a (diff) |
merge changes for 3.7.2-release
Diffstat (limited to 'indra/llcommon')
34 files changed, 1901 insertions, 225 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 3a4a8facc2..8767616a70 100755 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -43,6 +43,7 @@ set(llcommon_SOURCE_FILES llcriticaldamp.cpp llcursortypes.cpp lldate.cpp + lldeadmantimer.cpp lldependencies.cpp lldictionary.cpp llerror.cpp @@ -79,6 +80,7 @@ set(llcommon_SOURCE_FILES llptrto.cpp llprocess.cpp llprocessor.cpp + llprocinfo.cpp llqueuedthread.cpp llrand.cpp llrefcount.cpp @@ -122,6 +124,7 @@ set(llcommon_HEADER_FILES linden_common.h linked_lists.h llaccountingcost.h + llalignedarray.h llallocator.h llallocator_heap_profile.h llagentconstants.h @@ -145,6 +148,7 @@ set(llcommon_HEADER_FILES lldarray.h lldarrayptr.h lldate.h + lldeadmantimer.h lldefs.h lldependencies.h lldeleteutils.h @@ -206,6 +210,7 @@ set(llcommon_HEADER_FILES llpriqueuemap.h llprocess.h llprocessor.h + llprocinfo.h llptrskiplist.h llptrskipmap.h llptrto.h @@ -237,6 +242,7 @@ set(llcommon_HEADER_FILES llstrider.h llstring.h llstringtable.h + llstaticstringtable.h llsys.h llthread.h llthreadsafequeue.h @@ -322,12 +328,14 @@ if (LL_TESTS) LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lldate "" "${test_libs}") + LL_ADD_INTEGRATION_TEST(lldeadmantimer "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lldependencies "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llerror "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llframetimer "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llinstancetracker "" "${test_libs}") LL_ADD_INTEGRATION_TEST(lllazy "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}") + LL_ADD_INTEGRATION_TEST(llprocinfo "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}") LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}") diff --git a/indra/llcommon/llalignedarray.h b/indra/llcommon/llalignedarray.h new file mode 100644 index 0000000000..ed8fd31205 --- /dev/null +++ b/indra/llcommon/llalignedarray.h @@ -0,0 +1,139 @@ +/** + * @file llalignedarray.h + * @brief A static array which obeys alignment restrictions and mimics std::vector accessors. + * + * $LicenseInfo:firstyear=2002&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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$ + */ + +#ifndef LL_LLALIGNEDARRAY_H +#define LL_LLALIGNEDARRAY_H + +#include "llmemory.h" + +template <class T, U32 alignment> +class LLAlignedArray +{ +public: + T* mArray; + U32 mElementCount; + U32 mCapacity; + + LLAlignedArray(); + ~LLAlignedArray(); + + void push_back(const T& elem); + U32 size() const { return mElementCount; } + void resize(U32 size); + T* append(S32 N); + T& operator[](int idx); + const T& operator[](int idx) const; +}; + +template <class T, U32 alignment> +LLAlignedArray<T, alignment>::LLAlignedArray() +{ + llassert(alignment >= 16); + mArray = NULL; + mElementCount = 0; + mCapacity = 0; +} + +template <class T, U32 alignment> +LLAlignedArray<T, alignment>::~LLAlignedArray() +{ + ll_aligned_free(mArray); + mArray = NULL; + mElementCount = 0; + mCapacity = 0; +} + +template <class T, U32 alignment> +void LLAlignedArray<T, alignment>::push_back(const T& elem) +{ + T* old_buf = NULL; + if (mCapacity <= mElementCount) + { + mCapacity++; + mCapacity *= 2; + T* new_buf = (T*) ll_aligned_malloc(mCapacity*sizeof(T), alignment); + if (mArray) + { + ll_memcpy_nonaliased_aligned_16((char*)new_buf, (char*)mArray, sizeof(T)*mElementCount); + } + old_buf = mArray; + mArray = new_buf; + } + + mArray[mElementCount++] = elem; + + //delete old array here to prevent error on a.push_back(a[0]) + ll_aligned_free(old_buf); +} + +template <class T, U32 alignment> +void LLAlignedArray<T, alignment>::resize(U32 size) +{ + if (mCapacity < size) + { + mCapacity = size+mCapacity*2; + T* new_buf = mCapacity > 0 ? (T*) ll_aligned_malloc(mCapacity*sizeof(T), alignment) : NULL; + if (mArray) + { + ll_memcpy_nonaliased_aligned_16((char*) new_buf, (char*) mArray, sizeof(T)*mElementCount); + ll_aligned_free(mArray); + } + + /*for (U32 i = mElementCount; i < mCapacity; ++i) + { + new(new_buf+i) T(); + }*/ + mArray = new_buf; + } + + mElementCount = size; +} + + +template <class T, U32 alignment> +T& LLAlignedArray<T, alignment>::operator[](int idx) +{ + llassert(idx < mElementCount); + return mArray[idx]; +} + +template <class T, U32 alignment> +const T& LLAlignedArray<T, alignment>::operator[](int idx) const +{ + llassert(idx < mElementCount); + return mArray[idx]; +} + +template <class T, U32 alignment> +T* LLAlignedArray<T, alignment>::append(S32 N) +{ + U32 sz = size(); + resize(sz+N); + return &((*this)[sz]); +} + +#endif + diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp index a0802c6adf..b7815b0e35 100755 --- a/indra/llcommon/llapr.cpp +++ b/indra/llcommon/llapr.cpp @@ -226,7 +226,7 @@ void LLVolatileAPRPool::clearVolatileAPRPool() llassert_always(mNumActiveRef > 0) ; } - llassert(mNumTotalRef < (FULL_VOLATILE_APR_POOL << 2)) ; + llassert(mNumTotalRef <= (FULL_VOLATILE_APR_POOL << 2)) ; } BOOL LLVolatileAPRPool::isFull() diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index 752574c65d..1fe7d98472 100755 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -164,14 +164,20 @@ public: ~LLAtomic32<Type>() {}; operator const Type() { apr_uint32_t data = apr_atomic_read32(&mData); return Type(data); } + + Type CurrentValue() const { apr_uint32_t data = apr_atomic_read32(const_cast< volatile apr_uint32_t* >(&mData)); return Type(data); } + Type operator =(const Type& x) { apr_atomic_set32(&mData, apr_uint32_t(x)); return Type(mData); } void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); } void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); } Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++ Type operator --(int) { return apr_atomic_dec32(&mData); } // approximately --Type (0 if final is 0, non-zero otherwise) + + Type operator ++() { return apr_atomic_inc32(&mData); } // Type++ + Type operator --() { return apr_atomic_dec32(&mData); } // approximately --Type (0 if final is 0, non-zero otherwise) private: - apr_uint32_t mData; + volatile apr_uint32_t mData; }; typedef LLAtomic32<U32> LLAtomicU32; diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp index 8be9e4f4de..b938b0e65a 100755 --- a/indra/llcommon/llcommon.cpp +++ b/indra/llcommon/llcommon.cpp @@ -44,6 +44,7 @@ void LLCommon::initClass() } LLTimer::initClass(); LLThreadSafeRefCount::initThreadSafeRefCount(); + assert_main_thread(); // Make sure we record the main thread // LLWorkerThread::initClass(); // LLFrameCallbackManager::initClass(); } diff --git a/indra/llcommon/llcriticaldamp.cpp b/indra/llcommon/llcriticaldamp.cpp index 87d79b1ee0..49aac9ce75 100755 --- a/indra/llcommon/llcriticaldamp.cpp +++ b/indra/llcommon/llcriticaldamp.cpp @@ -87,3 +87,4 @@ F32 LLCriticalDamp::getInterpolant(const F32 time_constant, BOOL use_cache) return interpolant; } + diff --git a/indra/llcommon/lldeadmantimer.cpp b/indra/llcommon/lldeadmantimer.cpp new file mode 100644 index 0000000000..7d9097e344 --- /dev/null +++ b/indra/llcommon/lldeadmantimer.cpp @@ -0,0 +1,188 @@ +/** +* @file lldeadmantimer.cpp +* @brief Simple deadman-switch timer. +* @author monty@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 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 "lldeadmantimer.h" + + +// *TODO: Currently, this uses lltimer functions for its time +// aspects and this leaks into the apis in the U64s/F64s. Would +// like to perhaps switch this over to TSC register-based timers +// sometime and drop the overhead some more. + + +// Flag states and their meaning: +// mActive mDone Meaning +// false false Nothing running, no result available +// true false Timer running, no result available +// false true Timer finished, result can be read once +// true true Not allowed +// +LLDeadmanTimer::LLDeadmanTimer(F64 horizon, bool inc_cpu) + : mHorizon(time_type(llmax(horizon, F64(0.0)) * gClockFrequency)), + mActive(false), // If true, a timer is running. + mDone(false), // If true, timer has completed and can be read (once) + mStarted(U64L(0)), + mExpires(U64L(0)), + mStopped(U64L(0)), + mCount(U64L(0)), + mIncCPU(inc_cpu), + mUStartCPU(LLProcInfo::time_type(U64L(0))), + mUEndCPU(LLProcInfo::time_type(U64L(0))), + mSStartCPU(LLProcInfo::time_type(U64L(0))), + mSEndCPU(LLProcInfo::time_type(U64L(0))) +{} + + +// static +LLDeadmanTimer::time_type LLDeadmanTimer::getNow() +{ + return LLTimer::getCurrentClockCount(); +} + + +void LLDeadmanTimer::start(time_type now) +{ + // *TODO: If active, let's complete an existing timer and save + // the result to the side. I think this will be useful later. + // For now, wipe out anything in progress, start fresh. + + if (! now) + { + now = LLTimer::getCurrentClockCount(); + } + mActive = true; + mDone = false; + mStarted = now; + mExpires = now + mHorizon; + mStopped = now; + mCount = U64L(0); + if (mIncCPU) + { + LLProcInfo::getCPUUsage(mUStartCPU, mSStartCPU); + } +} + + +void LLDeadmanTimer::stop(time_type now) +{ + if (! mActive) + { + return; + } + + if (! now) + { + now = getNow(); + } + mStopped = now; + mActive = false; + mDone = true; + if (mIncCPU) + { + LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU); + } +} + + +bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count, + U64 & user_cpu, U64 & sys_cpu) +{ + const bool status(isExpired(now, started, stopped, count)); + if (status) + { + user_cpu = U64(mUEndCPU - mUStartCPU); + sys_cpu = U64(mSEndCPU - mSStartCPU); + } + return status; +} + + +bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count) +{ + if (mActive && ! mDone) + { + if (! now) + { + now = getNow(); + } + + if (now >= mExpires) + { + // mStopped from ringBell() is the value we want + mActive = false; + mDone = true; + } + } + + if (! mDone) + { + return false; + } + + started = mStarted * gClockFrequencyInv; + stopped = mStopped * gClockFrequencyInv; + count = mCount; + mDone = false; + + return true; +} + + +void LLDeadmanTimer::ringBell(time_type now, unsigned int count) +{ + if (! mActive) + { + return; + } + + if (! now) + { + now = getNow(); + } + + if (now >= mExpires) + { + // Timer has expired, this event will be dropped + mActive = false; + mDone = true; + } + else + { + // Timer renewed, keep going + mStopped = now; + mExpires = now + mHorizon; + mCount += count; + if (mIncCPU) + { + LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU); + } + } + + return; +} + diff --git a/indra/llcommon/lldeadmantimer.h b/indra/llcommon/lldeadmantimer.h new file mode 100644 index 0000000000..980976e176 --- /dev/null +++ b/indra/llcommon/lldeadmantimer.h @@ -0,0 +1,214 @@ +/** +* @file lldeadmantimer.h +* @brief Interface to a simple event timer with a deadman's switch +* @author monty@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 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$ +*/ + +#ifndef LL_DEADMANTIMER_H +#define LL_DEADMANTIMER_H + + +#include "linden_common.h" + +#include "lltimer.h" +#include "llprocinfo.h" + + +/// @file lldeadmantimer.h +/// +/// There are interesting user-experienced events in the viewer that +/// would seem to have well-defined start and stop points but which +/// actually lack such milestones in the code. Such events (like +/// time to load meshes after logging in, initial inventory load, +/// display name fetch) can be defined somewhat after-the-fact by +/// noticing when we no longer perform operations towards their +/// completion. This class is intended to help in such applications. +/// +/// What it implements is a deadman's switch (also known as a +/// keepalive switch and a doorbell switch). The basic operation is +/// as follows: +/// +/// * LLDeadmanTimer is instantiated with a horizon value in seconds, +/// one for each event of interest. +/// * When an event starts, @see start() is invoked to begin a +/// timing operation. +/// * As operations are performed in service of the event (issuing +/// HTTP requests, receiving responses), @see ringBell() is invoked +/// to inform the timer that the operation is still active. +/// * If the operation is canceled or otherwise terminated, @see +/// stop() can be called to end the timing operation. +/// * Concurrent with the ringBell() calls, the program makes +/// periodic (shorter than the horizon but not too short) calls +/// to @see isExpired() to see if the event has expired due to +/// either a stop() call or lack of activity (defined as a ringBell() +/// call in the previous 'horizon' seconds). If it has expired, +/// the caller also receives start, stop and count values for the +/// event which the application can then report in whatever manner +/// it sees fit. +/// * The timer becomes passive after an isExpired() call that returns +/// true. It can then be restarted with a new start() call. +/// +/// Threading: Instances are not thread-safe. They also use +/// timing code from lltimer.h which is also unsafe. +/// +/// Allocation: Not refcounted, may be stack or heap allocated. +/// + +class LL_COMMON_API LLDeadmanTimer +{ +public: + /// Public types + + /// Low-level time type chosen for compatibility with + /// LLTimer::getCurrentClockCount() which is the basis + /// of time operations in this class. This is likely + /// to change in a future version in a move to TSC-based + /// timing. + typedef U64 time_type; + +public: + /// Construct and initialize an LLDeadmanTimer + /// + /// @param horizon Time, in seconds, after the last @see ringBell() + /// call at which point the timer will consider itself + /// expired. + /// + /// @param inc_cpu If true, gather system and user cpu stats while + /// running the timer. This does require more syscalls + /// during updates. If false, cpu usage data isn't + /// collected and will be zero if queried. + LLDeadmanTimer(F64 horizon, bool inc_cpu); + + ~LLDeadmanTimer() + {} + +private: + LLDeadmanTimer(const LLDeadmanTimer &); // Not defined + void operator=(const LLDeadmanTimer &); // Not defined + +public: + /// Get the current time. Zero-basis for this time + /// representation is not defined and is different on + /// different platforms. Do not attempt to compute + /// negative times relative to the first value returned, + /// there may not be enough 'front porch' on the range + /// to prevent wraparound. + /// + /// Note: Implementation is expected to change in a + /// future release as well. + /// + static time_type getNow(); + + /// Begin timing. If the timer is already active, it is reset + /// and timing begins now. + /// + /// @param now Current time as returned by @see + /// LLTimer::getCurrentClockCount(). If zero, + /// method will lookup current time. + /// + void start(time_type now); + + /// End timing. Actively declare the end of the event independent + /// of the deadman's switch operation. @see isExpired() will return + /// true and appropriate values will be returned. + /// + /// @param now Current time as returned by @see + /// LLTimer::getCurrentClockCount(). If zero, + /// method will lookup current time. + /// + void stop(time_type now); + + /// Declare that something interesting happened. This has two + /// effects on an unexpired-timer. 1) The expiration time + /// is extended for 'horizon' seconds after the 'now' value. + /// 2) An internal counter associated with the event is incremented + /// by the @ref count parameter. This count is returned via the + /// @see isExpired() method. + /// + /// @param now Current time as returned by @see + /// LLTimer::getCurrentClockCount(). If zero, + /// method will lookup current time. + /// + /// @param count Count of events to be associated with + /// this bell ringing. + /// + void ringBell(time_type now, unsigned int count); + + /// Checks the status of the timer. If the timer has expired, + /// also returns various timer-related stats. Unlike ringBell(), + /// does not extend the horizon, it only checks for expiration. + /// + /// @param now Current time as returned by @see + /// LLTimer::getCurrentClockCount(). If zero, + /// method will lookup current time. + /// + /// @param started If expired, the starting time of the event is + /// returned to the caller via this reference. + /// + /// @param stopped If expired, the ending time of the event is + /// returned to the caller via this reference. + /// Ending time will be that provided in the + /// stop() method or the last ringBell() call + /// leading to expiration, whichever (stop() call + /// or notice of expiration) happened first. + /// + /// @param count If expired, the number of ringBell() calls + /// made prior to expiration. + /// + /// @param user_cpu Amount of CPU spent in user mode by the process + /// during the event. Value in microseconds and will + /// read zero if not enabled by the constructor. + /// + /// @param sys_cpu Amount of CPU spent in system mode by the process. + /// + /// @return true if the timer has expired, false otherwise. + /// If true, it also returns the started, + /// stopped and count values otherwise these are + /// left unchanged. + /// + bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count, + U64 & user_cpu, U64 & sys_cpu); + + /// Identical to the six-arugment form except it does without the + /// CPU time return if the caller isn't interested in it. + bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count); + +protected: + time_type mHorizon; + bool mActive; + bool mDone; + time_type mStarted; + time_type mExpires; + time_type mStopped; + time_type mCount; + + const bool mIncCPU; // Include CPU metrics in timer + LLProcInfo::time_type mUStartCPU; + LLProcInfo::time_type mUEndCPU; + LLProcInfo::time_type mSStartCPU; + LLProcInfo::time_type mSEndCPU; +}; + + +#endif // LL_DEADMANTIMER_H diff --git a/indra/llcommon/lleventapi.h b/indra/llcommon/lleventapi.h index 1a37d780b6..5991fe8fd5 100755 --- a/indra/llcommon/lleventapi.h +++ b/indra/llcommon/lleventapi.h @@ -47,6 +47,7 @@ class LL_COMMON_API LLEventAPI: public LLDispatchListener, typedef LLInstanceTracker<LLEventAPI, std::string> ibase; public: + /** * @param name LLEventPump name on which this LLEventAPI will listen. This * also serves as the LLInstanceTracker instance key. diff --git a/indra/llcommon/lleventtimer.h b/indra/llcommon/lleventtimer.h index 7f42623d01..dc918121e1 100755 --- a/indra/llcommon/lleventtimer.h +++ b/indra/llcommon/lleventtimer.h @@ -36,6 +36,7 @@ class LL_COMMON_API LLEventTimer : public LLInstanceTracker<LLEventTimer> { public: + LLEventTimer(F32 period); // period is the amount of time between each call to tick() in seconds LLEventTimer(const LLDate& time); virtual ~LLEventTimer(); diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp index 024fdd1b4d..01b6e60d2b 100755 --- a/indra/llcommon/llfasttimer.cpp +++ b/indra/llcommon/llfasttimer.cpp @@ -107,17 +107,13 @@ class NamedTimerFactory : public LLSingleton<NamedTimerFactory> { public: NamedTimerFactory() - : mTimerRoot(NULL) - {} - - /*virtual */ void initSingleton() + : mTimerRoot(new LLFastTimer::NamedTimer("root")) { - mTimerRoot = new LLFastTimer::NamedTimer("root"); mRootFrameState.setNamedTimer(mTimerRoot); mTimerRoot->setFrameState(&mRootFrameState); mTimerRoot->mParent = mTimerRoot; mTimerRoot->setCollapsed(false); - mRootFrameState.mParent = &mRootFrameState; + mRootFrameState.mParent = &mRootFrameState; } ~NamedTimerFactory() diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 81c4b78775..a99a1d88af 100755 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -139,10 +139,11 @@ public: // used to statically declare a new named timer class LL_COMMON_API DeclareTimer - : public LLInstanceTracker<DeclareTimer> + : public LLInstanceTracker< DeclareTimer > { friend class LLFastTimer; public: + DeclareTimer(const std::string& name, bool open); DeclareTimer(const std::string& name); diff --git a/indra/llcommon/llfoldertype.h b/indra/llcommon/llfoldertype.h index a0c847914f..a0c847914f 100755..100644 --- a/indra/llcommon/llfoldertype.h +++ b/indra/llcommon/llfoldertype.h diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h index ae836645b9..03ab0fb67f 100755 --- a/indra/llcommon/llinitparam.h +++ b/indra/llcommon/llinitparam.h @@ -1952,7 +1952,7 @@ namespace LLInitParam class Mandatory : public TypedParam<T, NAME_VALUE_LOOKUP, false> { typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t; - typedef Mandatory<T, NAME_VALUE_LOOKUP> self_t; + typedef Mandatory<T, NAME_VALUE_LOOKUP> self_t; typedef typename super_t::value_t value_t; typedef typename super_t::default_value_t default_value_t; diff --git a/indra/llcommon/llinstancetracker.cpp b/indra/llcommon/llinstancetracker.cpp index 5dc3ea5d7b..64a313b5ff 100755 --- a/indra/llcommon/llinstancetracker.cpp +++ b/indra/llcommon/llinstancetracker.cpp @@ -32,18 +32,3 @@ // external library headers // other Linden headers -//static -void * & LLInstanceTrackerBase::getInstances(std::type_info const & info) -{ - typedef std::map<std::string, void *> InstancesMap; - static InstancesMap instances; - - // std::map::insert() is just what we want here. You attempt to insert a - // (key, value) pair. If the specified key doesn't yet exist, it inserts - // the pair and returns a std::pair of (iterator, true). If the specified - // key DOES exist, insert() simply returns (iterator, false). One lookup - // handles both cases. - return instances.insert(InstancesMap::value_type(info.name(), - InstancesMap::mapped_type())) - .first->second; -} diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 1eab270e3c..55187d8325 100755 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -46,21 +46,7 @@ class LL_COMMON_API LLInstanceTrackerBase { protected: - /// Get a process-unique void* pointer slot for the specified type_info - static void * & getInstances(std::type_info const & info); - /// Find or create a STATICDATA instance for the specified TRACKED class. - /// STATICDATA must be default-constructible. - template<typename STATICDATA, class TRACKED> - static STATICDATA& getStatic() - { - void *& instances = getInstances(typeid(TRACKED)); - if (! instances) - { - instances = new STATICDATA; - } - return *static_cast<STATICDATA*>(instances); - } /// It's not essential to derive your STATICDATA (for use with /// getStatic()) from StaticBase; it's just that both known @@ -74,6 +60,8 @@ protected: }; }; +LL_COMMON_API void assert_main_thread(); + /// This mix-in class adds support for tracking all instances of the specified class parameter T /// The (optional) key associates a value of type KEY with a given instance of T, for quick lookup /// If KEY is not provided, then instances are stored in a simple set @@ -81,14 +69,18 @@ protected: template<typename T, typename KEY = T*> class LLInstanceTracker : public LLInstanceTrackerBase { - typedef LLInstanceTracker<T, KEY> MyT; + typedef LLInstanceTracker<T, KEY> self_t; typedef typename std::map<KEY, T*> InstanceMap; struct StaticData: public StaticBase { InstanceMap sMap; }; - static StaticData& getStatic() { return LLInstanceTrackerBase::getStatic<StaticData, MyT>(); } - static InstanceMap& getMap_() { return getStatic().sMap; } + static StaticData& getStatic() { static StaticData sData; return sData;} + static InstanceMap& getMap_() + { + // assert_main_thread(); fwiw this class is not thread safe, and it used by multiple threads. Bad things happen. + return getStatic().sMap; + } public: class instance_iter : public boost::iterator_facade<instance_iter, T, boost::forward_traversal_tag> @@ -220,7 +212,11 @@ private: } void remove_() { - getMap_().erase(mInstanceKey); + typename InstanceMap::iterator iter = getMap_().find(mInstanceKey); + if (iter != getMap_().end()) + { + getMap_().erase(iter); + } } private: @@ -232,13 +228,13 @@ private: template<typename T> class LLInstanceTracker<T, T*> : public LLInstanceTrackerBase { - typedef LLInstanceTracker<T, T*> MyT; + typedef LLInstanceTracker<T, T*> self_t; typedef typename std::set<T*> InstanceSet; struct StaticData: public StaticBase { InstanceSet sSet; }; - static StaticData& getStatic() { return LLInstanceTrackerBase::getStatic<StaticData, MyT>(); } + static StaticData& getStatic() { static StaticData sData; return sData; } static InstanceSet& getSet_() { return getStatic().sSet; } public: diff --git a/indra/llcommon/llleap.h b/indra/llcommon/llleap.h index 1a1ad23d39..e33f25e530 100755 --- a/indra/llcommon/llleap.h +++ b/indra/llcommon/llleap.h @@ -32,6 +32,7 @@ class LL_COMMON_API LLLeap: public LLInstanceTracker<LLLeap> { public: + /** * Pass a brief string description, mostly for logging purposes. The desc * need not be unique, but obviously the clearer we can make it, the diff --git a/indra/llcommon/lllslconstants.h b/indra/llcommon/lllslconstants.h index 9f32598e61..926ce32d75 100755 --- a/indra/llcommon/lllslconstants.h +++ b/indra/llcommon/lllslconstants.h @@ -67,6 +67,19 @@ const S32 LSL_PRIM_TEXGEN = 22; const S32 LSL_PRIM_POINT_LIGHT = 23; const S32 LSL_PRIM_CAST_SHADOWS = 24; const S32 LSL_PRIM_GLOW = 25; +const S32 LSL_PRIM_TEXT = 26; +const S32 LSL_PRIM_NAME = 27; +const S32 LSL_PRIM_DESC = 28; +const S32 LSL_PRIM_ROT_LOCAL = 29; +const S32 LSL_PRIM_PHYSICS_SHAPE_TYPE = 30; +const S32 LSL_PRIM_OMEGA = 32; +const S32 LSL_PRIM_POS_LOCAL = 33; +const S32 LSL_PRIM_LINK_TARGET = 34; +const S32 LSL_PRIM_SLICE = 35; + +const S32 LSL_PRIM_PHYSICS_SHAPE_PRIM = 0; +const S32 LSL_PRIM_PHYSICS_SHAPE_NONE = 1; +const S32 LSL_PRIM_PHYSICS_SHAPE_CONVEX = 2; const S32 LSL_PRIM_TYPE_BOX = 0; const S32 LSL_PRIM_TYPE_CYLINDER= 1; @@ -179,6 +192,22 @@ const S32 OBJECT_VELOCITY = 5; const S32 OBJECT_OWNER = 6; const S32 OBJECT_GROUP = 7; const S32 OBJECT_CREATOR = 8; +const S32 OBJECT_RUNNING_SCRIPT_COUNT = 9; +const S32 OBJECT_TOTAL_SCRIPT_COUNT = 10; +const S32 OBJECT_SCRIPT_MEMORY = 11; +const S32 OBJECT_SCRIPT_TIME = 12; +const S32 OBJECT_PRIM_EQUIVALENCE = 13; +const S32 OBJECT_SERVER_COST = 14; +const S32 OBJECT_STREAMING_COST = 15; +const S32 OBJECT_PHYSICS_COST = 16; +const S32 OBJECT_CHARACTER_TIME = 17; +const S32 OBJECT_ROOT = 18; +const S32 OBJECT_ATTACHED_POINT = 19; +const S32 OBJECT_PATHFINDING_TYPE = 20; +const S32 OBJECT_PHYSICS = 21; +const S32 OBJECT_PHANTOM = 22; +const S32 OBJECT_TEMP_ON_REZ = 23; +const S32 OBJECT_RENDER_WEIGHT = 24; // llTextBox() magic token string - yes this is a hack. sue me. char const* const TEXTBOX_MAGIC_TOKEN = "!!llTextBox!!"; diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index e725bdd9fa..61e30f11cc 100755 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -36,19 +36,68 @@ class LLMutex ; #define LL_CHECK_MEMORY #endif +LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment); + +#ifdef SHOW_ASSERT +#define ll_assert_aligned(ptr,alignment) ll_assert_aligned_func(reinterpret_cast<uintptr_t>(ptr),((U32)alignment)) +#else +#define ll_assert_aligned(ptr,alignment) +#endif + +#include <xmmintrin.h> + +template <typename T> T* LL_NEXT_ALIGNED_ADDRESS(T* address) +{ + return reinterpret_cast<T*>( + (reinterpret_cast<uintptr_t>(address) + 0xF) & ~0xF); +} + +template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address) +{ + return reinterpret_cast<T*>( + (reinterpret_cast<uintptr_t>(address) + 0x3F) & ~0x3F); +} + +#if LL_LINUX || LL_DARWIN + +#define LL_ALIGN_PREFIX(x) +#define LL_ALIGN_POSTFIX(x) __attribute__((aligned(x))) + +#elif LL_WINDOWS + +#define LL_ALIGN_PREFIX(x) __declspec(align(x)) +#define LL_ALIGN_POSTFIX(x) + +#else +#error "LL_ALIGN_PREFIX and LL_ALIGN_POSTFIX undefined" +#endif + +#define LL_ALIGN_16(var) LL_ALIGN_PREFIX(16) var LL_ALIGN_POSTFIX(16) + inline void* ll_aligned_malloc( size_t size, int align ) { +#if defined(LL_WINDOWS) + return _aligned_malloc(size, align); +#else void* mem = malloc( size + (align - 1) + sizeof(void*) ); char* aligned = ((char*)mem) + sizeof(void*); aligned += align - ((uintptr_t)aligned & (align - 1)); ((void**)aligned)[-1] = mem; return aligned; +#endif } inline void ll_aligned_free( void* ptr ) { - free( ((void**)ptr)[-1] ); +#if defined(LL_WINDOWS) + _aligned_free(ptr); +#else + if (ptr) + { + free( ((void**)ptr)[-1] ); + } +#endif } #if !LL_USE_TCMALLOC @@ -133,6 +182,78 @@ inline void ll_aligned_free_32(void *p) #endif } + +// Copy words 16-byte blocks from src to dst. Source and destination MUST NOT OVERLAP. +// Source and dest must be 16-byte aligned and size must be multiple of 16. +// +inline void ll_memcpy_nonaliased_aligned_16(char* __restrict dst, const char* __restrict src, size_t bytes) +{ + assert(src != NULL); + assert(dst != NULL); + assert(bytes > 0); + assert((bytes % sizeof(F32))== 0); + ll_assert_aligned(src,16); + ll_assert_aligned(dst,16); + assert((src < dst) ? ((src + bytes) < dst) : ((dst + bytes) < src)); + assert(bytes%16==0); + + char* end = dst + bytes; + + if (bytes > 64) + { + + // Find start of 64b aligned area within block + // + void* begin_64 = LL_NEXT_ALIGNED_ADDRESS_64(dst); + + //at least 64 bytes before the end of the destination, switch to 16 byte copies + void* end_64 = end-64; + + // Prefetch the head of the 64b area now + // + _mm_prefetch((char*)begin_64, _MM_HINT_NTA); + _mm_prefetch((char*)begin_64 + 64, _MM_HINT_NTA); + _mm_prefetch((char*)begin_64 + 128, _MM_HINT_NTA); + _mm_prefetch((char*)begin_64 + 192, _MM_HINT_NTA); + + // Copy 16b chunks until we're 64b aligned + // + while (dst < begin_64) + { + + _mm_store_ps((F32*)dst, _mm_load_ps((F32*)src)); + dst += 16; + src += 16; + } + + // Copy 64b chunks up to your tail + // + // might be good to shmoo the 512b prefetch offset + // (characterize performance for various values) + // + while (dst < end_64) + { + _mm_prefetch((char*)src + 512, _MM_HINT_NTA); + _mm_prefetch((char*)dst + 512, _MM_HINT_NTA); + _mm_store_ps((F32*)dst, _mm_load_ps((F32*)src)); + _mm_store_ps((F32*)(dst + 16), _mm_load_ps((F32*)(src + 16))); + _mm_store_ps((F32*)(dst + 32), _mm_load_ps((F32*)(src + 32))); + _mm_store_ps((F32*)(dst + 48), _mm_load_ps((F32*)(src + 48))); + dst += 64; + src += 64; + } + } + + // Copy remainder 16b tail chunks (or ALL 16b chunks for sub-64b copies) + // + while (dst < end) + { + _mm_store_ps((F32*)dst, _mm_load_ps((F32*)src)); + dst += 16; + src += 16; + } +} + #ifndef __DEBUG_PRIVATE_MEM__ #define __DEBUG_PRIVATE_MEM__ 0 #endif @@ -541,13 +662,7 @@ void LLPrivateMemoryPoolTester::operator delete[](void* addr) // LLSingleton moved to llsingleton.h -LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment); -#ifdef SHOW_ASSERT -#define ll_assert_aligned(ptr,alignment) ll_assert_aligned_func(reinterpret_cast<uintptr_t>(ptr),((U32)alignment)) -#else -#define ll_assert_aligned(ptr,alignment) -#endif #endif diff --git a/indra/llcommon/llprocinfo.cpp b/indra/llcommon/llprocinfo.cpp new file mode 100644 index 0000000000..c00f979b0b --- /dev/null +++ b/indra/llcommon/llprocinfo.cpp @@ -0,0 +1,94 @@ +/** +* @file llprocinfo.cpp +* @brief Process, cpu and resource usage information APIs. +* @author monty@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 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 "llprocinfo.h" + +#if LL_WINDOWS + +#define PSAPI_VERSION 1 +#include "windows.h" +#include "psapi.h" + +#elif LL_DARWIN + +#include <sys/resource.h> +#include <mach/mach.h> + +#else + +#include <sys/time.h> +#include <sys/resource.h> + +#endif // LL_WINDOWS/LL_DARWIN + + +// static +void LLProcInfo::getCPUUsage(time_type & user_time, time_type & system_time) +{ +#if LL_WINDOWS + + HANDLE self(GetCurrentProcess()); // Does not have to be closed + FILETIME ft_dummy, ft_system, ft_user; + + GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user); + ULARGE_INTEGER uli; + uli.u.LowPart = ft_system.dwLowDateTime; + uli.u.HighPart = ft_system.dwHighDateTime; + system_time = uli.QuadPart / U64L(10); // Convert to uS + uli.u.LowPart = ft_user.dwLowDateTime; + uli.u.HighPart = ft_user.dwHighDateTime; + user_time = uli.QuadPart / U64L(10); + +#elif LL_DARWIN + + struct rusage usage; + + if (getrusage(RUSAGE_SELF, &usage)) + { + user_time = system_time = time_type(0U); + return; + } + user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec; + system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec; + +#else // Linux + + struct rusage usage; + + if (getrusage(RUSAGE_SELF, &usage)) + { + user_time = system_time = time_type(0U); + return; + } + user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec; + system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec; + +#endif // LL_WINDOWS/LL_DARWIN/Linux +} + + diff --git a/indra/llcommon/llprocinfo.h b/indra/llcommon/llprocinfo.h new file mode 100644 index 0000000000..e78bcf490a --- /dev/null +++ b/indra/llcommon/llprocinfo.h @@ -0,0 +1,68 @@ +/** +* @file llprocinfo.h +* @brief Interface to process/cpu/resource information services. +* @author monty@lindenlab.com +* +* $LicenseInfo:firstyear=2013&license=viewerlgpl$ +* Second Life Viewer Source Code +* Copyright (C) 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$ +*/ + +#ifndef LL_PROCINFO_H +#define LL_PROCINFO_H + + +#include "linden_common.h" + +/// @file llprocinfo.h +/// +/// Right now, this is really a namespace disguised as a class. +/// It wraps some types and functions to return information about +/// process resource consumption in a non-OS-specific manner. +/// +/// Threading: No instances so that's thread-safe. Implementations +/// of static functions should be thread-safe, they mostly involve +/// direct syscall invocations. +/// +/// Allocation: Not instantiatable. + +class LL_COMMON_API LLProcInfo +{ +public: + /// Public types + + typedef U64 time_type; /// Relative microseconds + +private: + LLProcInfo(); // Not defined + ~LLProcInfo(); // Not defined + LLProcInfo(const LLProcInfo &); // Not defined + void operator=(const LLProcInfo &); // Not defined + +public: + /// Get accumulated system and user CPU time in + /// microseconds. Syscalls involved in every invocation. + /// + /// Threading: expected to be safe. + static void getCPUUsage(time_type & user_time, time_type & system_time); +}; + + +#endif // LL_PROCINFO_H diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp index 8276ec836a..f962485284 100755 --- a/indra/llcommon/llsd.cpp +++ b/indra/llcommon/llsd.cpp @@ -506,6 +506,8 @@ namespace LLSD::array_iterator beginArray() { return mData.begin(); } LLSD::array_iterator endArray() { return mData.end(); } + LLSD::reverse_array_iterator rbeginArray() { return mData.rbegin(); } + LLSD::reverse_array_iterator rendArray() { return mData.rend(); } virtual LLSD::array_const_iterator beginArray() const { return mData.begin(); } virtual LLSD::array_const_iterator endArray() const { return mData.end(); } @@ -947,6 +949,9 @@ LLSD::array_iterator LLSD::endArray() { return makeArray(impl).endArray(); } LLSD::array_const_iterator LLSD::beginArray() const{ return safe(impl).beginArray(); } LLSD::array_const_iterator LLSD::endArray() const { return safe(impl).endArray(); } +LLSD::reverse_array_iterator LLSD::rbeginArray() { return makeArray(impl).rbeginArray(); } +LLSD::reverse_array_iterator LLSD::rendArray() { return makeArray(impl).rendArray(); } + namespace llsd { diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h index 5eb69059ac..deb87d7497 100755 --- a/indra/llcommon/llsd.h +++ b/indra/llcommon/llsd.h @@ -320,11 +320,15 @@ public: typedef std::vector<LLSD>::iterator array_iterator; typedef std::vector<LLSD>::const_iterator array_const_iterator; + typedef std::vector<LLSD>::reverse_iterator reverse_array_iterator; array_iterator beginArray(); array_iterator endArray(); array_const_iterator beginArray() const; array_const_iterator endArray() const; + + reverse_array_iterator rbeginArray(); + reverse_array_iterator rendArray(); //@} /** @name Type Testing */ diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index eb8e2c9456..9b49e52377 100755 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -28,5 +28,4 @@ #include "llsingleton.h" -std::map<std::string, void *> * LLSingletonRegistry::sSingletonMap = NULL; diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 49d99f2cd0..40002313f1 100755 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -30,38 +30,6 @@ #include <typeinfo> #include <boost/noncopyable.hpp> -/// @brief A global registry of all singletons to prevent duplicate allocations -/// across shared library boundaries -class LL_COMMON_API LLSingletonRegistry { - private: - typedef std::map<std::string, void *> TypeMap; - static TypeMap * sSingletonMap; - - static void checkInit() - { - if(sSingletonMap == NULL) - { - sSingletonMap = new TypeMap(); - } - } - - public: - template<typename T> static void * & get() - { - std::string name(typeid(T).name()); - - checkInit(); - - // the first entry of the pair returned by insert will be either the existing - // iterator matching our key, or the newly inserted NULL initialized entry - // see "Insert element" in http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html - TypeMap::iterator result = - sSingletonMap->insert(std::make_pair(name, (void*)NULL)).first; - - return result->second; - } -}; - // LLSingleton implements the getInstance() method part of the Singleton // pattern. It can't make the derived class constructors protected, though, so // you have to do that yourself. @@ -101,20 +69,23 @@ private: } EInitState; // stores pointer to singleton instance - // and tracks initialization state of singleton - struct SingletonInstanceData + struct SingletonLifetimeManager { - EInitState mInitState; - DERIVED_TYPE* mSingletonInstance; - - SingletonInstanceData() - : mSingletonInstance(NULL), - mInitState(UNINITIALIZED) - {} - - ~SingletonInstanceData() + SingletonLifetimeManager() + { + construct(); + } + + static void construct() { - if (mInitState != DELETED) + sData.mInitState = CONSTRUCTING; + sData.mInstance = new DERIVED_TYPE(); + sData.mInitState = INITIALIZING; + } + + ~SingletonLifetimeManager() + { + if (sData.mInitState != DELETED) { deleteSingleton(); } @@ -124,9 +95,8 @@ private: public: virtual ~LLSingleton() { - SingletonInstanceData& data = getData(); - data.mSingletonInstance = NULL; - data.mInitState = DELETED; + sData.mInstance = NULL; + sData.mInitState = DELETED; } /** @@ -151,50 +121,46 @@ public: */ static void deleteSingleton() { - delete getData().mSingletonInstance; - getData().mSingletonInstance = NULL; - getData().mInitState = DELETED; + delete sData.mInstance; + sData.mInstance = NULL; + sData.mInitState = DELETED; } - static SingletonInstanceData& getData() - { - // this is static to cache the lookup results - static void * & registry = LLSingletonRegistry::get<DERIVED_TYPE>(); - - // *TODO - look into making this threadsafe - if(NULL == registry) - { - static SingletonInstanceData data; - registry = &data; - } - - return *static_cast<SingletonInstanceData *>(registry); - } static DERIVED_TYPE* getInstance() { - SingletonInstanceData& data = getData(); + static SingletonLifetimeManager sLifeTimeMgr; - if (data.mInitState == CONSTRUCTING) + switch (sData.mInitState) { + case UNINITIALIZED: + // should never be uninitialized at this point + llassert(false); + return NULL; + case CONSTRUCTING: llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl; - } - - if (data.mInitState == DELETED) - { + return NULL; + case INITIALIZING: + // go ahead and flag ourselves as initialized so we can be reentrant during initialization + sData.mInitState = INITIALIZED; + sData.mInstance->initSingleton(); + return sData.mInstance; + case INITIALIZED: + return sData.mInstance; + case DELETED: llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << llendl; + SingletonLifetimeManager::construct(); + sData.mInitState = INITIALIZED; + sData.mInstance->initSingleton(); + return sData.mInstance; } - - if (!data.mSingletonInstance) - { - data.mInitState = CONSTRUCTING; - data.mSingletonInstance = new DERIVED_TYPE(); - data.mInitState = INITIALIZING; - data.mSingletonInstance->initSingleton(); - data.mInitState = INITIALIZED; - } - - return data.mSingletonInstance; + + return NULL; + } + + static DERIVED_TYPE* getIfExists() + { + return sData.mInstance; } // Reference version of getInstance() @@ -208,18 +174,29 @@ public: // Use this to avoid accessing singletons before the can safely be constructed static bool instanceExists() { - return getData().mInitState == INITIALIZED; + return sData.mInitState == INITIALIZED; } // Has this singleton already been deleted? // Use this to avoid accessing singletons from a static object's destructor static bool destroyed() { - return getData().mInitState == DELETED; + return sData.mInitState == DELETED; } private: + virtual void initSingleton() {} + + struct SingletonData + { + EInitState mInitState; + DERIVED_TYPE* mInstance; + }; + static SingletonData sData; }; +template<typename T> +typename LLSingleton<T>::SingletonData LLSingleton<T>::sData; + #endif diff --git a/indra/llcommon/llstaticstringtable.h b/indra/llcommon/llstaticstringtable.h new file mode 100644 index 0000000000..d7e0e8a08d --- /dev/null +++ b/indra/llcommon/llstaticstringtable.h @@ -0,0 +1,82 @@ +/** + * @file llstringtable.h + * @brief The LLStringTable class provides a _fast_ method for finding + * unique copies of strings. + * + * $LicenseInfo:firstyear=2001&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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$ + */ + +#ifndef LL_STATIC_STRING_TABLE_H +#define LL_STATIC_STRING_TABLE_H + +#include "lldefs.h" +#include <boost/unordered_map.hpp> +#include "llstl.h" + +class LLStaticHashedString +{ +public: + + LLStaticHashedString(const std::string& s) + { + string_hash = makehash(s); + string = s; + } + + const std::string& String() const { return string; } + size_t Hash() const { return string_hash; } + + bool operator==(const LLStaticHashedString& b) const { return Hash() == b.Hash(); } + +protected: + + size_t makehash(const std::string& s) + { + size_t len = s.size(); + const char* c = s.c_str(); + size_t hashval = 0; + for (size_t i=0; i<len; i++) + { + hashval = ((hashval<<5) + hashval) + *c++; + } + return hashval; + } + + std::string string; + size_t string_hash; +}; + +struct LLStaticStringHasher +{ + enum { bucket_size = 8 }; + size_t operator()(const LLStaticHashedString& key_value) const { return key_value.Hash(); } + bool operator()(const LLStaticHashedString& left, const LLStaticHashedString& right) const { return left.Hash() < right.Hash(); } +}; + +template< typename MappedObject > +class LL_COMMON_API LLStaticStringTable + : public boost::unordered_map< LLStaticHashedString, MappedObject, LLStaticStringHasher > +{ +}; + +#endif + diff --git a/indra/llcommon/llstringtable.h b/indra/llcommon/llstringtable.h index 59d7372ed4..ff09e71677 100755 --- a/indra/llcommon/llstringtable.h +++ b/indra/llcommon/llstringtable.h @@ -42,14 +42,6 @@ //# define STRING_TABLE_HASH_MAP 1 #endif -#if STRING_TABLE_HASH_MAP -# if LL_WINDOWS -# include <hash_map> -# else -# include <ext/hash_map> -# endif -#endif - const U32 MAX_STRINGS_LENGTH = 256; class LL_COMMON_API LLStringTableEntry diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index 0730b2ed8b..e63045659e 100755 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -114,6 +114,9 @@ static const F32 MEM_INFO_THROTTLE = 20; static const F32 MEM_INFO_WINDOW = 10*60; #if LL_WINDOWS +// We cannot trust GetVersionEx function on Win8.1 , we should check this value when creating OS string +static const U32 WINNT_WINBLUE = 0x0603; + #ifndef DLLVERSIONINFO typedef struct _DllVersionInfo { @@ -214,6 +217,26 @@ static bool regex_search_no_exc(const S& string, M& match, const R& regex) } } +#if LL_WINDOWS +// GetVersionEx should not works correct with Windows 8.1 and the later version. We need to check this case +static bool check_for_version(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor) +{ + OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 }; + DWORDLONG const dwlConditionMask = VerSetConditionMask( + VerSetConditionMask( + VerSetConditionMask( + 0, VER_MAJORVERSION, VER_GREATER_EQUAL), + VER_MINORVERSION, VER_GREATER_EQUAL), + VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); + + osvi.dwMajorVersion = wMajorVersion; + osvi.dwMinorVersion = wMinorVersion; + osvi.wServicePackMajor = wServicePackMajor; + + return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE; +} +#endif + LLOSInfo::LLOSInfo() : mMajorVer(0), mMinorVer(0), mBuild(0), mOSVersionString("") @@ -222,6 +245,7 @@ LLOSInfo::LLOSInfo() : #if LL_WINDOWS OSVERSIONINFOEX osvi; BOOL bOsVersionInfoEx; + BOOL bShouldUseShellVersion = false; // Try calling GetVersionEx using the OSVERSIONINFOEX structure. ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); @@ -284,11 +308,19 @@ LLOSInfo::LLOSInfo() : } else if(osvi.dwMinorVersion == 2) { + if (check_for_version(HIBYTE(WINNT_WINBLUE), LOBYTE(WINNT_WINBLUE), 0)) + { + mOSStringSimple = "Microsoft Windows 8.1 "; + bShouldUseShellVersion = true; // GetVersionEx failed, going to use shell version + } + else + { if(osvi.wProductType == VER_NT_WORKSTATION) mOSStringSimple = "Microsoft Windows 8 "; else mOSStringSimple = "Windows Server 2012 "; } + } ///get native system info if available.. typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); ///function pointer for loading GetNativeSystemInfo @@ -354,9 +386,8 @@ LLOSInfo::LLOSInfo() : } else { - tmpstr = llformat("%s (Build %d)", - csdversion.c_str(), - (osvi.dwBuildNumber & 0xffff)); + tmpstr = !bShouldUseShellVersion ? llformat("%s (Build %d)", csdversion.c_str(), (osvi.dwBuildNumber & 0xffff)): + llformat("%s (Build %d)", csdversion.c_str(), shell32_build); } mOSString = mOSStringSimple + tmpstr; @@ -392,7 +423,7 @@ LLOSInfo::LLOSInfo() : std::string compatibility_mode; if(got_shell32_version) { - if(osvi.dwMajorVersion != shell32_major || osvi.dwMinorVersion != shell32_minor) + if((osvi.dwMajorVersion != shell32_major || osvi.dwMinorVersion != shell32_minor) && !bShouldUseShellVersion) { compatibility_mode = llformat(" compatibility mode. real ver: %d.%d (Build %d)", shell32_major, diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 1d56a52c32..e67d1bc57b 100755 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -3,7 +3,7 @@ * * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * 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 @@ -67,7 +67,8 @@ LL_COMMON_API void assert_main_thread() static U32 s_thread_id = LLThread::currentID(); if (LLThread::currentID() != s_thread_id) { - llerrs << "Illegal execution outside main thread." << llendl; + llwarns << "Illegal execution from thread id " << (S32) LLThread::currentID() + << " outside main thread " << (S32) s_thread_id << llendl; } } @@ -372,6 +373,36 @@ void LLMutex::lock() #endif } +bool LLMutex::trylock() +{ + if(isSelfLocked()) + { //redundant lock + mCount++; + return true; + } + + apr_status_t status(apr_thread_mutex_trylock(mAPRMutexp)); + if (APR_STATUS_IS_EBUSY(status)) + { + return false; + } + +#if MUTEX_DEBUG + // Have to have the lock before we can access the debug info + U32 id = LLThread::currentID(); + if (mIsLocked[id] != FALSE) + llerrs << "Already locked in Thread: " << id << llendl; + mIsLocked[id] = TRUE; +#endif + +#if LL_DARWIN + mLockingThread = LLThread::currentID(); +#else + mLockingThread = sThreadID; +#endif + return true; +} + void LLMutex::unlock() { if (mCount > 0) @@ -495,15 +526,7 @@ LLThreadSafeRefCount::LLThreadSafeRefCount() : LLThreadSafeRefCount::LLThreadSafeRefCount(const LLThreadSafeRefCount& src) { - if (sMutex) - { - sMutex->lock(); - } mRef = 0; - if (sMutex) - { - sMutex->unlock(); - } } LLThreadSafeRefCount::~LLThreadSafeRefCount() diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 0fb89c5613..8c7143304f 100755 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * 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 @@ -156,7 +156,8 @@ public: virtual ~LLMutex(); void lock(); // blocks - void unlock(); + bool trylock(); // non-blocking, returns true if lock held. + void unlock(); // undefined behavior when called on mutex not being held bool isLocked(); // non-blocking, but does do a lock/unlock so not free bool isSelfLocked(); //return true if locked in a same thread U32 lockingThread() const; //get ID of locking thread @@ -174,6 +175,8 @@ protected: #endif }; +//============================================================================ + // Actually a condition/mutex pair (since each condition needs to be associated with a mutex). class LL_COMMON_API LLCondition : public LLMutex { @@ -189,6 +192,8 @@ protected: apr_thread_cond_t *mAPRCondp; }; +//============================================================================ + class LLMutexLock { public: @@ -210,6 +215,43 @@ private: //============================================================================ +// Scoped locking class similar in function to LLMutexLock but uses +// the trylock() method to conditionally acquire lock without +// blocking. Caller resolves the resulting condition by calling +// the isLocked() method and either punts or continues as indicated. +// +// Mostly of interest to callers needing to avoid stalls who can +// guarantee another attempt at a later time. + +class LLMutexTrylock +{ +public: + LLMutexTrylock(LLMutex* mutex) + : mMutex(mutex), + mLocked(false) + { + if (mMutex) + mLocked = mMutex->trylock(); + } + + ~LLMutexTrylock() + { + if (mMutex && mLocked) + mMutex->unlock(); + } + + bool isLocked() const + { + return mLocked; + } + +private: + LLMutex* mMutex; + bool mLocked; +}; + +//============================================================================ + void LLThread::lockData() { mDataLock->lock(); @@ -242,49 +284,39 @@ public: LLThreadSafeRefCount(const LLThreadSafeRefCount&); LLThreadSafeRefCount& operator=(const LLThreadSafeRefCount& ref) { - if (sMutex) - { - sMutex->lock(); - } mRef = 0; - if (sMutex) - { - sMutex->unlock(); - } return *this; } - - void ref() { - if (sMutex) sMutex->lock(); mRef++; - if (sMutex) sMutex->unlock(); } - S32 unref() + void unref() { llassert(mRef >= 1); - if (sMutex) sMutex->lock(); - S32 res = --mRef; - if (sMutex) sMutex->unlock(); - if (0 == res) - { - delete this; - return 0; + if ((--mRef) == 0) // See note in llapr.h on atomic decrement operator return value. + { + // If we hit zero, the caller should be the only smart pointer owning the object and we can delete it. + // It is technically possible for a vanilla pointer to mess this up, or another thread to + // jump in, find this object, create another smart pointer and end up dangling, but if + // the code is that bad and not thread-safe, it's trouble already. + delete this; } - return res; - } + } + S32 getNumRefs() const { - return mRef; + const S32 currentVal = mRef.CurrentValue(); + return currentVal; } private: - S32 mRef; + LLAtomic32< S32 > mRef; }; + /** * intrusive pointer support for LLThreadSafeRefCount * this allows you to use boost::intrusive_ptr with any LLThreadSafeRefCount-derived type @@ -315,4 +347,6 @@ public: //============================================================================ +extern LL_COMMON_API void assert_main_thread(); + #endif // LL_LLTHREAD_H diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h index 513de0605d..e73741217c 100755 --- a/indra/llcommon/lltimer.h +++ b/indra/llcommon/lltimer.h @@ -146,6 +146,13 @@ static inline time_t time_max() } } +// These are really statics but they've been global for awhile +// and they're material to other timing classes. If you are +// not implementing a timer class, do not use these directly. +extern LL_COMMON_API F64 gClockFrequency; +extern LL_COMMON_API F64 gClockFrequencyInv; +extern LL_COMMON_API F64 gClocksToMicroseconds; + // Correction factor used by time_corrected() above. extern LL_COMMON_API S32 gUTCOffset; diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h deleted file mode 100644 index 6a5ff314e4..0000000000 --- a/indra/llcommon/llversionviewer.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * @file llversionviewer.h - * @brief - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, 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$ - */ - -#ifndef LL_LLVERSIONVIEWER_H -#define LL_LLVERSIONVIEWER_H - -const S32 LL_VERSION_MAJOR = 3; -const S32 LL_VERSION_MINOR = 4; -const S32 LL_VERSION_PATCH = 6; -const S32 LL_VERSION_BUILD = 0; - -const char * const LL_CHANNEL = "Second Life Developer"; - -#if LL_DARWIN -const char * const LL_VERSION_BUNDLE_ID = "com.secondlife.indra.viewer"; -#endif - -#endif diff --git a/indra/llcommon/tests/lldeadmantimer_test.cpp b/indra/llcommon/tests/lldeadmantimer_test.cpp new file mode 100644 index 0000000000..7fd2dde6e0 --- /dev/null +++ b/indra/llcommon/tests/lldeadmantimer_test.cpp @@ -0,0 +1,628 @@ +/** + * @file lldeadmantimer_test.cpp + * @brief Tests for the LLDeadmanTimer class. + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 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 "../lldeadmantimer.h" +#include "../llsd.h" +#include "../lltimer.h" + +#include "../test/lltut.h" + +// Convert between floating point time deltas and U64 time deltas. +// Reflects an implementation detail inside lldeadmantimer.cpp + +static LLDeadmanTimer::time_type float_time_to_u64(F64 delta) +{ + return LLDeadmanTimer::time_type(delta * gClockFrequency); +} + +static F64 u64_time_to_float(LLDeadmanTimer::time_type delta) +{ + return delta * gClockFrequencyInv; +} + + +namespace tut +{ + +struct deadmantimer_test +{ + deadmantimer_test() + { + // LLTimer internals updating + update_clock_frequencies(); + } +}; + +typedef test_group<deadmantimer_test> deadmantimer_group_t; +typedef deadmantimer_group_t::object deadmantimer_object_t; +tut::deadmantimer_group_t deadmantimer_instance("LLDeadmanTimer"); + +// Basic construction test and isExpired() call +template<> template<> +void deadmantimer_object_t::test<1>() +{ + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(10.0, false); + + ensure_equals("WOCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count), false); + ensure_approximately_equals("WOCM t1 - isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WOCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WOCM t1 - isExpired() does not modify count", count, U64L(8)); + } + + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(10.0, true); + + ensure_equals("WCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false); + ensure_approximately_equals("WCM t1 - isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WCM t1 - isExpired() does not modify count", count, U64L(8)); + ensure_equals("WCM t1 - isExpired() does not modify user_cpu", user_cpu, U64L(29000)); + ensure_equals("WCM t1 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); + } +} + + +// Construct with zero horizon - not useful generally but will be useful in testing +template<> template<> +void deadmantimer_object_t::test<2>() +{ + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(0.0, false); // Zero is pre-expired + + ensure_equals("WOCM isExpired() still returns false with 0.0 time ctor()", + timer.isExpired(0, started, stopped, count), false); + } + + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(0.0, true); // Zero is pre-expired + + ensure_equals("WCM isExpired() still returns false with 0.0 time ctor()", + timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false); + } +} + + +// "pre-expired" timer - starting a timer with a 0.0 horizon will result in +// expiration on first test. +template<> template<> +void deadmantimer_object_t::test<3>() +{ + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(0.0, false); + + timer.start(0); + ensure_equals("WOCM isExpired() returns true with 0.0 horizon time", + timer.isExpired(0, started, stopped, count), true); + ensure_approximately_equals("WOCM expired timer with no bell ringing has stopped == started", started, stopped, 8); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(0.0, true); + + timer.start(0); + ensure_equals("WCM isExpired() returns true with 0.0 horizon time", + timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true); + ensure_approximately_equals("WCM expired timer with no bell ringing has stopped == started", started, stopped, 8); + } +} + + +// "pre-expired" timer - bell rings are ignored as we're already expired. +template<> template<> +void deadmantimer_object_t::test<4>() +{ + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(0.0, false); + + timer.start(0); + timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1); + ensure_equals("WOCM isExpired() returns true with 0.0 horizon time after bell ring", + timer.isExpired(0, started, stopped, count), true); + ensure_approximately_equals("WOCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(0.0, true); + + timer.start(0); + timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1); + ensure_equals("WCM isExpired() returns true with 0.0 horizon time after bell ring", + timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true); + ensure_approximately_equals("WCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8); + } +} + + +// start(0) test - unexpired timer reports unexpired +template<> template<> +void deadmantimer_object_t::test<5>() +{ + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(10.0, false); + + timer.start(0); + ensure_equals("WOCM isExpired() returns false after starting with 10.0 horizon time", + timer.isExpired(0, started, stopped, count), false); + ensure_approximately_equals("WOCM t5 - isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WOCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WOCM t5 - isExpired() does not modify count", count, U64L(8)); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(10.0, true); + + timer.start(0); + ensure_equals("WCM isExpired() returns false after starting with 10.0 horizon time", + timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false); + ensure_approximately_equals("WCM t5 - isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WCM t5 - isExpired() does not modify count", count, U64L(8)); + ensure_equals("WCM t5 - isExpired() does not modify user_cpu", user_cpu, U64L(29000)); + ensure_equals("WCM t5 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); + } +} + + +// start() test - start in the past but not beyond 1 horizon +template<> template<> +void deadmantimer_object_t::test<6>() +{ + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(10.0, false); + + // Would like to do subtraction on current time but can't because + // the implementation on Windows is zero-based. We wrap around + // the backside resulting in a large U64 number. + + LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); + LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0)); + timer.start(the_past); + ensure_equals("WOCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past", + timer.isExpired(now, started, stopped, count), false); + ensure_approximately_equals("WOCM t6 - isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WOCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WOCM t6 - isExpired() does not modify count", count, U64L(8)); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(10.0, true); + + // Would like to do subtraction on current time but can't because + // the implementation on Windows is zero-based. We wrap around + // the backside resulting in a large U64 number. + + LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); + LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0)); + timer.start(the_past); + ensure_equals("WCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + ensure_approximately_equals("WCM t6 - isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("t6 - isExpired() does not modify count", count, U64L(8)); + ensure_equals("WCM t6 - isExpired() does not modify user_cpu", user_cpu, U64L(29000)); + ensure_equals("WCM t6 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); + } +} + + +// start() test - start in the past but well beyond 1 horizon +template<> template<> +void deadmantimer_object_t::test<7>() +{ + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(10.0, false); + + // Would like to do subtraction on current time but can't because + // the implementation on Windows is zero-based. We wrap around + // the backside resulting in a large U64 number. + + LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); + LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); + timer.start(the_past); + ensure_equals("WOCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", + timer.isExpired(now,started, stopped, count), true); + ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(10.0, true); + + // Would like to do subtraction on current time but can't because + // the implementation on Windows is zero-based. We wrap around + // the backside resulting in a large U64 number. + + LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); + LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); + timer.start(the_past); + ensure_equals("WCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", + timer.isExpired(now,started, stopped, count, user_cpu, sys_cpu), true); + ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8); + } +} + + +// isExpired() test - results are read-once. Probes after first true are false. +template<> template<> +void deadmantimer_object_t::test<8>() +{ + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(10.0, false); + + // Would like to do subtraction on current time but can't because + // the implementation on Windows is zero-based. We wrap around + // the backside resulting in a large U64 number. + + LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); + LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); + timer.start(the_past); + ensure_equals("WOCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", + timer.isExpired(now, started, stopped, count), true); + + started = 42.0; + stopped = 97.0; + count = U64L(8); + ensure_equals("WOCM t8 - second isExpired() returns false after true", + timer.isExpired(now, started, stopped, count), false); + ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WOCM t8 - 2nd isExpired() does not modify count", count, U64L(8)); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(10.0, true); + + // Would like to do subtraction on current time but can't because + // the implementation on Windows is zero-based. We wrap around + // the backside resulting in a large U64 number. + + LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); + LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); + timer.start(the_past); + ensure_equals("WCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); + + started = 42.0; + stopped = 97.0; + count = U64L(8); + user_cpu = 29000; + sys_cpu = 57000; + ensure_equals("WCM t8 - second isExpired() returns false after true", + timer.isExpired(now, started, stopped, count), false); + ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WCM t8 - 2nd isExpired() does not modify count", count, U64L(8)); + ensure_equals("WCM t8 - 2nd isExpired() does not modify user_cpu", user_cpu, U64L(29000)); + ensure_equals("WCM t8 - 2nd isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); + } +} + + +// ringBell() test - see that we can keep a timer from expiring +template<> template<> +void deadmantimer_object_t::test<9>() +{ + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(5.0, false); + + LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow()); + F64 real_start(u64_time_to_float(now)); + timer.start(0); + + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + ensure_equals("WOCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings", + timer.isExpired(now, started, stopped, count), false); + F64 last_good_ring(u64_time_to_float(now)); + + // Jump forward and expire + now += float_time_to_u64(10.0); + ensure_equals("WOCM t9 - 5.0 horizon timer expires on 10-second jump", + timer.isExpired(now, started, stopped, count), true); + ensure_approximately_equals("WOCM t9 - started matches start() time", started, real_start, 4); + ensure_approximately_equals("WOCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4); + ensure_equals("WOCM t9 - 10 good ringBell()s", count, U64L(10)); + ensure_equals("WOCM t9 - single read only", timer.isExpired(now, started, stopped, count), false); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(5.0, true); + + LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow()); + F64 real_start(u64_time_to_float(now)); + timer.start(0); + + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + ensure_equals("WCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + F64 last_good_ring(u64_time_to_float(now)); + + // Jump forward and expire + now += float_time_to_u64(10.0); + ensure_equals("WCM t9 - 5.0 horizon timer expires on 10-second jump", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); + ensure_approximately_equals("WCM t9 - started matches start() time", started, real_start, 4); + ensure_approximately_equals("WCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4); + ensure_equals("WCM t9 - 10 good ringBell()s", count, U64L(10)); + ensure_equals("WCM t9 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + } +} + + +// restart after expiration test - verify that restarts behave well +template<> template<> +void deadmantimer_object_t::test<10>() +{ + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(5.0, false); + + LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow()); + F64 real_start(u64_time_to_float(now)); + timer.start(0); + + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings", + timer.isExpired(now, started, stopped, count), false); + F64 last_good_ring(u64_time_to_float(now)); + + // Jump forward and expire + now += float_time_to_u64(10.0); + ensure_equals("WOCM t10 - 5.0 horizon timer expires on 10-second jump", + timer.isExpired(now, started, stopped, count), true); + ensure_approximately_equals("WOCM t10 - started matches start() time", started, real_start, 4); + ensure_approximately_equals("WOCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4); + ensure_equals("WOCM t10 - 10 good ringBell()s", count, U64L(10)); + ensure_equals("WOCM t10 - single read only", timer.isExpired(now, started, stopped, count), false); + + // Jump forward and restart + now += float_time_to_u64(1.0); + real_start = u64_time_to_float(now); + timer.start(now); + + // Run a modified bell ring sequence + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings", + timer.isExpired(now, started, stopped, count), false); + last_good_ring = u64_time_to_float(now); + + // Jump forward and expire + now += float_time_to_u64(10.0); + ensure_equals("WOCM t10 - 5.0 horizon timer expires on 8-second jump", + timer.isExpired(now, started, stopped, count), true); + ensure_approximately_equals("WOCM t10 - 2nd started matches start() time", started, real_start, 4); + ensure_approximately_equals("WOCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4); + ensure_equals("WOCM t10 - 8 good ringBell()s", count, U64L(8)); + ensure_equals("WOCM t10 - single read only - 2nd start", + timer.isExpired(now, started, stopped, count), false); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + + LLDeadmanTimer timer(5.0, true); + + LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow()); + F64 real_start(u64_time_to_float(now)); + timer.start(0); + + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + F64 last_good_ring(u64_time_to_float(now)); + + // Jump forward and expire + now += float_time_to_u64(10.0); + ensure_equals("WCM t10 - 5.0 horizon timer expires on 10-second jump", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); + ensure_approximately_equals("WCM t10 - started matches start() time", started, real_start, 4); + ensure_approximately_equals("WCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4); + ensure_equals("WCM t10 - 10 good ringBell()s", count, U64L(10)); + ensure_equals("WCM t10 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + + // Jump forward and restart + now += float_time_to_u64(1.0); + real_start = u64_time_to_float(now); + timer.start(now); + + // Run a modified bell ring sequence + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + last_good_ring = u64_time_to_float(now); + + // Jump forward and expire + now += float_time_to_u64(10.0); + ensure_equals("WCM t10 - 5.0 horizon timer expires on 8-second jump", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); + ensure_approximately_equals("WCM t10 - 2nd started matches start() time", started, real_start, 4); + ensure_approximately_equals("WCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4); + ensure_equals("WCM t10 - 8 good ringBell()s", count, U64L(8)); + ensure_equals("WCM t10 - single read only - 2nd start", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + } +} + + + +} // end namespace tut diff --git a/indra/llcommon/tests/llprocinfo_test.cpp b/indra/llcommon/tests/llprocinfo_test.cpp new file mode 100644 index 0000000000..12d5a695ee --- /dev/null +++ b/indra/llcommon/tests/llprocinfo_test.cpp @@ -0,0 +1,91 @@ +/** + * @file llprocinfo_test.cpp + * @brief Tests for the LLProcInfo class. + * + * $LicenseInfo:firstyear=2013&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 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 "../llprocinfo.h" + +#include "../test/lltut.h" +#include "../lltimer.h" + + +static const LLProcInfo::time_type bad_user(289375U), bad_system(275U); + + +namespace tut +{ + +struct procinfo_test +{ + procinfo_test() + { + } +}; + +typedef test_group<procinfo_test> procinfo_group_t; +typedef procinfo_group_t::object procinfo_object_t; +tut::procinfo_group_t procinfo_instance("LLProcInfo"); + + +// Basic invocation works +template<> template<> +void procinfo_object_t::test<1>() +{ + LLProcInfo::time_type user(bad_user), system(bad_system); + + set_test_name("getCPUUsage() basic function"); + + LLProcInfo::getCPUUsage(user, system); + + ensure_not_equals("getCPUUsage() writes to its user argument", user, bad_user); + ensure_not_equals("getCPUUsage() writes to its system argument", system, bad_system); +} + + +// Time increases +template<> template<> +void procinfo_object_t::test<2>() +{ + LLProcInfo::time_type user(bad_user), system(bad_system); + LLProcInfo::time_type user2(bad_user), system2(bad_system); + + set_test_name("getCPUUsage() increases over time"); + + LLProcInfo::getCPUUsage(user, system); + + for (int i(0); i < 100000; ++i) + { + ms_sleep(0); + } + + LLProcInfo::getCPUUsage(user2, system2); + + ensure_equals("getCPUUsage() user value doesn't decrease over time", user2 >= user, true); + ensure_equals("getCPUUsage() system value doesn't decrease over time", system2 >= system, true); +} + + +} // end namespace tut |