diff options
| -rwxr-xr-x | indra/llcommon/CMakeLists.txt | 6 | ||||
| -rw-r--r-- | indra/llcommon/lldeadmantimer.cpp | 188 | ||||
| -rw-r--r-- | indra/llcommon/lldeadmantimer.h | 216 | ||||
| -rw-r--r-- | indra/llcommon/llprocinfo.cpp | 94 | ||||
| -rw-r--r-- | indra/llcommon/llprocinfo.h | 68 | ||||
| -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 | ||||
| -rwxr-xr-x | indra/newview/llmeshrepository.cpp | 106 | ||||
| -rwxr-xr-x | indra/newview/llmeshrepository.h | 12 | 
10 files changed, 1411 insertions, 5 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 3a4a8facc2..ce62a1978d 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 @@ -145,6 +147,7 @@ set(llcommon_HEADER_FILES      lldarray.h      lldarrayptr.h      lldate.h +    lldeadmantimer.h      lldefs.h      lldependencies.h      lldeleteutils.h @@ -206,6 +209,7 @@ set(llcommon_HEADER_FILES      llpriqueuemap.h      llprocess.h      llprocessor.h +    llprocinfo.h      llptrskiplist.h      llptrskipmap.h      llptrto.h @@ -322,12 +326,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..0dde16b717 --- /dev/null +++ b/indra/llcommon/lldeadmantimer.h @@ -0,0 +1,216 @@ +/**  +* @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 on the status of the timer 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. +	/// 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 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 is 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/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 diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index 8d3539d297..6dc834e852 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -5,7 +5,7 @@   *   * $LicenseInfo:firstyear=2005&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 @@ -38,6 +38,7 @@  #include "llcallbacklist.h"  #include "llcurl.h"  #include "lldatapacker.h" +#include "lldeadmantimer.h"  #include "llfloatermodelpreview.h"  #include "llfloaterperms.h"  #include "lleconomy.h" @@ -52,6 +53,7 @@  #include "llviewercontrol.h"  #include "llviewerinventory.h"  #include "llviewermenufile.h" +#include "llviewermessage.h"  #include "llviewerobjectlist.h"  #include "llviewerregion.h"  #include "llviewertexturelist.h" @@ -95,8 +97,9 @@ U32 LLMeshRepository::sLODPending = 0;  U32 LLMeshRepository::sCacheBytesRead = 0;  U32 LLMeshRepository::sCacheBytesWritten = 0;  U32 LLMeshRepository::sPeakKbps = 0; -	 +LLDeadmanTimer LLMeshRepository::sQuiescentTimer(15.0, true);	// true -> gather cpu metrics +	  const U32 MAX_TEXTURE_UPLOAD_RETRIES = 5;  static S32 dump_num = 0; @@ -108,7 +111,7 @@ std::string make_dump_name(std::string prefix, S32 num)  void dump_llsd_to_file(const LLSD& content, std::string filename);  LLSD llsd_from_file(std::string filename); -std::string header_lod[] =  +const std::string header_lod[] =   {  	"lowest_lod",  	"low_lod", @@ -116,6 +119,12 @@ std::string header_lod[] =  	"high_lod"  }; +// Static data and functions to measure mesh load +// time metrics for a new region scene. +static bool metrics_inited(false); +static boost::signals2::connection metrics_teleport_connection; +static unsigned int metrics_teleport_start_count(0); +static void metrics_teleport_started();  //get the number of bytes resident in memory for given volume  U32 get_volume_memory_size(const LLVolume* volume) @@ -2307,12 +2316,26 @@ void LLMeshRepository::init()  	mThread = new LLMeshRepoThread();  	mThread->start(); + +	if (! metrics_inited) +	{ +		// Get teleport started signals to restart timings. +		metrics_teleport_connection = LLViewerMessage::getInstance()-> +			setTeleportStartedCallback(metrics_teleport_started); +		metrics_inited = true; +	}  }  void LLMeshRepository::shutdown()  {  	llinfos << "Shutting down mesh repository." << llendl; +	if (metrics_inited) +	{ +		metrics_teleport_connection.disconnect(); +		metrics_inited = false; +	} +  	for (U32 i = 0; i < mUploads.size(); ++i)  	{  		llinfos << "Discard the pending mesh uploads " << llendl; @@ -2358,6 +2381,9 @@ void LLMeshRepository::shutdown()  //called in the main thread.  S32 LLMeshRepository::update()  { +	// Conditionally log a mesh metrics event +	metricsUpdate(); +	  	if(mUploadWaitList.empty())  	{  		return 0 ; @@ -2377,6 +2403,9 @@ S32 LLMeshRepository::update()  S32 LLMeshRepository::loadMesh(LLVOVolume* vobj, const LLVolumeParams& mesh_params, S32 detail, S32 last_lod)  { +	// Manage time-to-load metrics for mesh download operations. +	metricsProgress(1); +  	if (detail < 0 || detail > 4)  	{  		return detail; @@ -2679,6 +2708,9 @@ void LLMeshRepository::notifyDecompositionReceived(LLModel::Decomposition* decom  void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVolume* volume)  { //called from main thread +	// Manage time-to-load metrics for mesh download operations. +	metricsProgress(0); +	  	S32 detail = LLVolumeLODGroup::getVolumeDetailFromScale(volume->getDetail());  	//get list of objects waiting to be notified this mesh is loaded @@ -2722,6 +2754,9 @@ void LLMeshRepository::notifyMeshLoaded(const LLVolumeParams& mesh_params, LLVol  void LLMeshRepository::notifyMeshUnavailable(const LLVolumeParams& mesh_params, S32 lod)  { //called from main thread +	// Manage time-to-load metrics for mesh download operations. +	metricsProgress(0); +	  	//get list of objects waiting to be notified this mesh is loaded  	mesh_load_map::iterator obj_iter = mLoadingMeshes[lod].find(mesh_params); @@ -3698,3 +3733,68 @@ bool LLMeshRepository::meshRezEnabled()  	}  	return false;  } + +// Threading:  main thread only +// static +void LLMeshRepository::metricsStart() +{ +	sQuiescentTimer.start(0); +} + +// Threading:  main thread only +// static +void LLMeshRepository::metricsStop() +{ +	sQuiescentTimer.stop(0); +} + +// Threading:  main thread only +// static +void LLMeshRepository::metricsProgress(unsigned int this_count) +{ +	static bool first_start(true); + +	if (first_start) +	{ +		++metrics_teleport_start_count; +		metricsStart(); +		first_start = false; +	} +	sQuiescentTimer.ringBell(0, this_count); +} + +// Threading:  main thread only +// static +void LLMeshRepository::metricsUpdate() +{ +	F64 started, stopped; +	U64 total_count(U64L(0)), user_cpu(U64L(0)), sys_cpu(U64L(0)); +	 +	if (sQuiescentTimer.isExpired(0, started, stopped, total_count, user_cpu, sys_cpu)) +	{ +		LLSD metrics; + +		metrics["reason"] = "Mesh Download Quiescent"; +		metrics["scope"] = "Login"; +		metrics["start"] = started; +		metrics["stop"] = stopped; +		metrics["downloads"] = LLSD::Integer(total_count); +		metrics["teleports"] = LLSD::Integer(metrics_teleport_start_count); +		metrics["user_cpu"] = double(user_cpu) / 1.0e6; +		metrics["sys_cpu"] = double(sys_cpu) / 1.0e6; +		llinfos << "EventMarker " << metrics << llendl; +	} +} + +// Will use a request to start a teleport as a signal to +// restart a timing sequence.  We don't get one of these +// for login so initial start is done above. +// +// Threading:  main thread only +// static +void metrics_teleport_started() +{ +	LLMeshRepository::metricsStart(); +	++metrics_teleport_start_count; +} + diff --git a/indra/newview/llmeshrepository.h b/indra/newview/llmeshrepository.h index 8602271f84..3cdc66e1f0 100755 --- a/indra/newview/llmeshrepository.h +++ b/indra/newview/llmeshrepository.h @@ -32,6 +32,7 @@  #include "lluuid.h"  #include "llviewertexture.h"  #include "llvolume.h" +#include "lldeadmantimer.h"  #define LLCONVEXDECOMPINTER_STATIC 1 @@ -455,14 +456,15 @@ public:  	static U32 sCacheBytesRead;  	static U32 sCacheBytesWritten;  	static U32 sPeakKbps; -	 +	static LLDeadmanTimer sQuiescentTimer;  // time-to-complete-mesh-downloads after significant events +  	static F32 getStreamingCost(LLSD& header, F32 radius, S32* bytes = NULL, S32* visible_bytes = NULL, S32 detail = -1, F32 *unscaled_value = NULL);  	LLMeshRepository();  	void init();  	void shutdown(); -	S32 update() ; +	S32 update();  	//mesh management functions  	S32 loadMesh(LLVOVolume* volume, const LLVolumeParams& mesh_params, S32 detail = 0, S32 last_lod = -1); @@ -495,6 +497,12 @@ public:  	S32 getMeshSize(const LLUUID& mesh_id, S32 lod); +	// Quiescent timer management, main thread only. +	static void metricsStart(); +	static void metricsStop(); +	static void metricsProgress(unsigned int count); +	static void metricsUpdate(); +	  	typedef std::map<LLVolumeParams, std::set<LLUUID> > mesh_load_map;  	mesh_load_map mLoadingMeshes[4];  | 
