Files
SingularityViewer/indra/llcommon/llkeythrottle.h
Drake Arconis dc5bfc1c41 Bit of cleanup
More cleanup and updates to llcommon
Removed legacy code and files
2012-08-02 23:19:07 -04:00

338 lines
8.6 KiB
C++

/**
* @file llkeythrottle.h
*
* $LicenseInfo:firstyear=2005&license=viewergpl$
*
* Copyright (c) 2005-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#ifndef LL_LLKEY_THROTTLE_H
#define LL_LLKEY_THROTTLE_H
// LLKeyThrottle keeps track of the number of action occurences with a key value
// for a type over a given time period. If the rate set in the constructor is
// exceeed, the key is considered blocked. The transition from unblocked to
// blocked is noted so the responsible agent can be informed. This transition
// takes twice the look back window to clear.
#include "linden_common.h"
#include "llframetimer.h"
#include <map>
// forward declaration so LLKeyThrottleImpl can befriend it
template <class T> class LLKeyThrottle;
// Implementation utility class - use LLKeyThrottle, not this
template <class T>
class LLKeyThrottleImpl
{
friend class LLKeyThrottle<T>;
protected:
struct Entry {
U32 count;
bool blocked;
Entry() : count(0), blocked(false) { }
};
typedef std::map<T, Entry> EntryMap;
EntryMap* prevMap;
EntryMap* currMap;
U32 countLimit;
// maximum number of keys allowed per interval
U64 intervalLength; // each map covers this time period (usec or frame number)
U64 startTime; // start of the time period (usec or frame number)
// currMap started counting at this time
// prevMap covers the previous interval
LLKeyThrottleImpl() :
prevMap(NULL),
currMap(NULL),
countLimit(0),
intervalLength(1),
startTime(0)
{}
static U64 getTime()
{
return LLFrameTimer::getTotalTime();
}
static U64 getFrame() // Return the current frame number
{
return (U64) LLFrameTimer::getFrameCount();
}
};
template< class T >
class LLKeyThrottle
{
public:
// @param realtime = FALSE for frame-based throttle, TRUE for usec
// real-time throttle
LLKeyThrottle(U32 limit, F32 interval, BOOL realtime = TRUE)
: m(* new LLKeyThrottleImpl<T>)
{
setParameters( limit, interval, realtime );
}
~LLKeyThrottle()
{
delete m.prevMap;
delete m.currMap;
delete &m;
}
enum State {
THROTTLE_OK, // rate not exceeded, let pass
THROTTLE_NEWLY_BLOCKED, // rate exceed for the first time
THROTTLE_BLOCKED, // rate exceed, block key
};
F64 getActionCount(const T& id)
{
U64 now = 0;
if ( mIsRealtime )
{
now = LLKeyThrottleImpl<T>::getTime();
}
else
{
now = LLKeyThrottleImpl<T>::getFrame();
}
if (now >= (m.startTime + m.intervalLength))
{
if (now < (m.startTime + 2 * m.intervalLength))
{
// prune old data
delete m.prevMap;
m.prevMap = m.currMap;
m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
m.startTime += m.intervalLength;
}
else
{
// lots of time has passed, all data is stale
delete m.prevMap;
delete m.currMap;
m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap;
m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
m.startTime = now;
}
}
U32 prevCount = 0;
typename LLKeyThrottleImpl<T>::EntryMap::const_iterator prev = m.prevMap->find(id);
if (prev != m.prevMap->end())
{
prevCount = prev->second.count;
}
typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
// curr.count is the number of keys in
// this current 'time slice' from the beginning of it until now
// prevCount is the number of keys in the previous
// time slice scaled to be one full time slice back from the current
// (now) time.
// compute current, windowed rate
F64 timeInCurrent = ((F64)(now - m.startTime) / m.intervalLength);
F64 averageCount = curr.count + prevCount * (1.0 - timeInCurrent);
return averageCount;
}
// call each time the key wants use
State noteAction(const T& id, S32 weight = 1)
{
U64 now = 0;
if ( mIsRealtime )
{
now = LLKeyThrottleImpl<T>::getTime();
}
else
{
now = LLKeyThrottleImpl<T>::getFrame();
}
if (now >= (m.startTime + m.intervalLength))
{
if (now < (m.startTime + 2 * m.intervalLength))
{
// prune old data
delete m.prevMap;
m.prevMap = m.currMap;
m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
m.startTime += m.intervalLength;
}
else
{
// lots of time has passed, all data is stale
delete m.prevMap;
delete m.currMap;
m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap;
m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
m.startTime = now;
}
}
U32 prevCount = 0;
bool prevBlocked = false;
typename LLKeyThrottleImpl<T>::EntryMap::const_iterator prev = m.prevMap->find(id);
if (prev != m.prevMap->end())
{
prevCount = prev->second.count;
prevBlocked = prev->second.blocked;
}
typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
bool wereBlocked = curr.blocked || prevBlocked;
curr.count += weight;
// curr.count is the number of keys in
// this current 'time slice' from the beginning of it until now
// prevCount is the number of keys in the previous
// time slice scaled to be one full time slice back from the current
// (now) time.
// compute current, windowed rate
F64 timeInCurrent = ((F64)(now - m.startTime) / m.intervalLength);
F64 averageCount = curr.count + prevCount * (1.0 - timeInCurrent);
curr.blocked |= averageCount > m.countLimit;
bool nowBlocked = curr.blocked || prevBlocked;
if (!nowBlocked)
{
return THROTTLE_OK;
}
else if (!wereBlocked)
{
return THROTTLE_NEWLY_BLOCKED;
}
else
{
return THROTTLE_BLOCKED;
}
}
// call to force throttle conditions for id
void throttleAction(const T& id)
{
noteAction(id);
typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
curr.count = llmax(m.countLimit, curr.count);
curr.blocked = true;
}
// returns true if key is blocked
bool isThrottled(const T& id) const
{
if (m.currMap->empty()
&& m.prevMap->empty())
{
// most of the time we'll fall in here
return false;
}
// NOTE, we ignore the case where id is in the map but the map is stale.
// You might think that we'd stop throttling things in such a case,
// however it may be that a god has disabled scripts in the region or
// estate --> we probably want to report the state of the id when the
// scripting engine was paused.
typename LLKeyThrottleImpl<T>::EntryMap::const_iterator entry = m.currMap->find(id);
if (entry != m.currMap->end())
{
return entry->second.blocked;
}
entry = m.prevMap->find(id);
if (entry != m.prevMap->end())
{
return entry->second.blocked;
}
return false;
}
// Get the throttling parameters
void getParameters( U32 & out_limit, F32 & out_interval, BOOL & out_realtime )
{
out_limit = m.countLimit;
out_interval = m.intervalLength;
out_realtime = mIsRealtime;
}
// Set the throttling behavior
void setParameters( U32 limit, F32 interval, BOOL realtime = TRUE )
{
// limit is the maximum number of keys
// allowed per interval (in seconds or frames)
mIsRealtime = realtime;
m.countLimit = limit;
if ( mIsRealtime )
{
m.intervalLength = (U64)(interval * USEC_PER_SEC);
m.startTime = LLKeyThrottleImpl<T>::getTime();
}
else
{
m.intervalLength = (U64)interval;
m.startTime = LLKeyThrottleImpl<T>::getFrame();
}
if ( m.intervalLength == 0 )
{ // Don't allow zero intervals
m.intervalLength = 1;
}
delete m.prevMap;
m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap;
delete m.currMap;
m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
}
protected:
LLKeyThrottleImpl<T>& m;
BOOL mIsRealtime; // TRUE to be time based (default), FALSE for frame based
};
#endif