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