diff options
author | Melanie | 2011-05-05 09:49:10 +0100 |
---|---|---|
committer | Melanie | 2011-05-05 09:49:10 +0100 |
commit | 4beb0c9b9b402c1e8f70a02c0efa557c1f292f1c (patch) | |
tree | 24e24cb7b424df74b13788d28be9d7588fbd99d0 /OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs | |
parent | Merge branch 'master' into careminster-presence-refactor (diff) | |
parent | Test m_Enabled on RemoveRegion. (diff) | |
download | opensim-SC-4beb0c9b9b402c1e8f70a02c0efa557c1f292f1c.zip opensim-SC-4beb0c9b9b402c1e8f70a02c0efa557c1f292f1c.tar.gz opensim-SC-4beb0c9b9b402c1e8f70a02c0efa557c1f292f1c.tar.bz2 opensim-SC-4beb0c9b9b402c1e8f70a02c0efa557c1f292f1c.tar.xz |
Merge branch 'master' into careminster-presence-refactor
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs new file mode 100644 index 0000000..29fd1a4 --- /dev/null +++ b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs | |||
@@ -0,0 +1,393 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.Reflection; | ||
32 | using OpenSim.Framework; | ||
33 | |||
34 | using log4net; | ||
35 | |||
36 | namespace OpenSim.Region.ClientStack.LindenUDP | ||
37 | { | ||
38 | /// <summary> | ||
39 | /// A hierarchical token bucket for bandwidth throttling. See | ||
40 | /// http://en.wikipedia.org/wiki/Token_bucket for more information | ||
41 | /// </summary> | ||
42 | public class TokenBucket | ||
43 | { | ||
44 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
45 | private static Int32 m_counter = 0; | ||
46 | |||
47 | private Int32 m_identifier; | ||
48 | |||
49 | /// <summary> | ||
50 | /// Number of ticks (ms) per quantum, drip rate and max burst | ||
51 | /// are defined over this interval. | ||
52 | /// </summary> | ||
53 | protected const Int32 m_ticksPerQuantum = 1000; | ||
54 | |||
55 | /// <summary> | ||
56 | /// This is the number of quantums worth of packets that can | ||
57 | /// be accommodated during a burst | ||
58 | /// </summary> | ||
59 | protected const Double m_quantumsPerBurst = 1.5; | ||
60 | |||
61 | /// <summary> | ||
62 | /// </summary> | ||
63 | protected const Int32 m_minimumDripRate = 1400; | ||
64 | |||
65 | /// <summary>Time of the last drip, in system ticks</summary> | ||
66 | protected Int32 m_lastDrip; | ||
67 | |||
68 | /// <summary> | ||
69 | /// The number of bytes that can be sent at this moment. This is the | ||
70 | /// current number of tokens in the bucket | ||
71 | /// </summary> | ||
72 | protected Int64 m_tokenCount; | ||
73 | |||
74 | /// <summary> | ||
75 | /// Map of children buckets and their requested maximum burst rate | ||
76 | /// </summary> | ||
77 | protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>(); | ||
78 | |||
79 | #region Properties | ||
80 | |||
81 | /// <summary> | ||
82 | /// The parent bucket of this bucket, or null if this bucket has no | ||
83 | /// parent. The parent bucket will limit the aggregate bandwidth of all | ||
84 | /// of its children buckets | ||
85 | /// </summary> | ||
86 | protected TokenBucket m_parent; | ||
87 | public TokenBucket Parent | ||
88 | { | ||
89 | get { return m_parent; } | ||
90 | set { m_parent = value; } | ||
91 | } | ||
92 | |||
93 | /// <summary> | ||
94 | /// Maximum burst rate in bytes per second. This is the maximum number | ||
95 | /// of tokens that can accumulate in the bucket at any one time. This | ||
96 | /// also sets the total request for leaf nodes | ||
97 | /// </summary> | ||
98 | protected Int64 m_burstRate; | ||
99 | public Int64 RequestedBurstRate | ||
100 | { | ||
101 | get { return m_burstRate; } | ||
102 | set { m_burstRate = (value < 0 ? 0 : value); } | ||
103 | } | ||
104 | |||
105 | public Int64 BurstRate | ||
106 | { | ||
107 | get { | ||
108 | double rate = RequestedBurstRate * BurstRateModifier(); | ||
109 | if (rate < m_minimumDripRate * m_quantumsPerBurst) | ||
110 | rate = m_minimumDripRate * m_quantumsPerBurst; | ||
111 | |||
112 | return (Int64) rate; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | /// <summary> | ||
117 | /// The speed limit of this bucket in bytes per second. This is the | ||
118 | /// number of tokens that are added to the bucket per quantum | ||
119 | /// </summary> | ||
120 | /// <remarks>Tokens are added to the bucket any time | ||
121 | /// <seealso cref="RemoveTokens"/> is called, at the granularity of | ||
122 | /// the system tick interval (typically around 15-22ms)</remarks> | ||
123 | protected Int64 m_dripRate; | ||
124 | public virtual Int64 RequestedDripRate | ||
125 | { | ||
126 | get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } | ||
127 | set { | ||
128 | m_dripRate = (value < 0 ? 0 : value); | ||
129 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); | ||
130 | m_totalDripRequest = m_dripRate; | ||
131 | if (m_parent != null) | ||
132 | m_parent.RegisterRequest(this,m_dripRate); | ||
133 | } | ||
134 | } | ||
135 | |||
136 | public virtual Int64 DripRate | ||
137 | { | ||
138 | get { | ||
139 | if (m_parent == null) | ||
140 | return Math.Min(RequestedDripRate,TotalDripRequest); | ||
141 | |||
142 | double rate = (double)RequestedDripRate * m_parent.DripRateModifier(); | ||
143 | if (rate < m_minimumDripRate) | ||
144 | rate = m_minimumDripRate; | ||
145 | |||
146 | return (Int64)rate; | ||
147 | } | ||
148 | } | ||
149 | |||
150 | /// <summary> | ||
151 | /// The current total of the requested maximum burst rates of | ||
152 | /// this bucket's children buckets. | ||
153 | /// </summary> | ||
154 | protected Int64 m_totalDripRequest; | ||
155 | public Int64 TotalDripRequest | ||
156 | { | ||
157 | get { return m_totalDripRequest; } | ||
158 | set { m_totalDripRequest = value; } | ||
159 | } | ||
160 | |||
161 | #endregion Properties | ||
162 | |||
163 | #region Constructor | ||
164 | |||
165 | /// <summary> | ||
166 | /// Default constructor | ||
167 | /// </summary> | ||
168 | /// <param name="parent">Parent bucket if this is a child bucket, or | ||
169 | /// null if this is a root bucket</param> | ||
170 | /// <param name="maxBurst">Maximum size of the bucket in bytes, or | ||
171 | /// zero if this bucket has no maximum capacity</param> | ||
172 | /// <param name="dripRate">Rate that the bucket fills, in bytes per | ||
173 | /// second. If zero, the bucket always remains full</param> | ||
174 | public TokenBucket(TokenBucket parent, Int64 dripRate) | ||
175 | { | ||
176 | m_identifier = m_counter++; | ||
177 | |||
178 | Parent = parent; | ||
179 | RequestedDripRate = dripRate; | ||
180 | // TotalDripRequest = dripRate; // this will be overwritten when a child node registers | ||
181 | // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); | ||
182 | m_lastDrip = Util.EnvironmentTickCount(); | ||
183 | } | ||
184 | |||
185 | #endregion Constructor | ||
186 | |||
187 | /// <summary> | ||
188 | /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning | ||
189 | /// no modification if the requested bandwidth is less than the | ||
190 | /// max burst bandwidth all the way to the root of the throttle | ||
191 | /// hierarchy. However, if any of the parents is over-booked, then | ||
192 | /// the modifier will be less than 1. | ||
193 | /// </summary> | ||
194 | protected double DripRateModifier() | ||
195 | { | ||
196 | Int64 driprate = DripRate; | ||
197 | return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; | ||
198 | } | ||
199 | |||
200 | /// <summary> | ||
201 | /// </summary> | ||
202 | protected double BurstRateModifier() | ||
203 | { | ||
204 | // for now... burst rate is always m_quantumsPerBurst (constant) | ||
205 | // larger than drip rate so the ratio of burst requests is the | ||
206 | // same as the drip ratio | ||
207 | return DripRateModifier(); | ||
208 | } | ||
209 | |||
210 | /// <summary> | ||
211 | /// Register drip rate requested by a child of this throttle. Pass the | ||
212 | /// changes up the hierarchy. | ||
213 | /// </summary> | ||
214 | public void RegisterRequest(TokenBucket child, Int64 request) | ||
215 | { | ||
216 | lock (m_children) | ||
217 | { | ||
218 | m_children[child] = request; | ||
219 | // m_totalDripRequest = m_children.Values.Sum(); | ||
220 | |||
221 | m_totalDripRequest = 0; | ||
222 | foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) | ||
223 | m_totalDripRequest += cref.Value; | ||
224 | } | ||
225 | |||
226 | // Pass the new values up to the parent | ||
227 | if (m_parent != null) | ||
228 | m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); | ||
229 | } | ||
230 | |||
231 | /// <summary> | ||
232 | /// Remove the rate requested by a child of this throttle. Pass the | ||
233 | /// changes up the hierarchy. | ||
234 | /// </summary> | ||
235 | public void UnregisterRequest(TokenBucket child) | ||
236 | { | ||
237 | lock (m_children) | ||
238 | { | ||
239 | m_children.Remove(child); | ||
240 | // m_totalDripRequest = m_children.Values.Sum(); | ||
241 | |||
242 | m_totalDripRequest = 0; | ||
243 | foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) | ||
244 | m_totalDripRequest += cref.Value; | ||
245 | } | ||
246 | |||
247 | |||
248 | // Pass the new values up to the parent | ||
249 | if (m_parent != null) | ||
250 | m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); | ||
251 | } | ||
252 | |||
253 | /// <summary> | ||
254 | /// Remove a given number of tokens from the bucket | ||
255 | /// </summary> | ||
256 | /// <param name="amount">Number of tokens to remove from the bucket</param> | ||
257 | /// <returns>True if the requested number of tokens were removed from | ||
258 | /// the bucket, otherwise false</returns> | ||
259 | public bool RemoveTokens(Int64 amount) | ||
260 | { | ||
261 | // Deposit tokens for this interval | ||
262 | Drip(); | ||
263 | |||
264 | // If we have enough tokens then remove them and return | ||
265 | if (m_tokenCount - amount >= 0) | ||
266 | { | ||
267 | // we don't have to remove from the parent, the drip rate is already | ||
268 | // reflective of the drip rate limits in the parent | ||
269 | m_tokenCount -= amount; | ||
270 | return true; | ||
271 | } | ||
272 | |||
273 | return false; | ||
274 | } | ||
275 | |||
276 | /// <summary> | ||
277 | /// Deposit tokens into the bucket from a child bucket that did | ||
278 | /// not use all of its available tokens | ||
279 | /// </summary> | ||
280 | protected void Deposit(Int64 count) | ||
281 | { | ||
282 | m_tokenCount += count; | ||
283 | |||
284 | // Deposit the overflow in the parent bucket, this is how we share | ||
285 | // unused bandwidth | ||
286 | Int64 burstrate = BurstRate; | ||
287 | if (m_tokenCount > burstrate) | ||
288 | m_tokenCount = burstrate; | ||
289 | } | ||
290 | |||
291 | /// <summary> | ||
292 | /// Add tokens to the bucket over time. The number of tokens added each | ||
293 | /// call depends on the length of time that has passed since the last | ||
294 | /// call to Drip | ||
295 | /// </summary> | ||
296 | /// <returns>True if tokens were added to the bucket, otherwise false</returns> | ||
297 | protected void Drip() | ||
298 | { | ||
299 | // This should never happen... means we are a leaf node and were created | ||
300 | // with no drip rate... | ||
301 | if (DripRate == 0) | ||
302 | { | ||
303 | m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0"); | ||
304 | return; | ||
305 | } | ||
306 | |||
307 | // Determine the interval over which we are adding tokens, never add | ||
308 | // more than a single quantum of tokens | ||
309 | Int32 deltaMS = Math.Min(Util.EnvironmentTickCountSubtract(m_lastDrip), m_ticksPerQuantum); | ||
310 | m_lastDrip = Util.EnvironmentTickCount(); | ||
311 | |||
312 | // This can be 0 in the very unusual case that the timer wrapped | ||
313 | // It can be 0 if we try add tokens at a sub-tick rate | ||
314 | if (deltaMS <= 0) | ||
315 | return; | ||
316 | |||
317 | Deposit(deltaMS * DripRate / m_ticksPerQuantum); | ||
318 | } | ||
319 | } | ||
320 | |||
321 | public class AdaptiveTokenBucket : TokenBucket | ||
322 | { | ||
323 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
324 | |||
325 | /// <summary> | ||
326 | /// The minimum rate for flow control. Minimum drip rate is one | ||
327 | /// packet per second. Open the throttle to 15 packets per second | ||
328 | /// or about 160kbps. | ||
329 | /// </summary> | ||
330 | protected const Int64 m_minimumFlow = m_minimumDripRate * 15; | ||
331 | |||
332 | // <summary> | ||
333 | // The maximum rate for flow control. Drip rate can never be | ||
334 | // greater than this. | ||
335 | // </summary> | ||
336 | protected Int64 m_maxDripRate = 0; | ||
337 | protected Int64 MaxDripRate | ||
338 | { | ||
339 | get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); } | ||
340 | set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); } | ||
341 | } | ||
342 | |||
343 | private bool m_enabled = false; | ||
344 | |||
345 | // <summary> | ||
346 | // | ||
347 | // </summary> | ||
348 | public virtual Int64 AdjustedDripRate | ||
349 | { | ||
350 | get { return m_dripRate; } | ||
351 | set { | ||
352 | m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value,m_minimumFlow,MaxDripRate); | ||
353 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); | ||
354 | if (m_parent != null) | ||
355 | m_parent.RegisterRequest(this,m_dripRate); | ||
356 | } | ||
357 | } | ||
358 | |||
359 | // <summary> | ||
360 | // | ||
361 | // </summary> | ||
362 | public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate, bool enabled) : base(parent,maxDripRate) | ||
363 | { | ||
364 | m_enabled = enabled; | ||
365 | |||
366 | if (m_enabled) | ||
367 | { | ||
368 | // m_log.DebugFormat("[TOKENBUCKET] Adaptive throttle enabled"); | ||
369 | MaxDripRate = maxDripRate; | ||
370 | AdjustedDripRate = m_minimumFlow; | ||
371 | } | ||
372 | } | ||
373 | |||
374 | // <summary> | ||
375 | // | ||
376 | // </summary> | ||
377 | public void ExpirePackets(Int32 count) | ||
378 | { | ||
379 | // m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count); | ||
380 | if (m_enabled) | ||
381 | AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count)); | ||
382 | } | ||
383 | |||
384 | // <summary> | ||
385 | // | ||
386 | // </summary> | ||
387 | public void AcknowledgePackets(Int32 count) | ||
388 | { | ||
389 | if (m_enabled) | ||
390 | AdjustedDripRate = AdjustedDripRate + count; | ||
391 | } | ||
392 | } | ||
393 | } | ||