summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMonty Brandenberg <monty@lindenlab.com>2013-04-09 23:44:59 +0000
committerMonty Brandenberg <monty@lindenlab.com>2013-04-09 23:44:59 +0000
commit188221a90c7a054d390ddaa534d391f6370ac6bc (patch)
tree8a77f8077221f18a46e29296d6dbd1e9a78252cb
parent8a330ee2eb1008951f5e5159057fa5736dd805b4 (diff)
SH-4088 Deadman timer switch started in llcommon. Unit test started.
Will be used for mesh, inventory, etc., operation markers.
-rw-r--r--indra/llcommon/CMakeLists.txt3
-rw-r--r--indra/llcommon/lldeadmantimer.cpp141
-rw-r--r--indra/llcommon/lldeadmantimer.h171
-rw-r--r--indra/llcommon/lltimer.h7
-rw-r--r--indra/llcommon/tests/lldeadmantimer_test.cpp63
5 files changed, 385 insertions, 0 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 5cce8ff2c4..7ed4137065 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -42,6 +42,7 @@ set(llcommon_SOURCE_FILES
llcriticaldamp.cpp
llcursortypes.cpp
lldate.cpp
+ lldeadmantimer.cpp
lldependencies.cpp
lldictionary.cpp
llerror.cpp
@@ -144,6 +145,7 @@ set(llcommon_HEADER_FILES
lldarray.h
lldarrayptr.h
lldate.h
+ lldeadmantimer.h
lldefs.h
lldependencies.h
lldeleteutils.h
@@ -322,6 +324,7 @@ 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}")
diff --git a/indra/llcommon/lldeadmantimer.cpp b/indra/llcommon/lldeadmantimer.cpp
new file mode 100644
index 0000000000..2f48d13c2d
--- /dev/null
+++ b/indra/llcommon/lldeadmantimer.cpp
@@ -0,0 +1,141 @@
+/**
+* @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"
+
+
+LLDeadmanTimer::LLDeadmanTimer(F64 horizon)
+ : mHorizon(U64(horizon * 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))
+{}
+
+
+void LLDeadmanTimer::start(U64 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);
+}
+
+
+void LLDeadmanTimer::stop(U64 now)
+{
+ if (! mActive)
+ {
+ return;
+ }
+
+ if (! now)
+ {
+ now = LLTimer::getCurrentClockCount();
+ }
+ mStopped = now;
+ mActive = false;
+ mDone = true;
+}
+
+
+bool LLDeadmanTimer::isExpired(F64 & started, F64 & stopped, U64 & count, U64 now)
+{
+ if (! mActive)
+ {
+ return false;
+ }
+
+ if (! mDone)
+ {
+ if (! now)
+ {
+ now = LLTimer::getCurrentClockCount();
+ }
+
+ 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(U64 now)
+{
+ if (! mActive)
+ {
+ return;
+ }
+
+ if (! now)
+ {
+ now = LLTimer::getCurrentClockCount();
+ }
+
+ if (now > mExpires)
+ {
+ mActive = false;
+ mDone = true;
+ }
+ else
+ {
+ mStopped = now;
+ mExpires = now + mHorizon;
+ ++mCount;
+ }
+
+ return;
+}
+
diff --git a/indra/llcommon/lldeadmantimer.h b/indra/llcommon/lldeadmantimer.h
new file mode 100644
index 0000000000..84023723ab
--- /dev/null
+++ b/indra/llcommon/lldeadmantimer.h
@@ -0,0 +1,171 @@
+/**
+* @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"
+
+
+/// @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:
+ /// 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.
+ ///
+ LLDeadmanTimer(F64 horizon);
+
+ ~LLDeadmanTimer()
+ {}
+
+private:
+ LLDeadmanTimer(const LLDeadmanTimer &); // Not defined
+ void operator=(const LLDeadmanTimer &); // Not defined
+
+public:
+ /// 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(U64 now = U64L(0));
+
+
+ /// 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(U64 now = U64L(0));
+
+
+ /// 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.
+ ///
+ void ringBell(U64 now = U64L(0));
+
+
+ /// 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 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 now Current time as returned by @see
+ /// LLTimer::getCurrentClockCount(). If zero,
+ /// method will lookup current time.
+ ///
+ /// @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(F64 & started, F64 & stopped, U64 & count, U64 now = U64L(0));
+
+protected:
+ U64 mHorizon;
+ bool mActive;
+ bool mDone;
+ U64 mStarted;
+ U64 mExpires;
+ U64 mStopped;
+ U64 mCount;
+};
+
+
+#endif // LL_DEADMANTIMER_H
diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h
index 513de0605d..e73741217c 100644
--- 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..52a27b9c0a
--- /dev/null
+++ b/indra/llcommon/tests/lldeadmantimer_test.cpp
@@ -0,0 +1,63 @@
+/**
+ * @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 "../test/lltut.h"
+
+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");
+
+template<> template<>
+void deadmantimer_object_t::test<1>()
+{
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(1.0);
+
+ ensure_equals("isExpired() returns false after ctor()", timer.isExpired(started, stopped, count), false);
+ ensure_approximately_equals("isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("isExpired() does not modified count", count, U64L(8));
+}
+
+} // end namespace tut