llkeythrottle.h

Go to the documentation of this file.
00001 
00031 #ifndef LL_LLKEY_THROTTLE_H
00032 #define LL_LLKEY_THROTTLE_H
00033 
00034 // LLKeyThrottle keeps track of the number of action occurences with a key value
00035 // for a type over a given time period.  If the rate set in the constructor is
00036 // exceeed, the key is considered blocked.  The transition from unblocked to
00037 // blocked is noted so the responsible agent can be informed.  This transition
00038 // takes twice the look back window to clear.
00039 
00040 #include "linden_common.h"
00041 
00042 #include "llframetimer.h"
00043 #include <map>
00044 
00045 
00046 // forward declaration so LLKeyThrottleImpl can befriend it
00047 template <class T> class LLKeyThrottle;
00048 
00049 
00050 // Implementation utility class - use LLKeyThrottle, not this
00051 template <class T>
00052 class LLKeyThrottleImpl
00053 {
00054         friend class LLKeyThrottle<T>;
00055 protected:
00056         struct Entry {
00057                 U32             count;
00058                 BOOL    blocked;
00059 
00060                 Entry() : count(0), blocked(FALSE) { }
00061         };
00062 
00063         typedef std::map<T, Entry> EntryMap;
00064 
00065         EntryMap * prevMap;
00066         EntryMap * currMap;
00067         
00068         U32 countLimit;
00069                 // maximum number of keys allowed per interval
00070                 
00071         U64 interval_usec;
00072                 // each map covers this time period
00073         U64 start_usec;
00074                 // currMap started counting at this time
00075                 // prevMap covers the previous interval
00076         
00077         LLKeyThrottleImpl() : prevMap(0), currMap(0),
00078                               countLimit(0), interval_usec(0),
00079                               start_usec(0) { };
00080 
00081         static U64 getTime()
00082         {
00083                 return LLFrameTimer::getTotalTime();
00084         }
00085 };
00086 
00087 
00088 template< class T >
00089 class LLKeyThrottle
00090 {
00091 public:
00092         LLKeyThrottle(U32 limit, F32 interval)
00093                 : m(* new LLKeyThrottleImpl<T>)
00094         {
00095                 // limit is the maximum number of keys
00096                 // allowed per interval (in seconds)
00097                 m.countLimit = limit;
00098                 m.interval_usec = (U64)(interval * USEC_PER_SEC);
00099                 m.start_usec = LLKeyThrottleImpl<T>::getTime();
00100 
00101                 m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap;
00102                 m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
00103         }
00104                 
00105         ~LLKeyThrottle()
00106         {
00107                 delete m.prevMap;
00108                 delete m.currMap;
00109                 delete &m;
00110         }
00111 
00112         enum State {
00113                 THROTTLE_OK,                    // rate not exceeded, let pass
00114                 THROTTLE_NEWLY_BLOCKED, // rate exceed for the first time
00115                 THROTTLE_BLOCKED,               // rate exceed, block key
00116         };
00117 
00118         // call each time the key wants use
00119         State noteAction(const T& id, S32 weight = 1)
00120         {
00121                 U64 now = LLKeyThrottleImpl<T>::getTime();
00122 
00123                 if (now >= (m.start_usec + m.interval_usec))
00124                 {
00125                         if (now < (m.start_usec + 2 * m.interval_usec))
00126                         {
00127                                 // prune old data
00128                                 delete m.prevMap;
00129                                 m.prevMap = m.currMap;
00130                                 m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
00131 
00132                                 m.start_usec += m.interval_usec;
00133                         }
00134                         else
00135                         {
00136                                 // lots of time has passed, all data is stale
00137                                 delete m.prevMap;
00138                                 delete m.currMap;
00139                                 m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap;
00140                                 m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
00141 
00142                                 m.start_usec = now;
00143                         }
00144                 }
00145 
00146                 U32 prevCount = 0;
00147                 BOOL prevBlocked = FALSE;
00148 
00149                 typename LLKeyThrottleImpl<T>::EntryMap::const_iterator prev = m.prevMap->find(id);
00150                 if (prev != m.prevMap->end())
00151                 {
00152                         prevCount = prev->second.count;
00153                         prevBlocked = prev->second.blocked;
00154                 }
00155 
00156                 typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
00157 
00158                 bool wereBlocked = curr.blocked || prevBlocked;
00159 
00160                 curr.count += weight;
00161 
00162                 // curr.count is the number of keys in
00163                 // this current 'time slice' from the beginning of it until now
00164                 // prevCount is the number of keys in the previous
00165                 // time slice scaled to be one full time slice back from the current 
00166                 // (now) time.
00167 
00168                 // compute current, windowed rate
00169                 F64 timeInCurrent = ((F64)(now - m.start_usec) / m.interval_usec);
00170                 F64 averageCount = curr.count + prevCount * (1.0 - timeInCurrent);
00171                 
00172                 curr.blocked |= averageCount > m.countLimit;
00173 
00174                 bool nowBlocked = curr.blocked || prevBlocked;
00175 
00176                 if (!nowBlocked)
00177                 {
00178                         return THROTTLE_OK;
00179                 }
00180                 else if (!wereBlocked)
00181                 {
00182                         return THROTTLE_NEWLY_BLOCKED;
00183                 }
00184                 else
00185                 {
00186                         return THROTTLE_BLOCKED;
00187                 }
00188         }
00189 
00190         // call to force throttle conditions for id
00191         void throttleAction(const T& id)
00192         {
00193                 noteAction(id);
00194                 typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
00195                 curr.count = llmax(m.countLimit, curr.count);
00196                 curr.blocked = TRUE;
00197         }
00198 
00199         // returns TRUE if key is blocked
00200         BOOL isThrottled(const T& id) const
00201         {
00202                 if (m.currMap->empty()
00203                         && m.prevMap->empty())
00204                 {
00205                         // most of the time we'll fall in here
00206                         return FALSE;
00207                 }
00208 
00209                 // NOTE, we ignore the case where id is in the map but the map is stale.  
00210                 // You might think that we'd stop throttling things in such a case, 
00211                 // however it may be that a god has disabled scripts in the region or 
00212                 // estate --> we probably want to report the state of the id when the 
00213                 // scripting engine was paused.
00214                 typename LLKeyThrottleImpl<T>::EntryMap::const_iterator entry = m.currMap->find(id);
00215                 if (entry != m.currMap->end())
00216                 {
00217                         return entry->second.blocked;
00218                 }
00219                 entry = m.prevMap->find(id);
00220                 if (entry != m.prevMap->end())
00221                 {
00222                         return entry->second.blocked;
00223                 }
00224                 return FALSE;
00225         }
00226 
00227 protected:
00228         LLKeyThrottleImpl<T>& m;
00229 };
00230 
00231 #endif

Generated on Fri May 16 08:32:03 2008 for SecondLife by  doxygen 1.5.5