From 134f86e8d5c414409631b25b8c6f0ee45fbd8631 Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Thu, 3 Nov 2016 21:44:39 +1000 Subject: Initial update to OpenSim 0.8.2.1 source code. --- .../Region/ClientStack/Linden/UDP/TokenBucket.cs | 292 +++++++++++++-------- 1 file changed, 181 insertions(+), 111 deletions(-) (limited to 'OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs') diff --git a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs index 4c33db5..4616203 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * @@ -42,9 +42,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP public class TokenBucket { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private static Int32 m_counter = 0; - -// private Int32 m_identifier; + + public string Identifier { get; private set; } + + public int DebugLevel { get; set; } /// /// Number of ticks (ms) per quantum, drip rate and max burst @@ -60,7 +61,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// - protected const Int32 m_minimumDripRate = 1400; + protected const Int32 m_minimumDripRate = LLUDPServer.MTU; /// Time of the last drip, in system ticks protected Int32 m_lastDrip; @@ -75,20 +76,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Map of children buckets and their requested maximum burst rate /// protected Dictionary m_children = new Dictionary(); - -#region Properties /// /// The parent bucket of this bucket, or null if this bucket has no /// parent. The parent bucket will limit the aggregate bandwidth of all /// of its children buckets /// - protected TokenBucket m_parent; - public TokenBucket Parent - { - get { return m_parent; } - set { m_parent = value; } - } + public TokenBucket Parent { get; protected set; } /// /// Maximum burst rate in bytes per second. This is the maximum number @@ -114,77 +108,106 @@ namespace OpenSim.Region.ClientStack.LindenUDP } /// - /// The speed limit of this bucket in bytes per second. This is the - /// number of tokens that are added to the bucket per quantum + /// The requested drip rate for this particular bucket. /// - /// Tokens are added to the bucket any time + /// + /// 0 then TotalDripRequest is used instead. + /// Can never be above MaxDripRate. + /// Tokens are added to the bucket at any time /// is called, at the granularity of - /// the system tick interval (typically around 15-22ms) - protected Int64 m_dripRate; + /// the system tick interval (typically around 15-22ms) + /// FIXME: It is extremely confusing to be able to set a RequestedDripRate of 0 and then receive a positive + /// number on get if TotalDripRequest is set. This also stops us being able to retrieve the fact that + /// RequestedDripRate is set to 0. Really, this should always return m_dripRate and then we can get + /// (m_dripRate == 0 ? TotalDripRequest : m_dripRate) on some other properties. + /// public virtual Int64 RequestedDripRate { - get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } - set { - m_dripRate = (value < 0 ? 0 : value); + get { return (m_dripRate == 0 ? TotalDripRequest : m_dripRate); } + set + { + if (value <= 0) + m_dripRate = 0; + else if (MaxDripRate > 0 && value > MaxDripRate) + m_dripRate = MaxDripRate; + else + m_dripRate = value; + m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); - m_totalDripRequest = m_dripRate; - if (m_parent != null) - m_parent.RegisterRequest(this,m_dripRate); + + if (Parent != null) + Parent.RegisterRequest(this, m_dripRate); } } + /// + /// Gets the drip rate. + /// + /// + /// DripRate can never be above max drip rate or below min drip rate. + /// If we are a child bucket then the drip rate return is modifed by the total load on the capacity of the + /// parent bucket. + /// public virtual Int64 DripRate { - get { - if (m_parent == null) - return Math.Min(RequestedDripRate,TotalDripRequest); - - double rate = (double)RequestedDripRate * m_parent.DripRateModifier(); + get + { + double rate; + + // FIXME: This doesn't properly work if we have a parent and children and a requested drip rate set + // on ourselves which is not equal to the child drip rates. + if (Parent == null) + { + if (TotalDripRequest > 0) + rate = Math.Min(RequestedDripRate, TotalDripRequest); + else + rate = RequestedDripRate; + } + else + { + rate = (double)RequestedDripRate * Parent.DripRateModifier(); + } + if (rate < m_minimumDripRate) rate = m_minimumDripRate; + else if (MaxDripRate > 0 && rate > MaxDripRate) + rate = MaxDripRate; return (Int64)rate; } } + protected Int64 m_dripRate; + + // + // The maximum rate for flow control. Drip rate can never be greater than this. + // + public Int64 MaxDripRate { get; set; } /// - /// The current total of the requested maximum burst rates of - /// this bucket's children buckets. + /// The current total of the requested maximum burst rates of children buckets. /// - protected Int64 m_totalDripRequest; - public Int64 TotalDripRequest - { - get { return m_totalDripRequest; } - set { m_totalDripRequest = value; } - } - -#endregion Properties - -#region Constructor + public Int64 TotalDripRequest { get; protected set; } /// /// Default constructor /// + /// Identifier for this token bucket /// Parent bucket if this is a child bucket, or /// null if this is a root bucket - /// Maximum size of the bucket in bytes, or - /// zero if this bucket has no maximum capacity - /// Rate that the bucket fills, in bytes per - /// second. If zero, the bucket always remains full - public TokenBucket(TokenBucket parent, Int64 dripRate) + /// + /// Requested rate that the bucket fills, in bytes per + /// second. If zero, the bucket always remains full. + /// + public TokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate) { -// m_identifier = m_counter++; - m_counter++; + Identifier = identifier; Parent = parent; - RequestedDripRate = dripRate; - // TotalDripRequest = dripRate; // this will be overwritten when a child node registers - // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); + RequestedDripRate = requestedDripRate; + MaxDripRate = maxDripRate; m_lastDrip = Util.EnvironmentTickCount(); } -#endregion Constructor - /// /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning /// no modification if the requested bandwidth is less than the @@ -195,7 +218,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected double DripRateModifier() { Int64 driprate = DripRate; - return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; + double modifier = driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; + +// if (DebugLevel > 0) +// m_log.DebugFormat( +// "[TOKEN BUCKET]: Returning drip modifier {0}/{1} = {2} from {3}", +// driprate, TotalDripRequest, modifier, Identifier); + + return modifier; } /// @@ -217,16 +247,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP lock (m_children) { m_children[child] = request; - // m_totalDripRequest = m_children.Values.Sum(); - m_totalDripRequest = 0; + TotalDripRequest = 0; foreach (KeyValuePair cref in m_children) - m_totalDripRequest += cref.Value; + TotalDripRequest += cref.Value; } // Pass the new values up to the parent - if (m_parent != null) - m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); + if (Parent != null) + { + Int64 effectiveDripRate; + + if (RequestedDripRate > 0) + effectiveDripRate = Math.Min(RequestedDripRate, TotalDripRequest); + else + effectiveDripRate = TotalDripRequest; + + Parent.RegisterRequest(this, effectiveDripRate); + } } /// @@ -238,17 +276,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP lock (m_children) { m_children.Remove(child); - // m_totalDripRequest = m_children.Values.Sum(); - m_totalDripRequest = 0; + TotalDripRequest = 0; foreach (KeyValuePair cref in m_children) - m_totalDripRequest += cref.Value; + TotalDripRequest += cref.Value; } - // Pass the new values up to the parent - if (m_parent != null) - m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); + if (Parent != null) + Parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); } /// @@ -301,7 +337,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // with no drip rate... if (DripRate == 0) { - m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0"); + m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0 for {0}", Identifier); return; } @@ -321,74 +357,108 @@ namespace OpenSim.Region.ClientStack.LindenUDP public class AdaptiveTokenBucket : TokenBucket { -// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public bool AdaptiveEnabled { get; set; } /// - /// The minimum rate for flow control. Minimum drip rate is one - /// packet per second. Open the throttle to 15 packets per second - /// or about 160kbps. + /// Target drip rate for this bucket. /// - protected const Int64 m_minimumFlow = m_minimumDripRate * 15; - - // - // The maximum rate for flow control. Drip rate can never be - // greater than this. - // - protected Int64 m_maxDripRate = 0; - protected Int64 MaxDripRate - { - get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); } - set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); } + /// Usually set by the client. If adaptive is enabled then throttles will increase until we reach this. + public Int64 TargetDripRate + { + get { return m_targetDripRate; } + set + { + m_targetDripRate = Math.Max(value, m_minimumFlow); + } } - - private bool m_enabled = false; - + protected Int64 m_targetDripRate; + // - // + // Adjust drip rate in response to network conditions. // public virtual Int64 AdjustedDripRate { get { return m_dripRate; } - set { - m_dripRate = OpenSim.Framework.Util.Clamp(value,m_minimumFlow,MaxDripRate); + set + { + m_dripRate = OpenSim.Framework.Util.Clamp(value, m_minimumFlow, TargetDripRate); m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); - if (m_parent != null) - m_parent.RegisterRequest(this,m_dripRate); + + if (Parent != null) + Parent.RegisterRequest(this, m_dripRate); } } + + /// + /// The minimum rate for adaptive flow control. + /// + protected Int64 m_minimumFlow = 32000; - // - // - // - public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate, bool enabled) : base(parent,maxDripRate) + /// + /// Constructor for the AdaptiveTokenBucket class + /// Unique identifier for the client + /// Parent bucket in the hierarchy + /// + /// The ceiling rate for adaptation + /// The floor rate for adaptation + /// + public AdaptiveTokenBucket(string identifier, TokenBucket parent, Int64 requestedDripRate, Int64 maxDripRate, Int64 minDripRate, bool enabled) + : base(identifier, parent, requestedDripRate, maxDripRate) { - m_enabled = enabled; + AdaptiveEnabled = enabled; - if (m_enabled) + if (AdaptiveEnabled) { - // m_log.DebugFormat("[TOKENBUCKET] Adaptive throttle enabled"); - MaxDripRate = maxDripRate; +// m_log.DebugFormat("[TOKENBUCKET]: Adaptive throttle enabled"); + m_minimumFlow = minDripRate; + TargetDripRate = m_minimumFlow; AdjustedDripRate = m_minimumFlow; } } - // - // - // - public void ExpirePackets(Int32 count) + /// + /// Reliable packets sent to the client for which we never received an ack adjust the drip rate down. + /// Number of packets that expired without successful delivery + /// + public void ExpirePackets(Int32 packets) { - // m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count); - if (m_enabled) - AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count)); + if (AdaptiveEnabled) + { + if (DebugLevel > 0) + m_log.WarnFormat( + "[ADAPTIVEBUCKET] drop {0} by {1} expired packets for {2}", + AdjustedDripRate, packets, Identifier); + + // AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,packets)); + + // Compute the fallback solely on the rate allocated beyond the minimum, this + // should smooth out the fallback to the minimum rate + AdjustedDripRate = m_minimumFlow + (Int64) ((AdjustedDripRate - m_minimumFlow) / Math.Pow(2, packets)); + } } - // - // - // - public void AcknowledgePackets(Int32 count) + /// + /// Reliable packets acked by the client adjust the drip rate up. + /// Number of packets successfully acknowledged + /// + public void AcknowledgePackets(Int32 packets) + { + if (AdaptiveEnabled) + AdjustedDripRate = AdjustedDripRate + packets * LLUDPServer.MTU; + } + + /// + /// Adjust the minimum flow level for the adaptive throttle, this will drop adjusted + /// throttles back to the minimum levels + /// minDripRate--the new minimum flow + /// + public void ResetMinimumAdaptiveFlow(Int64 minDripRate) { - if (m_enabled) - AdjustedDripRate = AdjustedDripRate + count; + m_minimumFlow = minDripRate; + TargetDripRate = m_minimumFlow; + AdjustedDripRate = m_minimumFlow; } } -} +} \ No newline at end of file -- cgit v1.1