Source code

Revision control

Other Tools

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* event);
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
nsDeque 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