PageRenderTime 72ms CodeModel.GetById 66ms app.highlight 4ms RepoModel.GetById 0ms app.codeStats 0ms

/Aurora/ClientStack/TokenBucket.cs

https://bitbucket.org/VirtualReality/software-testing
C# | 250 lines | 94 code | 24 blank | 132 comment | 11 complexity | 47af40ebdb290becee39ebc6a8e6b543 MD5 | raw file
  1/*
  2 * Copyright (c) Contributors, http://aurora-sim.org/, 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 Aurora-Sim 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 Aurora.ClientStack
 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>
 39        ///     Parent bucket to this bucket, or null if this is a root
 40        ///     bucket
 41        /// </summary>
 42        private readonly TokenBucket parent;
 43
 44        /// <summary>
 45        ///     Number of tokens currently in the bucket
 46        /// </summary>
 47        private int content;
 48
 49        /// <summary>
 50        ///     Time of the last drip, in system ticks
 51        /// </summary>
 52        private int lastDrip;
 53
 54        /// <summary>
 55        ///     Size of the bucket in bytes. If zero, the bucket has
 56        ///     infinite capacity
 57        /// </summary>
 58        private int maxBurst;
 59
 60        /// <summary>
 61        ///     Rate that the bucket fills, in bytes per millisecond. If
 62        ///     zero, the bucket always remains full
 63        /// </summary>
 64        private float tokensPerMS;
 65
 66        #region Properties
 67
 68        /// <summary>
 69        ///     The parent bucket of this bucket, or null if this bucket has no
 70        ///     parent. The parent bucket will limit the aggregate bandwidth of all
 71        ///     of its children buckets
 72        /// </summary>
 73        public TokenBucket Parent
 74        {
 75            get { return parent; }
 76        }
 77
 78        /// <summary>
 79        ///     Maximum burst rate in bytes per second. This is the maximum number
 80        ///     of tokens that can accumulate in the bucket at any one time
 81        /// </summary>
 82        public int MaxBurst
 83        {
 84            get { return maxBurst; }
 85            set { maxBurst = (value >= 0 ? value : 0); }
 86        }
 87
 88        /// <summary>
 89        ///     The speed limit of this bucket in bytes per second. This is the
 90        ///     number of tokens that are added to the bucket per second
 91        /// </summary>
 92        /// <remarks>
 93        ///     Tokens are added to the bucket any time
 94        ///     <seealso>
 95        ///         <cref>RemoveTokens</cref>
 96        ///     </seealso>
 97        ///     is called, at the granularity of
 98        ///     the system tick interval (typically around 15-22ms)
 99        /// </remarks>
100        public float DripRate
101        {
102            get { return tokensPerMS*1000; }
103            set
104            {
105                if (value == 0)
106                    tokensPerMS = 0;
107                else
108                {
109                    float bpms = (int) (value/1000.0f);
110
111                    tokensPerMS = bpms <= 0.5f ? .5f : bpms;
112                }
113            }
114        }
115
116        /// <summary>
117        ///     The speed limit of this bucket in bytes per millisecond
118        /// </summary>
119        public float DripPerMS
120        {
121            get { return tokensPerMS; }
122        }
123
124        /// <summary>
125        ///     The number of bytes that can be sent at this moment. This is the
126        ///     current number of tokens in the bucket
127        ///     <remarks>
128        ///         If this bucket has a parent bucket that does not have
129        ///         enough tokens for a request,
130        ///         <seealso>
131        ///             <cref>RemoveTokens</cref>
132        ///         </seealso>
133        ///         will
134        ///         return false regardless of the content of this bucket
135        ///     </remarks>
136        /// </summary>
137        public int Content
138        {
139            get { return content; }
140        }
141
142        #endregion Properties
143
144        /// <summary>
145        ///     Default constructor
146        /// </summary>
147        /// <param name="parent">
148        ///     Parent bucket if this is a child bucket, or
149        ///     null if this is a root bucket
150        /// </param>
151        /// <param name="maxBurst">
152        ///     Maximum size of the bucket in bytes, or
153        ///     zero if this bucket has no maximum capacity
154        /// </param>
155        /// <param name="dripRate">
156        ///     Rate that the bucket fills, in bytes per
157        ///     second. If zero, the bucket always remains full
158        /// </param>
159        public TokenBucket(TokenBucket parent, int maxBurst, int dripRate)
160        {
161            this.parent = parent;
162            MaxBurst = maxBurst;
163            DripRate = dripRate;
164            lastDrip = Environment.TickCount & Int32.MaxValue;
165        }
166
167        /// <summary>
168        ///     Remove a given number of tokens from the bucket
169        /// </summary>
170        /// <param name="amount">Number of tokens to remove from the bucket</param>
171        /// <returns>
172        ///     True if the requested number of tokens were removed from
173        ///     the bucket, otherwise false
174        /// </returns>
175        public bool RemoveTokens(int amount)
176        {
177            bool dummy;
178            return RemoveTokens(amount, out dummy);
179        }
180
181        /// <summary>
182        ///     Remove a given number of tokens from the bucket
183        /// </summary>
184        /// <param name="amount">Number of tokens to remove from the bucket</param>
185        /// <param name="dripSucceeded">
186        ///     True if tokens were added to the bucket
187        ///     during this call, otherwise false
188        /// </param>
189        /// <returns>
190        ///     True if the requested number of tokens were removed from
191        ///     the bucket, otherwise false
192        /// </returns>
193        public bool RemoveTokens(int amount, out bool dripSucceeded)
194        {
195            if (maxBurst == 0)
196            {
197                dripSucceeded = true;
198                return true;
199            }
200
201            dripSucceeded = Drip();
202
203            if (content - amount >= 0)
204            {
205                if (parent != null && !parent.RemoveTokens(amount))
206                    return false;
207
208                content -= amount;
209                return true;
210            }
211            return false;
212        }
213
214        /// <summary>
215        ///     Add tokens to the bucket over time. The number of tokens added each
216        ///     call depends on the length of time that has passed since the last
217        ///     call to Drip
218        /// </summary>
219        /// <returns>True if tokens were added to the bucket, otherwise false</returns>
220        public bool Drip()
221        {
222            if (tokensPerMS <= 0)
223            {
224                content = maxBurst;
225                return true;
226            }
227            int now = Environment.TickCount & Int32.MaxValue;
228            int deltaMS = now - lastDrip;
229
230            if (deltaMS <= 0)
231            {
232                if (deltaMS < 0)
233                    lastDrip = now;
234                return false;
235            }
236
237            int dripAmount = (int) (deltaMS*tokensPerMS);
238
239            content = Math.Min(content + dripAmount, maxBurst);
240            lastDrip = now;
241/*
242                if (dripAmount < 0 || content < 0)
243                    // sim has been idle for too long, integer has overflown
244                    // previous calculation is meaningless, let's put it at correct max
245                    content = maxBurst;
246*/
247            return true;
248        }
249    }
250}