From 45dc4e0a5442d1d03f7387164070145386a9b4e1 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 20 Oct 2009 18:19:17 -0700 Subject: * Added a sanity check to GetScriptAssemblies() and GetScriptStates() for the case where no scripting engine is enabled * Added TokenBucket.cs to OpenSim, with some fixes for setting a more accurate MaxBurst value and getting a more accurate Content value (by Drip()ing each get) --- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 2 + .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 2 + .../Region/ClientStack/LindenUDP/TokenBucket.cs | 213 +++++++++++++++++++++ .../LindenUDP/UnackedPacketCollection.cs | 2 + .../Framework/Scenes/SceneObjectPartInventory.cs | 26 ++- 5 files changed, 235 insertions(+), 10 deletions(-) create mode 100644 OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index bf0fda3..134cfe5 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -33,6 +33,8 @@ using OpenSim.Framework; using OpenMetaverse; using OpenMetaverse.Packets; +using TokenBucket = OpenSim.Region.ClientStack.LindenUDP.TokenBucket; + namespace OpenSim.Region.ClientStack.LindenUDP { #region Delegates diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 80ef95e..952d147 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -39,6 +39,8 @@ using OpenSim.Framework.Statistics; using OpenSim.Region.Framework.Scenes; using OpenMetaverse; +using TokenBucket = OpenSim.Region.ClientStack.LindenUDP.TokenBucket; + namespace OpenSim.Region.ClientStack.LindenUDP { /// diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs new file mode 100644 index 0000000..0a64095 --- /dev/null +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -0,0 +1,213 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; + +namespace OpenSim.Region.ClientStack.LindenUDP +{ + /// + /// A hierarchical token bucket for bandwidth throttling. See + /// http://en.wikipedia.org/wiki/Token_bucket for more information + /// + public class TokenBucket + { + /// Parent bucket to this bucket, or null if this is a root + /// bucket + TokenBucket parent; + /// Size of the bucket in bytes. If zero, the bucket has + /// infinite capacity + int maxBurst; + /// Rate that the bucket fills, in bytes per millisecond. If + /// zero, the bucket always remains full + int tokensPerMS; + /// Number of tokens currently in the bucket + int content; + /// Time of the last drip, in system ticks + int lastDrip; + + #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 + /// + public TokenBucket Parent + { + get { return parent; } + } + + /// + /// Maximum burst rate in bytes per second. This is the maximum number + /// of tokens that can accumulate in the bucket at any one time + /// + public int MaxBurst + { + get { return maxBurst; } + set { maxBurst = (value >= 0 ? value : 0); } + } + + /// + /// The speed limit of this bucket in bytes per second. This is the + /// number of tokens that are added to the bucket per second + /// + /// Tokens are added to the bucket any time + /// is called, at the granularity of + /// the system tick interval (typically around 15-22ms) + public int DripRate + { + get { return tokensPerMS * 1000; } + set + { + if (value == 0) + tokensPerMS = 0; + else + { + int bpms = (int)((float)value / 1000.0f); + + if (bpms <= 0) + tokensPerMS = 1; // 1 byte/ms is the minimum granularity + else + tokensPerMS = bpms; + } + } + } + + /// + /// The number of bytes that can be sent at this moment. This is the + /// current number of tokens in the bucket + /// If this bucket has a parent bucket that does not have + /// enough tokens for a request, will + /// return false regardless of the content of this bucket + /// + public int Content + { + get + { + Drip(); + return content; + } + } + + #endregion Properties + + /// + /// Default constructor + /// + /// 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, int maxBurst, int dripRate) + { + this.parent = parent; + MaxBurst = maxBurst; + DripRate = dripRate; + lastDrip = Environment.TickCount & Int32.MaxValue; + } + + /// + /// Remove a given number of tokens from the bucket + /// + /// 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(int amount) + { + bool dummy; + return RemoveTokens(amount, out dummy); + } + + /// + /// Remove a given number of tokens from the bucket + /// + /// Number of tokens to remove from the bucket + /// True if tokens were added to the bucket + /// during this call, otherwise false + /// True if the requested number of tokens were removed from + /// the bucket, otherwise false + public bool RemoveTokens(int amount, out bool dripSucceeded) + { + if (maxBurst == 0) + { + dripSucceeded = true; + return true; + } + + dripSucceeded = Drip(); + + if (content - amount >= 0) + { + if (parent != null && !parent.RemoveTokens(amount)) + return false; + + content -= amount; + return true; + } + else + { + return false; + } + } + + /// + /// Add tokens to the bucket over time. The number of tokens added each + /// call depends on the length of time that has passed since the last + /// call to Drip + /// + /// True if tokens were added to the bucket, otherwise false + private bool Drip() + { + if (tokensPerMS == 0) + { + content = maxBurst; + return true; + } + else + { + int now = Environment.TickCount & Int32.MaxValue; + int deltaMS = now - lastDrip; + + if (deltaMS <= 0) + { + if (deltaMS < 0) + lastDrip = now; + return false; + } + + int dripAmount = deltaMS * tokensPerMS; + + content = Math.Min(content + dripAmount, maxBurst); + lastDrip = now; + + return true; + } + } + } +} diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index f3242c1..87c7df4 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -147,6 +147,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP int now = Environment.TickCount; foreach (OutgoingPacket packet in packets.Values) { + // TickCount of zero means a packet is in the resend queue + // but hasn't actually been sent over the wire yet if (packet.TickCount == 0) continue; diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs index 098e010..f4ca877 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs @@ -871,12 +871,15 @@ namespace OpenSim.Region.Framework.Scenes { foreach (IScriptModule e in engines) { - string n = e.GetAssemblyName(item.ItemID); - if (n != "") + if (e != null) { - if (!ret.Contains(n)) - ret.Add(n); - break; + string n = e.GetAssemblyName(item.ItemID); + if (n != String.Empty) + { + if (!ret.Contains(n)) + ret.Add(n); + break; + } } } } @@ -898,12 +901,15 @@ namespace OpenSim.Region.Framework.Scenes { foreach (IScriptModule e in engines) { - string n = e.GetXMLState(item.ItemID); - if (n != "") + if (e != null) { - if (!ret.ContainsKey(item.ItemID)) - ret[item.ItemID] = n; - break; + string n = e.GetXMLState(item.ItemID); + if (n != String.Empty) + { + if (!ret.ContainsKey(item.ItemID)) + ret[item.ItemID] = n; + break; + } } } } -- cgit v1.1