PageRenderTime 36ms CodeModel.GetById 15ms RepoModel.GetById 0ms 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
Possible License(s): LGPL-2.1
  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. #ifndef LL_LLKEY_THROTTLE_H
  26. #define LL_LLKEY_THROTTLE_H
  27. // LLKeyThrottle keeps track of the number of action occurences with a key value
  28. // for a type over a given time period. If the rate set in the constructor is
  29. // exceeed, the key is considered blocked. The transition from unblocked to
  30. // blocked is noted so the responsible agent can be informed. This transition
  31. // takes twice the look back window to clear.
  32. #include "linden_common.h"
  33. #include "llframetimer.h"
  34. #include <map>
  35. // forward declaration so LLKeyThrottleImpl can befriend it
  36. template <class T> class LLKeyThrottle;
  37. // Implementation utility class - use LLKeyThrottle, not this
  38. template <class T>
  39. class LLKeyThrottleImpl
  40. {
  41. friend class LLKeyThrottle<T>;
  42. protected:
  43. struct Entry {
  44. U32 count;
  45. bool blocked;
  46. Entry() : count(0), blocked(false) { }
  47. };
  48. typedef std::map<T, Entry> EntryMap;
  49. EntryMap* prevMap;
  50. EntryMap* currMap;
  51. U32 countLimit;
  52. // maximum number of keys allowed per interval
  53. U64 intervalLength; // each map covers this time period (usec or frame number)
  54. U64 startTime; // start of the time period (usec or frame number)
  55. // currMap started counting at this time
  56. // prevMap covers the previous interval
  57. LLKeyThrottleImpl() :
  58. prevMap(NULL),
  59. currMap(NULL),
  60. countLimit(0),
  61. intervalLength(1),
  62. startTime(0)
  63. {}
  64. static U64 getTime()
  65. {
  66. return LLFrameTimer::getTotalTime();
  67. }
  68. static U64 getFrame() // Return the current frame number
  69. {
  70. return (U64) LLFrameTimer::getFrameCount();
  71. }
  72. };
  73. template< class T >
  74. class LLKeyThrottle
  75. {
  76. public:
  77. // @param realtime = FALSE for frame-based throttle, TRUE for usec
  78. // real-time throttle
  79. LLKeyThrottle(U32 limit, F32 interval, BOOL realtime = TRUE)
  80. : m(* new LLKeyThrottleImpl<T>)
  81. {
  82. setParameters( limit, interval, realtime );
  83. }
  84. ~LLKeyThrottle()
  85. {
  86. delete m.prevMap;
  87. delete m.currMap;
  88. delete &m;
  89. }
  90. enum State {
  91. THROTTLE_OK, // rate not exceeded, let pass
  92. THROTTLE_NEWLY_BLOCKED, // rate exceed for the first time
  93. THROTTLE_BLOCKED, // rate exceed, block key
  94. };
  95. F64 getActionCount(const T& id)
  96. {
  97. U64 now = 0;
  98. if ( mIsRealtime )
  99. {
  100. now = LLKeyThrottleImpl<T>::getTime();
  101. }
  102. else
  103. {
  104. now = LLKeyThrottleImpl<T>::getFrame();
  105. }
  106. if (now >= (m.startTime + m.intervalLength))
  107. {
  108. if (now < (m.startTime + 2 * m.intervalLength))
  109. {
  110. // prune old data
  111. delete m.prevMap;
  112. m.prevMap = m.currMap;
  113. m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
  114. m.startTime += m.intervalLength;
  115. }
  116. else
  117. {
  118. // lots of time has passed, all data is stale
  119. delete m.prevMap;
  120. delete m.currMap;
  121. m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap;
  122. m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
  123. m.startTime = now;
  124. }
  125. }
  126. U32 prevCount = 0;
  127. typename LLKeyThrottleImpl<T>::EntryMap::const_iterator prev = m.prevMap->find(id);
  128. if (prev != m.prevMap->end())
  129. {
  130. prevCount = prev->second.count;
  131. }
  132. typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
  133. // curr.count is the number of keys in
  134. // this current 'time slice' from the beginning of it until now
  135. // prevCount is the number of keys in the previous
  136. // time slice scaled to be one full time slice back from the current
  137. // (now) time.
  138. // compute current, windowed rate
  139. F64 timeInCurrent = ((F64)(now - m.startTime) / m.intervalLength);
  140. F64 averageCount = curr.count + prevCount * (1.0 - timeInCurrent);
  141. return averageCount;
  142. }
  143. // call each time the key wants use
  144. State noteAction(const T& id, S32 weight = 1)
  145. {
  146. U64 now = 0;
  147. if ( mIsRealtime )
  148. {
  149. now = LLKeyThrottleImpl<T>::getTime();
  150. }
  151. else
  152. {
  153. now = LLKeyThrottleImpl<T>::getFrame();
  154. }
  155. if (now >= (m.startTime + m.intervalLength))
  156. {
  157. if (now < (m.startTime + 2 * m.intervalLength))
  158. {
  159. // prune old data
  160. delete m.prevMap;
  161. m.prevMap = m.currMap;
  162. m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
  163. m.startTime += m.intervalLength;
  164. }
  165. else
  166. {
  167. // lots of time has passed, all data is stale
  168. delete m.prevMap;
  169. delete m.currMap;
  170. m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap;
  171. m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
  172. m.startTime = now;
  173. }
  174. }
  175. U32 prevCount = 0;
  176. bool prevBlocked = false;
  177. typename LLKeyThrottleImpl<T>::EntryMap::const_iterator prev = m.prevMap->find(id);
  178. if (prev != m.prevMap->end())
  179. {
  180. prevCount = prev->second.count;
  181. prevBlocked = prev->second.blocked;
  182. }
  183. typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
  184. bool wereBlocked = curr.blocked || prevBlocked;
  185. curr.count += weight;
  186. // curr.count is the number of keys in
  187. // this current 'time slice' from the beginning of it until now
  188. // prevCount is the number of keys in the previous
  189. // time slice scaled to be one full time slice back from the current
  190. // (now) time.
  191. // compute current, windowed rate
  192. F64 timeInCurrent = ((F64)(now - m.startTime) / m.intervalLength);
  193. F64 averageCount = curr.count + prevCount * (1.0 - timeInCurrent);
  194. curr.blocked |= averageCount > m.countLimit;
  195. bool nowBlocked = curr.blocked || prevBlocked;
  196. if (!nowBlocked)
  197. {
  198. return THROTTLE_OK;
  199. }
  200. else if (!wereBlocked)
  201. {
  202. return THROTTLE_NEWLY_BLOCKED;
  203. }
  204. else
  205. {
  206. return THROTTLE_BLOCKED;
  207. }
  208. }
  209. // call to force throttle conditions for id
  210. void throttleAction(const T& id)
  211. {
  212. noteAction(id);
  213. typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
  214. curr.count = llmax(m.countLimit, curr.count);
  215. curr.blocked = true;
  216. }
  217. // returns true if key is blocked
  218. bool isThrottled(const T& id) const
  219. {
  220. if (m.currMap->empty()
  221. && m.prevMap->empty())
  222. {
  223. // most of the time we'll fall in here
  224. return false;
  225. }
  226. // NOTE, we ignore the case where id is in the map but the map is stale.
  227. // You might think that we'd stop throttling things in such a case,
  228. // however it may be that a god has disabled scripts in the region or
  229. // estate --> we probably want to report the state of the id when the
  230. // scripting engine was paused.
  231. typename LLKeyThrottleImpl<T>::EntryMap::const_iterator entry = m.currMap->find(id);
  232. if (entry != m.currMap->end())
  233. {
  234. return entry->second.blocked;
  235. }
  236. entry = m.prevMap->find(id);
  237. if (entry != m.prevMap->end())
  238. {
  239. return entry->second.blocked;
  240. }
  241. return false;
  242. }
  243. // Get the throttling parameters
  244. void getParameters( U32 & out_limit, F32 & out_interval, BOOL & out_realtime )
  245. {
  246. out_limit = m.countLimit;
  247. out_interval = m.intervalLength;
  248. out_realtime = mIsRealtime;
  249. }
  250. // Set the throttling behavior
  251. void setParameters( U32 limit, F32 interval, BOOL realtime = TRUE )
  252. {
  253. // limit is the maximum number of keys
  254. // allowed per interval (in seconds or frames)
  255. mIsRealtime = realtime;
  256. m.countLimit = limit;
  257. if ( mIsRealtime )
  258. {
  259. m.intervalLength = (U64)(interval * USEC_PER_SEC);
  260. m.startTime = LLKeyThrottleImpl<T>::getTime();
  261. }
  262. else
  263. {
  264. m.intervalLength = (U64)interval;
  265. m.startTime = LLKeyThrottleImpl<T>::getFrame();
  266. }
  267. if ( m.intervalLength == 0 )
  268. { // Don't allow zero intervals
  269. m.intervalLength = 1;
  270. }
  271. delete m.prevMap;
  272. m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap;
  273. delete m.currMap;
  274. m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
  275. }
  276. protected:
  277. LLKeyThrottleImpl<T>& m;
  278. BOOL mIsRealtime; // TRUE to be time based (default), FALSE for frame based
  279. };
  280. #endif