TokenBucket.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  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. using System;
  28. using System.Collections;
  29. using System.Collections.Generic;
  30. using System.Reflection;
  31. using OpenSim.Framework;
  32. using log4net;
  33. namespace OpenSim.Region.ClientStack.LindenUDP
  34. {
  35. /// <summary>
  36. /// A hierarchical token bucket for bandwidth throttling. See
  37. /// http://en.wikipedia.org/wiki/Token_bucket for more information
  38. /// </summary>
  39. public class TokenBucket
  40. {
  41. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  42. private static Int32 m_counter = 0;
  43. /// <summary>
  44. /// minimum recovery rate, ie bandwith
  45. /// </summary>
  46. protected const float MINDRIPRATE = 500;
  47. // maximim burst size, ie max number of bytes token can have
  48. protected const float MAXBURST = 7500;
  49. /// <summary>Time of the last drip</summary>
  50. protected double m_lastDrip;
  51. /// <summary>
  52. /// The number of bytes that can be sent at this moment. This is the
  53. /// current number of tokens in the bucket
  54. /// </summary>
  55. protected float m_tokenCount;
  56. /// <summary>
  57. /// Map of children buckets and their requested maximum burst rate
  58. /// </summary>
  59. protected Dictionary<TokenBucket, float> m_children = new Dictionary<TokenBucket, float>();
  60. #region Properties
  61. /// <summary>
  62. /// The parent bucket of this bucket, or null if this bucket has no
  63. /// parent. The parent bucket will limit the aggregate bandwidth of all
  64. /// of its children buckets
  65. /// </summary>
  66. protected TokenBucket m_parent;
  67. public TokenBucket Parent
  68. {
  69. get { return m_parent; }
  70. set { m_parent = value; }
  71. }
  72. /// <summary>
  73. /// This is the maximum number
  74. /// of tokens that can accumulate in the bucket at any one time. This
  75. /// also sets the total request for leaf nodes
  76. /// </summary>
  77. protected float m_burst;
  78. protected float m_maxDripRate = 0;
  79. public virtual float MaxDripRate
  80. {
  81. get { return m_maxDripRate; }
  82. set { m_maxDripRate = value; }
  83. }
  84. public float RequestedBurst
  85. {
  86. get { return m_burst; }
  87. set {
  88. float rate = (value < 0 ? 0 : value);
  89. if (rate > MAXBURST)
  90. rate = MAXBURST;
  91. m_burst = rate;
  92. }
  93. }
  94. public float Burst
  95. {
  96. get
  97. {
  98. return RequestedBurst * BurstModifier();
  99. }
  100. }
  101. /// <summary>
  102. /// The requested drip rate for this particular bucket.
  103. /// </summary>
  104. /// <remarks>
  105. /// 0 then TotalDripRequest is used instead.
  106. /// Can never be above MaxDripRate.
  107. /// Tokens are added to the bucket at any time
  108. /// <seealso cref="RemoveTokens"/> is called, at the granularity of
  109. /// the system tick interval (typically around 15-22ms)</remarks>
  110. protected float m_dripRate;
  111. public float RequestedDripRate
  112. {
  113. get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); }
  114. set {
  115. m_dripRate = (value < 0 ? 0 : value);
  116. m_totalDripRequest = m_dripRate;
  117. if (m_parent != null)
  118. m_parent.RegisterRequest(this,m_dripRate);
  119. }
  120. }
  121. public float DripRate
  122. {
  123. get {
  124. float rate = Math.Min(RequestedDripRate,TotalDripRequest);
  125. if (m_parent == null)
  126. return rate;
  127. rate *= m_parent.DripRateModifier();
  128. if (rate < MINDRIPRATE)
  129. rate = MINDRIPRATE;
  130. return rate;
  131. }
  132. }
  133. /// <summary>
  134. /// The current total of the requested maximum burst rates of children buckets.
  135. /// </summary>
  136. protected float m_totalDripRequest;
  137. public float TotalDripRequest
  138. {
  139. get { return m_totalDripRequest; }
  140. set { m_totalDripRequest = value; }
  141. }
  142. #endregion Properties
  143. #region Constructor
  144. /// <summary>
  145. /// Default constructor
  146. /// </summary>
  147. /// <param name="identifier">Identifier for this token bucket</param>
  148. /// <param name="parent">Parent bucket if this is a child bucket, or
  149. /// null if this is a root bucket</param>
  150. /// <param name="maxBurst">Maximum size of the bucket in bytes, or
  151. /// zero if this bucket has no maximum capacity</param>
  152. /// <param name="dripRate">Rate that the bucket fills, in bytes per
  153. /// second. If zero, the bucket always remains full</param>
  154. public TokenBucket(TokenBucket parent, float dripRate, float MaxBurst)
  155. {
  156. m_counter++;
  157. Parent = parent;
  158. RequestedDripRate = dripRate;
  159. RequestedBurst = MaxBurst;
  160. m_lastDrip = Util.GetTimeStamp() + 1000; // skip first drip
  161. }
  162. #endregion Constructor
  163. /// <summary>
  164. /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning
  165. /// no modification if the requested bandwidth is less than the
  166. /// max burst bandwidth all the way to the root of the throttle
  167. /// hierarchy. However, if any of the parents is over-booked, then
  168. /// the modifier will be less than 1.
  169. /// </summary>
  170. protected float DripRateModifier()
  171. {
  172. float driprate = DripRate;
  173. return driprate >= TotalDripRequest ? 1.0f : (driprate / TotalDripRequest);
  174. }
  175. /// <summary>
  176. /// </summary>
  177. protected float BurstModifier()
  178. {
  179. return DripRateModifier();
  180. }
  181. /// <summary>
  182. /// Register drip rate requested by a child of this throttle. Pass the
  183. /// changes up the hierarchy.
  184. /// </summary>
  185. public void RegisterRequest(TokenBucket child, float request)
  186. {
  187. lock (m_children)
  188. {
  189. m_children[child] = request;
  190. m_totalDripRequest = 0;
  191. foreach (KeyValuePair<TokenBucket, float> cref in m_children)
  192. m_totalDripRequest += cref.Value;
  193. }
  194. // Pass the new values up to the parent
  195. if (m_parent != null)
  196. m_parent.RegisterRequest(this, Math.Min(RequestedDripRate, TotalDripRequest));
  197. }
  198. /// <summary>
  199. /// Remove the rate requested by a child of this throttle. Pass the
  200. /// changes up the hierarchy.
  201. /// </summary>
  202. public void UnregisterRequest(TokenBucket child)
  203. {
  204. lock (m_children)
  205. {
  206. m_children.Remove(child);
  207. m_totalDripRequest = 0;
  208. foreach (KeyValuePair<TokenBucket, float> cref in m_children)
  209. m_totalDripRequest += cref.Value;
  210. }
  211. // Pass the new values up to the parent
  212. if (Parent != null)
  213. Parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
  214. }
  215. /// <summary>
  216. /// Remove a given number of tokens from the bucket
  217. /// </summary>
  218. /// <param name="amount">Number of tokens to remove from the bucket</param>
  219. /// <returns>True if the requested number of tokens were removed from
  220. /// the bucket, otherwise false</returns>
  221. public bool RemoveTokens(int amount)
  222. {
  223. // Deposit tokens for this interval
  224. Drip();
  225. // If we have enough tokens then remove them and return
  226. if (m_tokenCount > 0)
  227. {
  228. m_tokenCount -= amount;
  229. return true;
  230. }
  231. return false;
  232. }
  233. public bool CheckTokens(int amount)
  234. {
  235. return (m_tokenCount > 0);
  236. }
  237. public int GetCatBytesCanSend(int timeMS)
  238. {
  239. return (int)(timeMS * DripRate * 1e-3);
  240. }
  241. /// <summary>
  242. /// Add tokens to the bucket over time. The number of tokens added each
  243. /// call depends on the length of time that has passed since the last
  244. /// call to Drip
  245. /// </summary>
  246. /// <returns>True if tokens were added to the bucket, otherwise false</returns>
  247. protected void Drip()
  248. {
  249. // This should never happen... means we are a leaf node and were created
  250. // with no drip rate...
  251. if (DripRate == 0)
  252. {
  253. m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0 for {0}", m_counter);
  254. return;
  255. }
  256. double now = Util.GetTimeStamp();
  257. double delta = now - m_lastDrip;
  258. m_lastDrip = now;
  259. if (delta <= 0)
  260. return;
  261. m_tokenCount += (float)delta * DripRate;
  262. float burst = Burst;
  263. if (m_tokenCount > burst)
  264. m_tokenCount = burst;
  265. }
  266. }
  267. public class AdaptiveTokenBucket : TokenBucket
  268. {
  269. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  270. public bool AdaptiveEnabled { get; set; }
  271. /// <summary>
  272. /// The minimum rate for flow control. Minimum drip rate is one
  273. /// packet per second.
  274. /// </summary>
  275. protected const float m_minimumFlow = 50000;
  276. // <summary>
  277. // The maximum rate for flow control. Drip rate can never be
  278. // greater than this.
  279. // </summary>
  280. public override float MaxDripRate
  281. {
  282. get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); }
  283. set
  284. {
  285. m_maxDripRate = (value == 0 ? m_totalDripRequest : Math.Max(value, m_minimumFlow));
  286. }
  287. }
  288. private bool m_enabled = false;
  289. // <summary>
  290. // Adjust drip rate in response to network conditions.
  291. // </summary>
  292. public float AdjustedDripRate
  293. {
  294. get { return m_dripRate; }
  295. set
  296. {
  297. m_dripRate = OpenSim.Framework.Util.Clamp<float>(value, m_minimumFlow, MaxDripRate);
  298. if (m_parent != null)
  299. m_parent.RegisterRequest(this, m_dripRate);
  300. }
  301. }
  302. // <summary>
  303. //
  304. // </summary>
  305. public AdaptiveTokenBucket(TokenBucket parent, float maxDripRate, float maxBurst, bool enabled)
  306. : base(parent, maxDripRate, maxBurst)
  307. {
  308. m_enabled = enabled;
  309. m_maxDripRate = (maxDripRate == 0 ? m_totalDripRequest : Math.Max(maxDripRate, m_minimumFlow));
  310. if (enabled)
  311. m_dripRate = m_maxDripRate * .5f;
  312. else
  313. m_dripRate = m_maxDripRate;
  314. if (m_parent != null)
  315. m_parent.RegisterRequest(this, m_dripRate);
  316. }
  317. /// <summary>
  318. /// Reliable packets sent to the client for which we never received an ack adjust the drip rate down.
  319. /// <param name="packets">Number of packets that expired without successful delivery</param>
  320. /// </summary>
  321. public void ExpirePackets(Int32 count)
  322. {
  323. // m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count);
  324. if (m_enabled)
  325. AdjustedDripRate = (Int64)(AdjustedDripRate / Math.Pow(2, count));
  326. }
  327. // <summary>
  328. //
  329. // </summary>
  330. public void AcknowledgePackets(Int32 count)
  331. {
  332. if (m_enabled)
  333. AdjustedDripRate = AdjustedDripRate + count;
  334. }
  335. }
  336. }