aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs213
1 files changed, 213 insertions, 0 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
new file mode 100644
index 0000000..0a64095
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
@@ -0,0 +1,213 @@
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
28using System;
29
30namespace OpenSim.Region.ClientStack.LindenUDP
31{
32 /// <summary>
33 /// A hierarchical token bucket for bandwidth throttling. See
34 /// http://en.wikipedia.org/wiki/Token_bucket for more information
35 /// </summary>
36 public class TokenBucket
37 {
38 /// <summary>Parent bucket to this bucket, or null if this is a root
39 /// bucket</summary>
40 TokenBucket parent;
41 /// <summary>Size of the bucket in bytes. If zero, the bucket has
42 /// infinite capacity</summary>
43 int maxBurst;
44 /// <summary>Rate that the bucket fills, in bytes per millisecond. If
45 /// zero, the bucket always remains full</summary>
46 int tokensPerMS;
47 /// <summary>Number of tokens currently in the bucket</summary>
48 int content;
49 /// <summary>Time of the last drip, in system ticks</summary>
50 int lastDrip;
51
52 #region Properties
53
54 /// <summary>
55 /// 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
57 /// of its children buckets
58 /// </summary>
59 public TokenBucket Parent
60 {
61 get { return parent; }
62 }
63
64 /// <summary>
65 /// 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
67 /// </summary>
68 public int MaxBurst
69 {
70 get { return maxBurst; }
71 set { maxBurst = (value >= 0 ? value : 0); }
72 }
73
74 /// <summary>
75 /// 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
77 /// </summary>
78 /// <remarks>Tokens are added to the bucket any time
79 /// <seealso cref="RemoveTokens"/> is called, at the granularity of
80 /// the system tick interval (typically around 15-22ms)</remarks>
81 public int DripRate
82 {
83 get { return tokensPerMS * 1000; }
84 set
85 {
86 if (value == 0)
87 tokensPerMS = 0;
88 else
89 {
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 }
98 }
99
100 /// <summary>
101 /// The number of bytes that can be sent at this moment. This is the
102 /// current number of tokens in the bucket
103 /// <remarks>If this bucket has a parent bucket that does not have
104 /// enough tokens for a request, <seealso cref="RemoveTokens"/> will
105 /// return false regardless of the content of this bucket</remarks>
106 /// </summary>
107 public int Content
108 {
109 get
110 {
111 Drip();
112 return content;
113 }
114 }
115
116 #endregion Properties
117
118 /// <summary>
119 /// Default constructor
120 /// </summary>
121 /// <param name="parent">Parent bucket if this is a child bucket, or
122 /// null if this is a root bucket</param>
123 /// <param name="maxBurst">Maximum size of the bucket in bytes, or
124 /// zero if this bucket has no maximum capacity</param>
125 /// <param name="dripRate">Rate that the bucket fills, in bytes per
126 /// second. If zero, the bucket always remains full</param>
127 public TokenBucket(TokenBucket parent, int maxBurst, int dripRate)
128 {
129 this.parent = parent;
130 MaxBurst = maxBurst;
131 DripRate = dripRate;
132 lastDrip = Environment.TickCount & Int32.MaxValue;
133 }
134
135 /// <summary>
136 /// Remove a given number of tokens from the bucket
137 /// </summary>
138 /// <param name="amount">Number of tokens to remove from the bucket</param>
139 /// <returns>True if the requested number of tokens were removed from
140 /// the bucket, otherwise false</returns>
141 public bool RemoveTokens(int amount)
142 {
143 bool dummy;
144 return RemoveTokens(amount, out dummy);
145 }
146
147 /// <summary>
148 /// Remove a given number of tokens from the bucket
149 /// </summary>
150 /// <param name="amount">Number of tokens to remove from the bucket</param>
151 /// <param name="dripSucceeded">True if tokens were added to the bucket
152 /// during this call, otherwise false</param>
153 /// <returns>True if the requested number of tokens were removed from
154 /// the bucket, otherwise false</returns>
155 public bool RemoveTokens(int amount, out bool dripSucceeded)
156 {
157 if (maxBurst == 0)
158 {
159 dripSucceeded = true;
160 return true;
161 }
162
163 dripSucceeded = Drip();
164
165 if (content - amount >= 0)
166 {
167 if (parent != null && !parent.RemoveTokens(amount))
168 return false;
169
170 content -= amount;
171 return true;
172 }
173 else
174 {
175 return false;
176 }
177 }
178
179 /// <summary>
180 /// Add tokens to the bucket over time. The number of tokens added each
181 /// call depends on the length of time that has passed since the last
182 /// call to Drip
183 /// </summary>
184 /// <returns>True if tokens were added to the bucket, otherwise false</returns>
185 private bool Drip()
186 {
187 if (tokensPerMS == 0)
188 {
189 content = maxBurst;
190 return true;
191 }
192 else
193 {
194 int now = Environment.TickCount & Int32.MaxValue;
195 int deltaMS = now - lastDrip;
196
197 if (deltaMS <= 0)
198 {
199 if (deltaMS < 0)
200 lastDrip = now;
201 return false;
202 }
203
204 int dripAmount = deltaMS * tokensPerMS;
205
206 content = Math.Min(content + dripAmount, maxBurst);
207 lastDrip = now;
208
209 return true;
210 }
211 }
212 }
213}