aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs288
1 files changed, 198 insertions, 90 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
index 91e3d20..07b0a1d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
@@ -26,6 +26,10 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.Reflection;
32using log4net;
29 33
30namespace OpenSim.Region.ClientStack.LindenUDP 34namespace OpenSim.Region.ClientStack.LindenUDP
31{ 35{
@@ -35,89 +39,126 @@ namespace OpenSim.Region.ClientStack.LindenUDP
35 /// </summary> 39 /// </summary>
36 public class TokenBucket 40 public class TokenBucket
37 { 41 {
38 /// <summary>Parent bucket to this bucket, or null if this is a root 42 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
39 /// bucket</summary> 43 private static Int32 m_counter = 0;
40 TokenBucket parent; 44
41 /// <summary>Size of the bucket in bytes. If zero, the bucket has 45 private Int32 m_identifier;
42 /// infinite capacity</summary> 46
43 int maxBurst; 47 /// <summary>
44 /// <summary>Rate that the bucket fills, in bytes per millisecond. If 48 /// Number of ticks (ms) per quantum, drip rate and max burst
45 /// zero, the bucket always remains full</summary> 49 /// are defined over this interval.
46 int tokensPerMS; 50 /// </summary>
47 /// <summary>Number of tokens currently in the bucket</summary> 51 private const Int32 m_ticksPerQuantum = 1000;
48 int content; 52
53 /// <summary>
54 /// This is the number of quantums worth of packets that can
55 /// be accommodated during a burst
56 /// </summary>
57 private const Double m_quantumsPerBurst = 1.5;
58
59 /// <summary>
60 /// </summary>
61 private const Int32 m_minimumDripRate = 1400;
62
49 /// <summary>Time of the last drip, in system ticks</summary> 63 /// <summary>Time of the last drip, in system ticks</summary>
50 int lastDrip; 64 private Int32 m_lastDrip;
65
66 /// <summary>
67 /// The number of bytes that can be sent at this moment. This is the
68 /// current number of tokens in the bucket
69 /// </summary>
70 private Int64 m_tokenCount;
51 71
52 #region Properties 72 /// <summary>
73 /// Map of children buckets and their requested maximum burst rate
74 /// </summary>
75 private Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>();
76
77#region Properties
53 78
54 /// <summary> 79 /// <summary>
55 /// The parent bucket of this bucket, or null if this bucket has no 80 /// The parent bucket of this bucket, or null if this bucket has no
56 /// parent. The parent bucket will limit the aggregate bandwidth of all 81 /// parent. The parent bucket will limit the aggregate bandwidth of all
57 /// of its children buckets 82 /// of its children buckets
58 /// </summary> 83 /// </summary>
84 private TokenBucket m_parent;
59 public TokenBucket Parent 85 public TokenBucket Parent
60 { 86 {
61 get { return parent; } 87 get { return m_parent; }
88 set { m_parent = value; }
62 } 89 }
63 90
64 /// <summary> 91 /// <summary>
65 /// Maximum burst rate in bytes per second. This is the maximum number 92 /// Maximum burst rate in bytes per second. This is the maximum number
66 /// of tokens that can accumulate in the bucket at any one time 93 /// of tokens that can accumulate in the bucket at any one time. This
94 /// also sets the total request for leaf nodes
67 /// </summary> 95 /// </summary>
68 public int MaxBurst 96 private Int64 m_burstRate;
97 public Int64 RequestedBurstRate
69 { 98 {
70 get { return maxBurst; } 99 get { return m_burstRate; }
71 set { maxBurst = (value >= 0 ? value : 0); } 100 set { m_burstRate = (value < 0 ? 0 : value); }
72 } 101 }
73 102
103 public Int64 BurstRate
104 {
105 get {
106 double rate = RequestedBurstRate * BurstRateModifier();
107 if (rate < m_minimumDripRate * m_quantumsPerBurst)
108 rate = m_minimumDripRate * m_quantumsPerBurst;
109
110 return (Int64) rate;
111 }
112 }
113
74 /// <summary> 114 /// <summary>
75 /// The speed limit of this bucket in bytes per second. This is the 115 /// The speed limit of this bucket in bytes per second. This is the
76 /// number of tokens that are added to the bucket per second 116 /// number of tokens that are added to the bucket per quantum
77 /// </summary> 117 /// </summary>
78 /// <remarks>Tokens are added to the bucket any time 118 /// <remarks>Tokens are added to the bucket any time
79 /// <seealso cref="RemoveTokens"/> is called, at the granularity of 119 /// <seealso cref="RemoveTokens"/> is called, at the granularity of
80 /// the system tick interval (typically around 15-22ms)</remarks> 120 /// the system tick interval (typically around 15-22ms)</remarks>
81 public int DripRate 121 private Int64 m_dripRate;
122 public Int64 RequestedDripRate
82 { 123 {
83 get { return tokensPerMS * 1000; } 124 get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); }
84 set 125 set {
85 { 126 m_dripRate = (value < 0 ? 0 : value);
86 if (value == 0) 127 m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
87 tokensPerMS = 0; 128 m_totalDripRequest = m_dripRate;
88 else 129 if (m_parent != null)
89 { 130 m_parent.RegisterRequest(this,m_dripRate);
90 int bpms = (int)((float)value / 1000.0f);
91
92 if (bpms <= 0)
93 tokensPerMS = 1; // 1 byte/ms is the minimum granularity
94 else
95 tokensPerMS = bpms;
96 }
97 } 131 }
98 } 132 }
99 133
100 /// <summary> 134 public Int64 DripRate
101 /// The speed limit of this bucket in bytes per millisecond
102 /// </summary>
103 public int DripPerMS
104 { 135 {
105 get { return tokensPerMS; } 136 get {
137 if (m_parent == null)
138 return Math.Min(RequestedDripRate,TotalDripRequest);
139
140 double rate = (double)RequestedDripRate * m_parent.DripRateModifier();
141 if (rate < m_minimumDripRate)
142 rate = m_minimumDripRate;
143
144 return (Int64)rate;
145 }
106 } 146 }
107 147
108 /// <summary> 148 /// <summary>
109 /// The number of bytes that can be sent at this moment. This is the 149 /// The current total of the requested maximum burst rates of
110 /// current number of tokens in the bucket 150 /// this bucket's children buckets.
111 /// <remarks>If this bucket has a parent bucket that does not have
112 /// enough tokens for a request, <seealso cref="RemoveTokens"/> will
113 /// return false regardless of the content of this bucket</remarks>
114 /// </summary> 151 /// </summary>
115 public int Content 152 private Int64 m_totalDripRequest;
116 { 153 public Int64 TotalDripRequest
117 get { return content; } 154 {
118 } 155 get { return m_totalDripRequest; }
156 set { m_totalDripRequest = value; }
157 }
158
159#endregion Properties
119 160
120 #endregion Properties 161#region Constructor
121 162
122 /// <summary> 163 /// <summary>
123 /// Default constructor 164 /// Default constructor
@@ -128,46 +169,114 @@ namespace OpenSim.Region.ClientStack.LindenUDP
128 /// zero if this bucket has no maximum capacity</param> 169 /// zero if this bucket has no maximum capacity</param>
129 /// <param name="dripRate">Rate that the bucket fills, in bytes per 170 /// <param name="dripRate">Rate that the bucket fills, in bytes per
130 /// second. If zero, the bucket always remains full</param> 171 /// second. If zero, the bucket always remains full</param>
131 public TokenBucket(TokenBucket parent, int maxBurst, int dripRate) 172 public TokenBucket(TokenBucket parent, Int64 dripRate)
173 {
174 m_identifier = m_counter++;
175
176 Parent = parent;
177 RequestedDripRate = dripRate;
178 // TotalDripRequest = dripRate; // this will be overwritten when a child node registers
179 // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst);
180 m_lastDrip = Environment.TickCount & Int32.MaxValue;
181 }
182
183#endregion Constructor
184
185 /// <summary>
186 /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning
187 /// no modification if the requested bandwidth is less than the
188 /// max burst bandwidth all the way to the root of the throttle
189 /// hierarchy. However, if any of the parents is over-booked, then
190 /// the modifier will be less than 1.
191 /// </summary>
192 private double DripRateModifier()
193 {
194 Int64 driprate = DripRate;
195 return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
196 }
197
198 /// <summary>
199 /// </summary>
200 private double BurstRateModifier()
201 {
202 // for now... burst rate is always m_quantumsPerBurst (constant)
203 // larger than drip rate so the ratio of burst requests is the
204 // same as the drip ratio
205 return DripRateModifier();
206 }
207
208 /// <summary>
209 /// Register drip rate requested by a child of this throttle. Pass the
210 /// changes up the hierarchy.
211 /// </summary>
212 public void RegisterRequest(TokenBucket child, Int64 request)
132 { 213 {
133 this.parent = parent; 214 m_children[child] = request;
134 MaxBurst = maxBurst; 215 // m_totalDripRequest = m_children.Values.Sum();
135 DripRate = dripRate; 216
136 lastDrip = Environment.TickCount; 217 m_totalDripRequest = 0;
218 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
219 m_totalDripRequest += cref.Value;
220
221 // Pass the new values up to the parent
222 if (m_parent != null)
223 m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
137 } 224 }
138 225
139 /// <summary> 226 /// <summary>
227 /// Remove the rate requested by a child of this throttle. Pass the
228 /// changes up the hierarchy.
229 /// </summary>
230 public void UnregisterRequest(TokenBucket child)
231 {
232 m_children.Remove(child);
233 // m_totalDripRequest = m_children.Values.Sum();
234
235 m_totalDripRequest = 0;
236 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
237 m_totalDripRequest += cref.Value;
238
239 // Pass the new values up to the parent
240 if (m_parent != null)
241 m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
242 }
243
244 /// <summary>
140 /// Remove a given number of tokens from the bucket 245 /// Remove a given number of tokens from the bucket
141 /// </summary> 246 /// </summary>
142 /// <param name="amount">Number of tokens to remove from the bucket</param> 247 /// <param name="amount">Number of tokens to remove from the bucket</param>
143 /// <returns>True if the requested number of tokens were removed from 248 /// <returns>True if the requested number of tokens were removed from
144 /// the bucket, otherwise false</returns> 249 /// the bucket, otherwise false</returns>
145 public bool RemoveTokens(int amount) 250 public bool RemoveTokens(Int64 amount)
146 { 251 {
147 if (maxBurst == 0) 252 // Deposit tokens for this interval
148 {
149 return true;
150 }
151
152 if (amount > maxBurst)
153 {
154 throw new Exception("amount " + amount + " exceeds maxBurst " + maxBurst);
155 }
156
157 Drip(); 253 Drip();
158 254
159 if (content < amount) 255 // If we have enough tokens then remove them and return
256 if (m_tokenCount - amount >= 0)
160 { 257 {
161 return false; 258 // we don't have to remove from the parent, the drip rate is already
259 // reflective of the drip rate limits in the parent
260 m_tokenCount -= amount;
261 return true;
162 } 262 }
163 263
164 if (parent != null && !parent.RemoveTokens(amount)) 264 return false;
165 { 265 }
166 return false;
167 }
168 266
169 content -= amount; 267 /// <summary>
170 return true; 268 /// Deposit tokens into the bucket from a child bucket that did
269 /// not use all of its available tokens
270 /// </summary>
271 private void Deposit(Int64 count)
272 {
273 m_tokenCount += count;
274
275 // Deposit the overflow in the parent bucket, this is how we share
276 // unused bandwidth
277 Int64 burstrate = BurstRate;
278 if (m_tokenCount > burstrate)
279 m_tokenCount = burstrate;
171 } 280 }
172 281
173 /// <summary> 282 /// <summary>
@@ -176,30 +285,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
176 /// call to Drip 285 /// call to Drip
177 /// </summary> 286 /// </summary>
178 /// <returns>True if tokens were added to the bucket, otherwise false</returns> 287 /// <returns>True if tokens were added to the bucket, otherwise false</returns>
179 public bool Drip() 288 private void Drip()
180 { 289 {
181 if (tokensPerMS == 0) 290 // This should never happen... means we are a leaf node and were created
291 // with no drip rate...
292 if (DripRate == 0)
182 { 293 {
183 content = maxBurst; 294 m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0");
184 return true; 295 return;
185 } 296 }
297
298 // Determine the interval over which we are adding tokens, never add
299 // more than a single quantum of tokens
300 Int32 now = Environment.TickCount & Int32.MaxValue;
301 Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum);
186 302
187 int now = Environment.TickCount; 303 m_lastDrip = now;
188 int deltaMS = now - lastDrip;
189 lastDrip = now;
190 304
305 // This can be 0 in the very unusual case that the timer wrapped
306 // It can be 0 if we try add tokens at a sub-tick rate
191 if (deltaMS <= 0) 307 if (deltaMS <= 0)
192 { 308 return;
193 return false;
194 }
195 309
196 long dripAmount = (long)deltaMS * (long)tokensPerMS + (long)content; 310 Deposit(deltaMS * DripRate / m_ticksPerQuantum);
197 if (dripAmount > maxBurst)
198 {
199 dripAmount = maxBurst;
200 }
201 content = (int)dripAmount;
202 return true;
203 } 311 }
204 } 312 }
205} 313}