diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index f91abfe2b8..45013b3b20 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs @@ -144,8 +144,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP get { return m_throttleClient; } } - /// Throttle bucket for this agent's connection - private readonly TokenBucket m_throttleCategory; /// Throttle buckets for each packet category private readonly TokenBucket[] m_throttleCategories; /// Outgoing queues for throttled packets @@ -163,6 +161,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_maxRTO = 60000; public bool m_deliverPackets = true; + private float m_burstTime; + public int m_lastStartpingTimeMS; public int m_pingMS; @@ -216,17 +216,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (maxRTO != 0) m_maxRTO = maxRTO; + m_burstTime = rates.BrustTime; + float m_burst = rates.ClientMaxRate * m_burstTime; + // Create a token bucket throttle for this client that has the scene token bucket as a parent - m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled); - // Create a token bucket throttle for the total categary with the client bucket as a throttle - m_throttleCategory = new TokenBucket(m_throttleClient, 0); + m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.ClientMaxRate, m_burst, rates.AdaptiveThrottlesEnabled); // Create an array of token buckets for this clients different throttle categories m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; m_cannibalrate = rates.CannibalizeTextureRate; - long totalrate = 0; - long catrate = 0; + m_burst = rates.Total * rates.BrustTime; for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) { @@ -235,13 +235,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Initialize the packet outboxes, where packets sit while they are waiting for tokens m_packetOutboxes[i] = new DoubleLocklessQueue(); // Initialize the token buckets that control the throttling for each category - catrate = rates.GetRate(type); - totalrate += catrate; - m_throttleCategories[i] = new TokenBucket(m_throttleCategory, catrate); + m_throttleCategories[i] = new TokenBucket(m_throttleClient, rates.GetRate(type), m_burst); } - m_throttleCategory.RequestedDripRate = totalrate; - // Default the retransmission timeout to one second RTO = m_defaultRTO; @@ -285,7 +281,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; - m_info.totalThrottle = (int)m_throttleCategory.DripRate; + m_info.totalThrottle = (int)m_throttleClient.DripRate; return m_info; } @@ -373,6 +369,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Make sure none of the throttles are set below our packet MTU, // otherwise a throttle could become permanently clogged + +/* not using floats resend = Math.Max(resend, LLUDPServer.MTU); land = Math.Max(land, LLUDPServer.MTU); wind = Math.Max(wind, LLUDPServer.MTU); @@ -380,6 +378,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP task = Math.Max(task, LLUDPServer.MTU); texture = Math.Max(texture, LLUDPServer.MTU); asset = Math.Max(asset, LLUDPServer.MTU); +*/ // Since most textures are now delivered through http, make it possible // to cannibalize some of the bw from the texture throttle to use for @@ -388,7 +387,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP texture = (int)((1 - m_cannibalrate) * texture); int total = resend + land + wind + cloud + task + texture + asset; - + + float m_burst = total * m_burstTime; + //m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, Total={8}", // AgentID, resend, land, wind, cloud, task, texture, asset, total); @@ -397,26 +398,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; bucket.RequestedDripRate = resend; + bucket.RequestedBurst = m_burst; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; bucket.RequestedDripRate = land; + bucket.RequestedBurst = m_burst; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; bucket.RequestedDripRate = wind; + bucket.RequestedBurst = m_burst; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; bucket.RequestedDripRate = cloud; + bucket.RequestedBurst = m_burst; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; bucket.RequestedDripRate = asset; + bucket.RequestedBurst = m_burst; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; bucket.RequestedDripRate = task; + bucket.RequestedBurst = m_burst; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; bucket.RequestedDripRate = texture; - - m_throttleCategory.RequestedDripRate = total; + bucket.RequestedBurst = m_burst; // Reset the packed throttles cached data m_packedThrottles = null; @@ -465,10 +471,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP public int GetCatBytesCanSend(ThrottleOutPacketType cat, int timeMS) { - TokenBucket bucket = m_throttleCategories[(int)cat]; - int bytes = timeMS * (int)(bucket.RequestedDripRate / 1000); - bytes += (int)bucket.CurrentTokenCount(); - return bytes; + int icat = (int)cat; + if (icat > 0 && icat < THROTTLE_CATEGORY_COUNT) + { + TokenBucket bucket = m_throttleCategories[icat]; + return bucket.GetCatBytesCanSend(timeMS); + } + else + return 0; } /// @@ -572,6 +582,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_udpServer.SendPacketFinal(nextPacket); m_nextPackets[i] = null; packetSent = true; + + if (m_packetOutboxes[i].Count < 5) + emptyCategories |= CategoryToFlag(i); } } else @@ -599,6 +612,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Send the packet m_udpServer.SendPacketFinal(packet); packetSent = true; + + if (queue.Count < 5) + emptyCategories |= CategoryToFlag(i); } else { @@ -606,11 +622,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_nextPackets[i] = packet; } - // If the queue is empty after this dequeue, fire the queue - // empty callback now so it has a chance to fill before we - // get back here - if (queue.Count == 0) - emptyCategories |= CategoryToFlag(i); } else { diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index dea9d7fe2c..9b3802d8b3 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -449,7 +449,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } #endregion BinaryStats - m_throttle = new TokenBucket(null, sceneThrottleBps); + m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps * 10e-3f); ThrottleRates = new ThrottleRates(configSource); Random rnd = new Random(Util.EnvironmentTickCount()); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs index 2b307c7c9b..451dee577a 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs @@ -62,6 +62,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Amount of the texture throttle to steal for the task throttle public double CannibalizeTextureRate; + public int ClientMaxRate; + public float BrustTime; + /// /// Default constructor /// @@ -80,8 +83,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP Texture = throttleConfig.GetInt("texture_default", 18500); Asset = throttleConfig.GetInt("asset_default", 10500); - // 2500000 bps max - Total = throttleConfig.GetInt("client_throttle_max_bps",312500); + Total = Resend + Land + Wind + Cloud + Task + Texture + Asset; + // 3000000 bps default max + ClientMaxRate = throttleConfig.GetInt("client_throttle_max_bps", 375000); + if (ClientMaxRate > 1000000) + ClientMaxRate = 1000000; // no more than 8Mbps + + BrustTime = (float)throttleConfig.GetInt("client_throttle_burtsTimeMS", 10); + BrustTime *= 1e-3f; AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs index 26467bc57c..384439cb72 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/TokenBucket.cs @@ -44,13 +44,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static Int32 m_counter = 0; -// private Int32 m_identifier; - - /// - /// Number of ticks (ms) per quantum, drip rate and max burst - /// are defined over this interval. - /// - protected const Int32 m_ticksPerQuantum = 1000; +// private Int32 m_identifier; + + protected const float m_timeScale = 1e-3f; /// /// This is the number of m_minimumDripRate bytes @@ -59,11 +55,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// to recheck a bucket in ms /// /// - protected const Double m_quantumsPerBurst = 5; + protected const float m_quantumsPerBurst = 5; /// /// - protected const Int32 m_minimumDripRate = 1400; + protected const float m_minimumDripRate = 1400; /// Time of the last drip, in system ticks protected Int32 m_lastDrip; @@ -72,12 +68,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// The number of bytes that can be sent at this moment. This is the /// current number of tokens in the bucket /// - protected Int64 m_tokenCount; + protected float m_tokenCount; /// /// Map of children buckets and their requested maximum burst rate /// - protected Dictionary m_children = new Dictionary(); + protected Dictionary m_children = new Dictionary(); #region Properties @@ -97,33 +93,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// This is the maximum number /// of tokens that can accumulate in the bucket at any one time. This /// also sets the total request for leaf nodes - /// this is not a rate. /// - protected Int64 m_burstRate; - public Int64 RequestedBurstRate + protected float m_burst; + public float RequestedBurst { - get { return m_burstRate; } + get { return m_burst; } set { - double rate = (value < 0 ? 0 : value); + float rate = (value < 0 ? 0 : value); if (rate < m_minimumDripRate) rate = m_minimumDripRate; else if (rate > m_minimumDripRate * m_quantumsPerBurst) rate = m_minimumDripRate * m_quantumsPerBurst; - m_burstRate = (Int64)rate; + m_burst = rate; } } - public Int64 BurstRate + public float Burst { get { - double rate = RequestedBurstRate * BurstRateModifier(); + float rate = RequestedBurst * BurstModifier(); if (rate < m_minimumDripRate) rate = m_minimumDripRate; - else if (rate > m_minimumDripRate * m_quantumsPerBurst) - rate = m_minimumDripRate * m_quantumsPerBurst; - - return (Int64) rate; + return (float)rate; } } @@ -134,40 +126,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Tokens are added to the bucket any time /// is called, at the granularity of /// the system tick interval (typically around 15-22ms) - protected Int64 m_dripRate; - public virtual Int64 RequestedDripRate + protected float m_dripRate; + public virtual float RequestedDripRate { get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } set { m_dripRate = (value < 0 ? 0 : value); m_totalDripRequest = m_dripRate; - double rate = m_dripRate; - if (rate > m_minimumDripRate * m_quantumsPerBurst) - rate = m_minimumDripRate * m_quantumsPerBurst; - else if (rate < m_minimumDripRate) - rate = m_minimumDripRate; - - m_burstRate = (Int64)rate; - - m_tokenCount = 0; - if (m_parent != null) m_parent.RegisterRequest(this,m_dripRate); } } - public virtual Int64 DripRate + public virtual float DripRate { get { + float rate = Math.Min(RequestedDripRate,TotalDripRequest); if (m_parent == null) - return Math.Min(RequestedDripRate,TotalDripRequest); - - double rate = (double)RequestedDripRate * m_parent.DripRateModifier(); + return rate; + + rate *= m_parent.DripRateModifier(); if (rate < m_minimumDripRate) rate = m_minimumDripRate; - return (Int64)rate; + return (float)rate; } } @@ -175,8 +158,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// The current total of the requested maximum burst rates of /// this bucket's children buckets. /// - protected Int64 m_totalDripRequest; - public Int64 TotalDripRequest + protected float m_totalDripRequest; + public float TotalDripRequest { get { return m_totalDripRequest; } set { m_totalDripRequest = value; } @@ -195,13 +178,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// 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) + public TokenBucket(TokenBucket parent, float dripRate, float MaxBurst) { // m_identifier = m_counter++; m_counter++; Parent = parent; RequestedDripRate = dripRate; + RequestedBurst = MaxBurst; // TotalDripRequest = dripRate; // this will be overwritten when a child node registers // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); m_lastDrip = Util.EnvironmentTickCount() + 100000; @@ -216,15 +200,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// hierarchy. However, if any of the parents is over-booked, then /// the modifier will be less than 1. /// - protected double DripRateModifier() + protected float DripRateModifier() { - Int64 driprate = DripRate; - return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; + float driprate = DripRate; + return driprate >= TotalDripRequest ? 1.0f : driprate / TotalDripRequest; } /// /// - protected double BurstRateModifier() + protected float BurstModifier() { // for now... burst rate is always m_quantumsPerBurst (constant) // larger than drip rate so the ratio of burst requests is the @@ -236,7 +220,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Register drip rate requested by a child of this throttle. Pass the /// changes up the hierarchy. /// - public void RegisterRequest(TokenBucket child, Int64 request) + public void RegisterRequest(TokenBucket child, float request) { lock (m_children) { @@ -244,7 +228,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // m_totalDripRequest = m_children.Values.Sum(); m_totalDripRequest = 0; - foreach (KeyValuePair cref in m_children) + foreach (KeyValuePair cref in m_children) m_totalDripRequest += cref.Value; } @@ -265,7 +249,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // m_totalDripRequest = m_children.Values.Sum(); m_totalDripRequest = 0; - foreach (KeyValuePair cref in m_children) + foreach (KeyValuePair cref in m_children) m_totalDripRequest += cref.Value; } @@ -281,7 +265,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Number of tokens to remove from the bucket /// True if the requested number of tokens were removed from /// the bucket, otherwise false - public bool RemoveTokens(Int64 amount) + public bool RemoveTokens(int amount) { // Deposit tokens for this interval Drip(); @@ -298,24 +282,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP return false; } - public long CurrentTokenCount() + public int GetCatBytesCanSend(int timeMS) { - return m_tokenCount; - } - - /// - /// Deposit tokens into the bucket from a child bucket that did - /// not use all of its available tokens - /// - protected void Deposit(Int64 count) - { - m_tokenCount += count; - - // Deposit the overflow in the parent bucket, this is how we share - // unused bandwidth - Int64 burstrate = BurstRate; - if (m_tokenCount > burstrate) - m_tokenCount = burstrate; +// return (int)(m_tokenCount + timeMS * m_dripRate * 1e-3); + return (int)(timeMS * m_dripRate * 1e-3); } /// @@ -334,12 +304,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return; } - // Determine the interval over which we are adding tokens, never add - // more than a single quantum of tokens - - // No... add no more than the estimated time between checks - - Int32 deltaMS = Math.Min(Util.EnvironmentTickCountSubtract(m_lastDrip), m_ticksPerQuantum); + Int32 deltaMS = Util.EnvironmentTickCountSubtract(m_lastDrip); m_lastDrip = Util.EnvironmentTickCount(); // This can be 0 in the very unusual case that the timer wrapped @@ -347,7 +312,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (deltaMS <= 0) return; - Deposit(deltaMS * DripRate / m_ticksPerQuantum); + m_tokenCount += deltaMS * DripRate * m_timeScale; + + float burst = Burst; + if (m_tokenCount > burst) + m_tokenCount = burst; } } @@ -357,20 +326,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// 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. + /// packet per second. /// - protected const Int64 m_minimumFlow = m_minimumDripRate; + + protected const float m_minimumFlow = 50000; // // The maximum rate for flow control. Drip rate can never be // greater than this. // - protected Int64 m_maxDripRate = 0; - protected Int64 MaxDripRate + + protected float m_maxDripRate = 0; + public float MaxDripRate { get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); } - set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); } + set + { + m_maxDripRate = (value == 0 ? m_totalDripRequest : Math.Max(value, m_minimumFlow)); + } } private bool m_enabled = false; @@ -378,18 +351,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP // // // - public virtual Int64 AdjustedDripRate + public virtual float AdjustedDripRate { get { return m_dripRate; } set { - m_dripRate = OpenSim.Framework.Util.Clamp(value,m_minimumFlow,MaxDripRate); - - double rate = m_dripRate; - if (rate > m_minimumDripRate * m_quantumsPerBurst) - rate = m_minimumDripRate * m_quantumsPerBurst; - else if (rate < m_minimumDripRate) - rate = m_minimumDripRate; - m_burstRate = (Int64)rate; + m_dripRate = OpenSim.Framework.Util.Clamp(value,m_minimumFlow,MaxDripRate); if (m_parent != null) m_parent.RegisterRequest(this,m_dripRate); @@ -399,16 +365,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP // // // - public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate, bool enabled) : base(parent,maxDripRate) + public AdaptiveTokenBucket(TokenBucket parent, float maxDripRate,float maxBurst, bool enabled) + : base(parent, maxDripRate,maxBurst) { m_enabled = enabled; + + MaxDripRate = maxDripRate; - if (m_enabled) - { - // m_log.DebugFormat("[TOKENBUCKET] Adaptive throttle enabled"); - MaxDripRate = maxDripRate; - AdjustedDripRate = m_minimumFlow; - } + if (enabled) + AdjustedDripRate = m_maxDripRate * .5f; + else + AdjustedDripRate = m_maxDripRate; } //