diff options
| author | obscurestar <none@none> | 2014-02-24 15:12:04 -0800 | 
|---|---|---|
| committer | obscurestar <none@none> | 2014-02-24 15:12:04 -0800 | 
| commit | 8fd270af1cb7ee2cad7c47909b308ef31caf4cd3 (patch) | |
| tree | 0908629d1ae5173098bb204ab2447c6d351d894e /indra/llcommon | |
| parent | 57d7cbb8375ca59bcb3d3643e4013b88e8883822 (diff) | |
| parent | de8fea13627cc5978b8a6135802a52864a11c39a (diff) | |
Pulled from release.
Diffstat (limited to 'indra/llcommon')
| -rwxr-xr-x | indra/llcommon/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | indra/llcommon/lldeadmantimer.cpp | 188 | ||||
| -rw-r--r-- | indra/llcommon/lldeadmantimer.h | 214 | ||||
| -rw-r--r-- | indra/llcommon/llprocinfo.cpp | 94 | ||||
| -rw-r--r-- | indra/llcommon/llprocinfo.h | 68 | ||||
| -rwxr-xr-x | indra/llcommon/llthread.cpp | 32 | ||||
| -rwxr-xr-x | indra/llcommon/llthread.h | 46 | ||||
| -rwxr-xr-x | indra/llcommon/lltimer.h | 7 | ||||
| -rw-r--r-- | indra/llcommon/tests/lldeadmantimer_test.cpp | 628 | ||||
| -rw-r--r-- | indra/llcommon/tests/llprocinfo_test.cpp | 91 | 
10 files changed, 1371 insertions, 3 deletions
| diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 8eb0c6249d..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 @@ -146,6 +148,7 @@ set(llcommon_HEADER_FILES      lldarray.h      lldarrayptr.h      lldate.h +    lldeadmantimer.h      lldefs.h      lldependencies.h      lldeleteutils.h @@ -207,6 +210,7 @@ set(llcommon_HEADER_FILES      llpriqueuemap.h      llprocess.h      llprocessor.h +    llprocinfo.h      llptrskiplist.h      llptrskipmap.h      llptrto.h @@ -324,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/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/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/llthread.cpp b/indra/llcommon/llthread.cpp index 60adeeaeb7..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 @@ -373,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) diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index f51d985b5f..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(); 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/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 | 
