PageRenderTime 23ms CodeModel.GetById 2ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llcommon/llkeythrottle.h

https://bitbucket.org/lindenlab/viewer-beta/
C++ Header | 331 lines | 215 code | 51 blank | 65 comment | 24 complexity | da4dcf6f2d965dee210af1fec90e9774 MD5 | raw file
  1/** 
  2 * @file llkeythrottle.h
  3 *
  4 * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  5 * Second Life Viewer Source Code
  6 * Copyright (C) 2010, Linden Research, Inc.
  7 * 
  8 * This library is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU Lesser General Public
 10 * License as published by the Free Software Foundation;
 11 * version 2.1 of the License only.
 12 * 
 13 * This library is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 16 * Lesser General Public License for more details.
 17 * 
 18 * You should have received a copy of the GNU Lesser General Public
 19 * License along with this library; if not, write to the Free Software
 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 21 * 
 22 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 23 * $/LicenseInfo$
 24 */
 25
 26#ifndef LL_LLKEY_THROTTLE_H
 27#define LL_LLKEY_THROTTLE_H
 28
 29// LLKeyThrottle keeps track of the number of action occurences with a key value
 30// for a type over a given time period.  If the rate set in the constructor is
 31// exceeed, the key is considered blocked.  The transition from unblocked to
 32// blocked is noted so the responsible agent can be informed.  This transition
 33// takes twice the look back window to clear.
 34
 35#include "linden_common.h"
 36
 37#include "llframetimer.h"
 38#include <map>
 39
 40
 41// forward declaration so LLKeyThrottleImpl can befriend it
 42template <class T> class LLKeyThrottle;
 43
 44
 45// Implementation utility class - use LLKeyThrottle, not this
 46template <class T>
 47class LLKeyThrottleImpl
 48{
 49	friend class LLKeyThrottle<T>;
 50protected:
 51	struct Entry {
 52		U32		count;
 53		bool	blocked;
 54
 55		Entry() : count(0), blocked(false) { }
 56	};
 57
 58	typedef std::map<T, Entry> EntryMap;
 59
 60	EntryMap* prevMap;
 61	EntryMap* currMap;
 62	
 63	U32 countLimit;
 64		// maximum number of keys allowed per interval
 65		
 66	U64 intervalLength;		// each map covers this time period (usec or frame number)
 67	U64 startTime;			// start of the time period (usec or frame number)
 68		// currMap started counting at this time
 69		// prevMap covers the previous interval
 70	
 71	LLKeyThrottleImpl() :
 72		prevMap(NULL),
 73		currMap(NULL),
 74		countLimit(0),
 75		intervalLength(1),
 76		startTime(0)
 77	{}
 78
 79	static U64 getTime()
 80	{
 81		return LLFrameTimer::getTotalTime();
 82	}
 83	static U64 getFrame()		// Return the current frame number
 84	{
 85		return (U64) LLFrameTimer::getFrameCount();
 86	}
 87};
 88
 89
 90template< class T >
 91class LLKeyThrottle
 92{
 93public:
 94	// @param realtime = FALSE for frame-based throttle, TRUE for usec
 95	// real-time throttle
 96	LLKeyThrottle(U32 limit, F32 interval, BOOL realtime = TRUE)	
 97		: m(* new LLKeyThrottleImpl<T>)
 98	{
 99		setParameters( limit, interval, realtime );
100	}
101		
102	~LLKeyThrottle()
103	{
104		delete m.prevMap;
105		delete m.currMap;
106		delete &m;
107	}
108
109	enum State {
110		THROTTLE_OK,			// rate not exceeded, let pass
111		THROTTLE_NEWLY_BLOCKED,	// rate exceed for the first time
112		THROTTLE_BLOCKED,		// rate exceed, block key
113	};
114
115	F64 getActionCount(const T& id)
116	{
117		U64 now = 0;
118		if ( mIsRealtime )
119		{
120			now = LLKeyThrottleImpl<T>::getTime();
121		}
122		else
123		{
124			now = LLKeyThrottleImpl<T>::getFrame();
125		}
126
127		if (now >= (m.startTime + m.intervalLength))
128		{
129			if (now < (m.startTime + 2 * m.intervalLength))
130			{
131				// prune old data
132				delete m.prevMap;
133				m.prevMap = m.currMap;
134				m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
135
136				m.startTime += m.intervalLength;
137			}
138			else
139			{
140				// lots of time has passed, all data is stale
141				delete m.prevMap;
142				delete m.currMap;
143				m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap;
144				m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
145
146				m.startTime = now;
147			}
148		}
149
150		U32 prevCount = 0;
151
152		typename LLKeyThrottleImpl<T>::EntryMap::const_iterator prev = m.prevMap->find(id);
153		if (prev != m.prevMap->end())
154		{
155			prevCount = prev->second.count;
156		}
157
158		typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
159
160		// curr.count is the number of keys in
161		// this current 'time slice' from the beginning of it until now
162		// prevCount is the number of keys in the previous
163		// time slice scaled to be one full time slice back from the current 
164		// (now) time.
165
166		// compute current, windowed rate
167		F64 timeInCurrent = ((F64)(now - m.startTime) / m.intervalLength);
168		F64 averageCount = curr.count + prevCount * (1.0 - timeInCurrent);
169		return averageCount;
170	}
171
172	// call each time the key wants use
173	State noteAction(const T& id, S32 weight = 1)
174	{
175		U64 now = 0;
176		if ( mIsRealtime )
177		{
178			now = LLKeyThrottleImpl<T>::getTime();
179		}
180		else
181		{
182			now = LLKeyThrottleImpl<T>::getFrame();
183		}
184
185		if (now >= (m.startTime + m.intervalLength))
186		{
187			if (now < (m.startTime + 2 * m.intervalLength))
188			{
189				// prune old data
190				delete m.prevMap;
191				m.prevMap = m.currMap;
192				m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
193
194				m.startTime += m.intervalLength;
195			}
196			else
197			{
198				// lots of time has passed, all data is stale
199				delete m.prevMap;
200				delete m.currMap;
201				m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap;
202				m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
203
204				m.startTime = now;
205			}
206		}
207
208		U32 prevCount = 0;
209		bool prevBlocked = false;
210
211		typename LLKeyThrottleImpl<T>::EntryMap::const_iterator prev = m.prevMap->find(id);
212		if (prev != m.prevMap->end())
213		{
214			prevCount = prev->second.count;
215			prevBlocked = prev->second.blocked;
216		}
217
218		typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
219
220		bool wereBlocked = curr.blocked || prevBlocked;
221
222		curr.count += weight;
223
224		// curr.count is the number of keys in
225		// this current 'time slice' from the beginning of it until now
226		// prevCount is the number of keys in the previous
227		// time slice scaled to be one full time slice back from the current 
228		// (now) time.
229
230		// compute current, windowed rate
231		F64 timeInCurrent = ((F64)(now - m.startTime) / m.intervalLength);
232		F64 averageCount = curr.count + prevCount * (1.0 - timeInCurrent);
233		
234		curr.blocked |= averageCount > m.countLimit;
235
236		bool nowBlocked = curr.blocked || prevBlocked;
237
238		if (!nowBlocked)
239		{
240			return THROTTLE_OK;
241		}
242		else if (!wereBlocked)
243		{
244			return THROTTLE_NEWLY_BLOCKED;
245		}
246		else
247		{
248			return THROTTLE_BLOCKED;
249		}
250	}
251
252	// call to force throttle conditions for id
253	void throttleAction(const T& id)
254	{
255		noteAction(id);
256		typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
257		curr.count = llmax(m.countLimit, curr.count);
258		curr.blocked = true;
259	}
260
261	// returns true if key is blocked
262	bool isThrottled(const T& id) const
263	{
264		if (m.currMap->empty()
265			&& m.prevMap->empty())
266		{
267			// most of the time we'll fall in here
268			return false;
269		}
270
271		// NOTE, we ignore the case where id is in the map but the map is stale.  
272		// You might think that we'd stop throttling things in such a case, 
273		// however it may be that a god has disabled scripts in the region or 
274		// estate --> we probably want to report the state of the id when the 
275		// scripting engine was paused.
276		typename LLKeyThrottleImpl<T>::EntryMap::const_iterator entry = m.currMap->find(id);
277		if (entry != m.currMap->end())
278		{
279			return entry->second.blocked;
280		}
281		entry = m.prevMap->find(id);
282		if (entry != m.prevMap->end())
283		{
284			return entry->second.blocked;
285		}
286		return false;
287	}
288
289	// Get the throttling parameters
290	void getParameters( U32 & out_limit, F32 & out_interval, BOOL & out_realtime )
291	{
292		out_limit = m.countLimit;
293		out_interval = m.intervalLength;
294		out_realtime = mIsRealtime;
295	}
296
297	// Set the throttling behavior
298	void setParameters( U32 limit, F32 interval, BOOL realtime = TRUE )
299	{
300		// limit is the maximum number of keys
301		// allowed per interval (in seconds or frames)
302		mIsRealtime = realtime;
303		m.countLimit = limit;
304		if ( mIsRealtime )
305		{
306			m.intervalLength = (U64)(interval * USEC_PER_SEC);
307			m.startTime = LLKeyThrottleImpl<T>::getTime();
308		}
309		else
310		{
311			m.intervalLength = (U64)interval;
312			m.startTime = LLKeyThrottleImpl<T>::getFrame();
313		}
314
315		if ( m.intervalLength == 0 )
316		{	// Don't allow zero intervals
317			m.intervalLength = 1;
318		}
319
320		delete m.prevMap;
321		m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap;
322		delete m.currMap;
323		m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
324	}
325
326protected:
327	LLKeyThrottleImpl<T>& m;
328	BOOL	mIsRealtime;	// TRUE to be time based (default), FALSE for frame based
329};
330
331#endif