EventTokenBucket.h (5864B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef NetEventTokenBucket_h__ 8 #define NetEventTokenBucket_h__ 9 10 #include "ARefBase.h" 11 #include "nsCOMPtr.h" 12 #include "nsDeque.h" 13 #include "nsINamed.h" 14 #include "nsITimer.h" 15 16 #include "mozilla/TimeStamp.h" 17 18 class nsICancelable; 19 20 namespace mozilla { 21 namespace net { 22 23 /* A token bucket is used to govern the maximum rate a series of events 24 can be executed at. For instance if your event was "eat a piece of cake" 25 then a token bucket configured to allow "1 piece per day" would spread 26 the eating of a 8 piece cake over 8 days even if you tried to eat the 27 whole thing up front. In a practical sense it 'costs' 1 token to execute 28 an event and tokens are 'earned' at a particular rate as time goes by. 29 30 The token bucket can be perfectly smooth or allow a configurable amount of 31 burstiness. A bursty token bucket allows you to save up unused credits, while 32 a perfectly smooth one would not. A smooth "1 per day" cake token bucket 33 would require 9 days to eat that cake if you skipped a slice on day 4 34 (use the token or lose it), while a token bucket configured with a burst 35 of 2 would just let you eat 2 slices on day 5 (the credits for day 4 and day 36 5) and finish the cake in the usual 8 days. 37 38 EventTokenBucket(hz=20, burst=5) creates a token bucket with the following 39 properties: 40 41 + events from an infinite stream will be admitted 20 times per second (i.e. 42 hz=20 means 1 event per 50 ms). Timers will be used to space things evenly 43 down to 5ms gaps (i.e. up to 200hz). Token buckets with rates greater than 44 200hz will admit multiple events with 5ms gaps between them. 10000hz is the 45 maximum rate and 1hz is the minimum rate. 46 47 + The burst size controls the limit of 'credits' that a token bucket can 48 accumulate when idle. For our (20,5) example each event requires 50ms of 49 credit (again, 20hz = 50ms per event). a burst size of 5 means that the 50 token bucket can accumulate a maximum of 250ms (5 * 50ms) for this bucket. 51 If no events have been admitted for the last full second the bucket can 52 still only accumulate 250ms of credit - but that credit means that 5 events 53 can be admitted without delay. A burst size of 1 is the minimum. The 54 EventTokenBucket is created with maximum credits already applied, but they 55 can be cleared with the ClearCredits() method. The maximum burst size is 15 56 minutes worth of events. 57 58 + An event is submitted to the token bucket asynchronously through 59 SubmitEvent(). The OnTokenBucketAdmitted() method of the submitted event 60 is used as a callback when the event is ready to run. A cancelable event is 61 returned to the SubmitEvent() caller for use in the case they do not wish 62 to wait for the callback. 63 */ 64 65 class EventTokenBucket; 66 67 class ATokenBucketEvent { 68 public: 69 virtual void OnTokenBucketAdmitted() = 0; 70 }; 71 72 class TokenBucketCancelable; 73 74 class EventTokenBucket : public nsITimerCallback, 75 public nsINamed, 76 public ARefBase { 77 public: 78 NS_DECL_THREADSAFE_ISUPPORTS 79 NS_DECL_NSITIMERCALLBACK 80 NS_DECL_NSINAMED 81 82 // This should be constructed on the main thread 83 EventTokenBucket(uint32_t eventsPerSecond, uint32_t burstSize); 84 85 // These public methods are all meant to be called from the socket thread 86 void ClearCredits(); 87 uint32_t BurstEventsAvailable(); 88 uint32_t QueuedEvents(); 89 90 // a paused token bucket will not process any events, but it will accumulate 91 // credits. ClearCredits can be used before unpausing if desired. 92 void Pause(); 93 void UnPause(); 94 void Stop(); 95 96 // The returned cancelable event can only be canceled from the socket thread 97 nsresult SubmitEvent(ATokenBucketEvent* event, nsICancelable** cancelable); 98 99 private: 100 virtual ~EventTokenBucket(); 101 void CleanupTimers(); 102 103 friend class RunNotifyEvent; 104 friend class SetTimerEvent; 105 106 bool TryImmediateDispatch(TokenBucketCancelable* cancelable); 107 void SetRate(uint32_t eventsPerSecond, uint32_t burstSize); 108 109 void DispatchEvents(); 110 void UpdateTimer(); 111 void UpdateCredits(); 112 113 const static uint64_t kUsecPerSec = 1000000; 114 const static uint64_t kUsecPerMsec = 1000; 115 const static uint64_t kMaxHz = 10000; 116 117 uint64_t 118 mUnitCost; // usec of credit needed for 1 event (from eventsPerSecond) 119 uint64_t mMaxCredit; // usec mCredit limit (from busrtSize) 120 uint64_t mCredit; // usec of accumulated credit. 121 122 bool mPaused; 123 bool mStopped; 124 nsRefPtrDeque<TokenBucketCancelable> mEvents; 125 bool mTimerArmed; 126 TimeStamp mLastUpdate; 127 128 // The timer is created on the main thread, but is armed and executes Notify() 129 // callbacks on the socket thread in order to maintain low latency of event 130 // delivery. 131 nsCOMPtr<nsITimer> mTimer; 132 133 #ifdef XP_WIN 134 // Windows timers are 15ms granularity by default. When we have active events 135 // that need to be dispatched at 50ms or less granularity we change the OS 136 // granularity to 1ms. 90 seconds after that need has elapsed we will change 137 // it back 138 const static uint64_t kCostFineGrainThreshold = 50 * kUsecPerMsec; 139 140 void FineGrainTimers(); // get 1ms granularity 141 void NormalTimers(); // reset to default granularity 142 void WantNormalTimers(); // reset after 90 seconds if not needed in interim 143 void FineGrainResetTimerNotify(); // delayed callback to reset 144 145 TimeStamp mLastFineGrainTimerUse; 146 bool mFineGrainTimerInUse; 147 bool mFineGrainResetTimerArmed; 148 nsCOMPtr<nsITimer> mFineGrainResetTimer; 149 #endif 150 }; 151 152 } // namespace net 153 } // namespace mozilla 154 155 #endif