phoenix-firestorm/indra/llcommon/lldeadmantimer.h

215 lines
8.6 KiB
C++

/**
* @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