TokenBucket.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  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. public string Identifier { get; private set; }
  43. public int DebugLevel { get; set; }
  44. /// <summary>
  45. /// Number of ticks (ms) per quantum, drip rate and max burst
  46. /// are defined over this interval.
  47. /// </summary>
  48. protected const Int32 m_ticksPerQuantum = 1000;
  49. /// <summary>
  50. /// This is the number of quantums worth of packets that can
  51. /// be accommodated during a burst
  52. /// </summary>
  53. protected const Double m_quantumsPerBurst = 1.5;
  54. /// <summary>
  55. /// </summary>
  56. protected const Int32 m_minimumDripRate = LLUDPServer.MTU;
  57. /// <summary>Time of the last drip, in system ticks</summary>
  58. protected Int32 m_lastDrip;
  59. /// <summary>
  60. /// The number of bytes that can be sent at this moment. This is the
  61. /// current number of tokens in the bucket
  62. /// </summary>
  63. protected Int64 m_tokenCount;
  64. /// <summary>
  65. /// Map of children buckets and their requested maximum burst rate
  66. /// </summary>
  67. protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>();
  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 { get; protected set; }
  74. /// <summary>
  75. /// Maximum burst rate in bytes per second. This is the maximum number
  76. /// of tokens that can accumulate in the bucket at any one time. This
  77. /// also sets the total request for leaf nodes
  78. /// </summary>
  79. protected Int64 m_burstRate;
  80. public Int64 RequestedBurstRate
  81. {
  82. get { return m_burstRate; }
  83. set { m_burstRate = (value < 0 ? 0 : value); }
  84. }
  85. public Int64 BurstRate
  86. {
  87. get {
  88. double rate = RequestedBurstRate * BurstRateModifier();
  89. if (rate < m_minimumDripRate * m_quantumsPerBurst)
  90. rate = m_minimumDripRate * m_quantumsPerBurst;
  91. return (Int64) rate;
  92. }
  93. }
  94. /// <summary>
  95. /// The requested drip rate for this particular bucket.
  96. /// </summary>
  97. /// <remarks>
  98. /// 0 then TotalDripRequest is used instead.
  99. /// Can never be above MaxDripRate.
  100. /// Tokens are added to the bucket at any time
  101. /// <seealso cref="RemoveTokens"/> is called, at the granularity of
  102. /// the system tick interval (typically around 15-22ms)
  103. /// FIXME: It is extremely confusing to be able to set a RequestedDripRate of 0 and then receive a positive
  104. /// number on get if TotalDripRequest is set. This also stops us being able to retrieve the fact that
  105. /// RequestedDripRate is set to 0. Really, this should always return m_dripRate and then we can get
  106. /// (m_dripRate == 0 ? TotalDripRequest : m_dripRate) on some other properties.
  107. /// </remarks>
  108. public virtual Int64 RequestedDripRate
  109. {
  110. get { return (m_dripRate == 0 ? TotalDripRequest : m_dripRate); }
  111. set
  112. {
  113. if (value <= 0)
  114. m_dripRate = 0;
  115. else if (MaxDripRate > 0 && value > MaxDripRate)
  116. m_dripRate = MaxDripRate;
  117. else
  118. m_dripRate = value;
  119. m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
  120. if (Parent != null)
  121. Parent.RegisterRequest(this, m_dripRate);
  122. }
  123. }
  124. /// <summary>
  125. /// Gets the drip rate.
  126. /// </summary>
  127. /// <value>
  128. /// DripRate can never be above max drip rate or below min drip rate.
  129. /// If we are a child bucket then the drip rate return is modifed by the total load on the capacity of the
  130. /// parent bucket.
  131. /// </value>
  132. public virtual Int64 DripRate
  133. {
  134. get
  135. {
  136. double rate;
  137. // FIXME: This doesn't properly work if we have a parent and children and a requested drip rate set
  138. // on ourselves which is not equal to the child drip rates.
  139. if (Parent == null)
  140. {
  141. if (TotalDripRequest > 0)
  142. rate = Math.Min(RequestedDripRate, TotalDripRequest);
  143. else
  144. rate = RequestedDripRate;
  145. }
  146. else
  147. {
  148. rate = (double)RequestedDripRate * Parent.DripRateModifier();
  149. }
  150. if (rate < m_minimumDripRate)
  151. rate = m_minimumDripRate;
  152. else if (MaxDripRate > 0 && rate > MaxDripRate)
  153. rate = MaxDripRate;
  154. return (Int64)rate;
  155. }
  156. }
  157. protected Int64 m_dripRate;
  158. // <summary>
  159. // The maximum rate for flow control. Drip rate can never be greater than this.
  160. // </summary>
  161. public Int64 MaxDripRate { get; set; }
  162. /// <summary>
  163. /// The current total of the requested maximum burst rates of children buckets.
  164. /// </summary>
  165. public Int64 TotalDripRequest { get; protected set; }
  166. /// <summary>
  167. /// Default constructor
  168. /// </summary>
  169. /// <param name="identifier">Identifier for this token bucket</param>
  170. /// <param name="parent">Parent bucket if this is a child bucket, or
  171. /// null if this is a root bucket</param>
  172. /// <param name="requestedDripRate">
  173. /// Requested rate that the bucket fills, in bytes per
  174. /// second. If zero, the bucket always remains full.
  175. /// </param>
  176. public TokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate)
  177. {
  178. Identifier = identifier;
  179. Parent = parent;
  180. RequestedDripRate = requestedDripRate;
  181. MaxDripRate = maxDripRate;
  182. m_lastDrip = Util.EnvironmentTickCount();
  183. }
  184. /// <summary>
  185. /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning
  186. /// no modification if the requested bandwidth is less than the
  187. /// max burst bandwidth all the way to the root of the throttle
  188. /// hierarchy. However, if any of the parents is over-booked, then
  189. /// the modifier will be less than 1.
  190. /// </summary>
  191. protected double DripRateModifier()
  192. {
  193. Int64 driprate = DripRate;
  194. double modifier = driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
  195. // if (DebugLevel > 0)
  196. // m_log.DebugFormat(
  197. // "[TOKEN BUCKET]: Returning drip modifier {0}/{1} = {2} from {3}",
  198. // driprate, TotalDripRequest, modifier, Identifier);
  199. return modifier;
  200. }
  201. /// <summary>
  202. /// </summary>
  203. protected double BurstRateModifier()
  204. {
  205. // for now... burst rate is always m_quantumsPerBurst (constant)
  206. // larger than drip rate so the ratio of burst requests is the
  207. // same as the drip ratio
  208. return DripRateModifier();
  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. TotalDripRequest = 0;
  220. foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
  221. TotalDripRequest += cref.Value;
  222. }
  223. // Pass the new values up to the parent
  224. if (Parent != null)
  225. {
  226. Int64 effectiveDripRate;
  227. if (RequestedDripRate > 0)
  228. effectiveDripRate = Math.Min(RequestedDripRate, TotalDripRequest);
  229. else
  230. effectiveDripRate = TotalDripRequest;
  231. Parent.RegisterRequest(this, effectiveDripRate);
  232. }
  233. }
  234. /// <summary>
  235. /// Remove the rate requested by a child of this throttle. Pass the
  236. /// changes up the hierarchy.
  237. /// </summary>
  238. public void UnregisterRequest(TokenBucket child)
  239. {
  240. lock (m_children)
  241. {
  242. m_children.Remove(child);
  243. TotalDripRequest = 0;
  244. foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
  245. TotalDripRequest += cref.Value;
  246. }
  247. // Pass the new values up to the parent
  248. if (Parent != null)
  249. Parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest));
  250. }
  251. /// <summary>
  252. /// Remove a given number of tokens from the bucket
  253. /// </summary>
  254. /// <param name="amount">Number of tokens to remove from the bucket</param>
  255. /// <returns>True if the requested number of tokens were removed from
  256. /// the bucket, otherwise false</returns>
  257. public bool RemoveTokens(Int64 amount)
  258. {
  259. // Deposit tokens for this interval
  260. Drip();
  261. // If we have enough tokens then remove them and return
  262. if (m_tokenCount - amount >= 0)
  263. {
  264. // we don't have to remove from the parent, the drip rate is already
  265. // reflective of the drip rate limits in the parent
  266. m_tokenCount -= amount;
  267. return true;
  268. }
  269. return false;
  270. }
  271. /// <summary>
  272. /// Deposit tokens into the bucket from a child bucket that did
  273. /// not use all of its available tokens
  274. /// </summary>
  275. protected void Deposit(Int64 count)
  276. {
  277. m_tokenCount += count;
  278. // Deposit the overflow in the parent bucket, this is how we share
  279. // unused bandwidth
  280. Int64 burstrate = BurstRate;
  281. if (m_tokenCount > burstrate)
  282. m_tokenCount = burstrate;
  283. }
  284. /// <summary>
  285. /// Add tokens to the bucket over time. The number of tokens added each
  286. /// call depends on the length of time that has passed since the last
  287. /// call to Drip
  288. /// </summary>
  289. /// <returns>True if tokens were added to the bucket, otherwise false</returns>
  290. protected void Drip()
  291. {
  292. // This should never happen... means we are a leaf node and were created
  293. // with no drip rate...
  294. if (DripRate == 0)
  295. {
  296. m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0 for {0}", Identifier);
  297. return;
  298. }
  299. // Determine the interval over which we are adding tokens, never add
  300. // more than a single quantum of tokens
  301. Int32 deltaMS = Math.Min(Util.EnvironmentTickCountSubtract(m_lastDrip), m_ticksPerQuantum);
  302. m_lastDrip = Util.EnvironmentTickCount();
  303. // This can be 0 in the very unusual case that the timer wrapped
  304. // It can be 0 if we try add tokens at a sub-tick rate
  305. if (deltaMS <= 0)
  306. return;
  307. Deposit(deltaMS * DripRate / m_ticksPerQuantum);
  308. }
  309. }
  310. public class AdaptiveTokenBucket : TokenBucket
  311. {
  312. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  313. public bool AdaptiveEnabled { get; set; }
  314. /// <summary>
  315. /// Target drip rate for this bucket.
  316. /// </summary>
  317. /// <remarks>Usually set by the client. If adaptive is enabled then throttles will increase until we reach this.</remarks>
  318. public Int64 TargetDripRate
  319. {
  320. get { return m_targetDripRate; }
  321. set
  322. {
  323. m_targetDripRate = Math.Max(value, m_minimumFlow);
  324. }
  325. }
  326. protected Int64 m_targetDripRate;
  327. // <summary>
  328. // Adjust drip rate in response to network conditions.
  329. // </summary>
  330. public virtual Int64 AdjustedDripRate
  331. {
  332. get { return m_dripRate; }
  333. set
  334. {
  335. m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value, m_minimumFlow, TargetDripRate);
  336. m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
  337. if (Parent != null)
  338. Parent.RegisterRequest(this, m_dripRate);
  339. }
  340. }
  341. /// <summary>
  342. /// The minimum rate for adaptive flow control.
  343. /// </summary>
  344. protected Int64 m_minimumFlow = 32000;
  345. /// <summary>
  346. /// Constructor for the AdaptiveTokenBucket class
  347. /// <param name="identifier">Unique identifier for the client</param>
  348. /// <param name="parent">Parent bucket in the hierarchy</param>
  349. /// <param name="requestedDripRate"></param>
  350. /// <param name="maxDripRate">The ceiling rate for adaptation</param>
  351. /// <param name="minDripRate">The floor rate for adaptation</param>
  352. /// </summary>
  353. public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate, Int64 minDripRate, bool enabled)
  354. : base(identifier, parent, requestedDripRate, maxDripRate)
  355. {
  356. AdaptiveEnabled = enabled;
  357. if (AdaptiveEnabled)
  358. {
  359. // m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled");
  360. m_minimumFlow = minDripRate;
  361. TargetDripRate = m_minimumFlow;
  362. AdjustedDripRate = m_minimumFlow;
  363. }
  364. }
  365. /// <summary>
  366. /// Reliable packets sent to the client for which we never received an ack adjust the drip rate down.
  367. /// <param name="packets">Number of packets that expired without successful delivery</param>
  368. /// </summary>
  369. public void ExpirePackets(Int32 packets)
  370. {
  371. if (AdaptiveEnabled)
  372. {
  373. if (DebugLevel > 0)
  374. m_log.WarnFormat(
  375. "[ADAPTIVEBUCKET] drop {0} by {1} expired packets for {2}",
  376. AdjustedDripRate, packets, Identifier);
  377. // AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,packets));
  378. // Compute the fallback solely on the rate allocated beyond the minimum, this
  379. // should smooth out the fallback to the minimum rate
  380. AdjustedDripRate = m_minimumFlow + (Int64) ((AdjustedDripRate - m_minimumFlow) / Math.Pow(2, packets));
  381. }
  382. }
  383. /// <summary>
  384. /// Reliable packets acked by the client adjust the drip rate up.
  385. /// <param name="packets">Number of packets successfully acknowledged</param>
  386. /// </summary>
  387. public void AcknowledgePackets(Int32 packets)
  388. {
  389. if (AdaptiveEnabled)
  390. AdjustedDripRate = AdjustedDripRate + packets * LLUDPServer.MTU;
  391. }
  392. /// <summary>
  393. /// Adjust the minimum flow level for the adaptive throttle, this will drop adjusted
  394. /// throttles back to the minimum levels
  395. /// <param>minDripRate--the new minimum flow</param>
  396. /// </summary>
  397. public void ResetMinimumAdaptiveFlow(Int64 minDripRate)
  398. {
  399. m_minimumFlow = minDripRate;
  400. TargetDripRate = m_minimumFlow;
  401. AdjustedDripRate = m_minimumFlow;
  402. }
  403. }
  404. }