netwerk/base/src/EventTokenBucket.h

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial