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