From 6e9cdb9ce32807ddd1a39e72c436b8fd788768d2 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 11 Apr 2011 09:06:28 -0700 Subject: New tokenbucket algorithm. This one provides fair sharing of the queues when client and simulator throttles are set. This algorithm also uses pre-defined burst rate of 150% of the sustained rate for each of the throttles. Removed the "state" queue. The state queue is not a Linden queue and appeared to be used just to get kill packets sent. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 6 +- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 96 ++++--- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 2 +- .../Region/ClientStack/LindenUDP/TokenBucket.cs | 300 ++++++++++++++------- 4 files changed, 259 insertions(+), 145 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 8de31d7..934a2d5 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -1610,7 +1610,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - OutPacket(kill, ThrottleOutPacketType.State); + // OutPacket(kill, ThrottleOutPacketType.State); + OutPacket(kill, ThrottleOutPacketType.Task); } } @@ -2440,7 +2441,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Effect = effectBlocks; - OutPacket(packet, ThrottleOutPacketType.State); + // OutPacket(packet, ThrottleOutPacketType.State); + OutPacket(packet, ThrottleOutPacketType.Task); } public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 9a8bfd3..5a69851 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -135,7 +135,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_nextOnQueueEmpty = 1; /// Throttle bucket for this agent's connection - private readonly TokenBucket m_throttle; + private readonly TokenBucket 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 @@ -174,7 +176,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_maxRTO = maxRTO; // Create a token bucket throttle for this client that has the scene token bucket as a parent - m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); + m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit); + // Create a token bucket throttle for the total categary with the client bucket as a throttle + m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit); // Create an array of token buckets for this clients different throttle categories m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; @@ -185,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Initialize the packet outboxes, where packets sit while they are waiting for tokens m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); // Initialize the token buckets that control the throttling for each category - m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); + m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetLimit(type)); } // Default the retransmission timeout to three seconds @@ -206,6 +210,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_packetOutboxes[i].Clear(); m_nextPackets[i] = null; } + + // pull the throttle out of the scene throttle + m_throttleClient.Parent.UnregisterRequest(m_throttleClient); OnPacketStats = null; OnQueueEmpty = null; } @@ -216,6 +223,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Information about the client connection public ClientInfo GetClientInfo() { +/// + TokenBucket tb; + + tb = m_throttleClient.Parent; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest,"ROOT"); + + tb = m_throttleClient; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CLIENT"); + + tb = m_throttleCategory; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CATEGORY"); + + for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) + { + tb = m_throttleCategories[i]; + m_log.WarnFormat("[TOKENS] {4} <{0}:{1}>: Actual={2},Requested={3}",AgentID,i,tb.DripRate,tb.RequestedDripRate," BUCKET"); + } + +/// + // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists // of pending and needed ACKs for every client every time some method wants information about // this connection is a recipe for poor performance @@ -223,13 +250,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP info.pendingAcks = new Dictionary(); info.needAck = new Dictionary(); - info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; - info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; - info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; - info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; - info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; - info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; - info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; + info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; + info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; + info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; + info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; + // info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; + info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + info.taskThrottle + info.assetThrottle + info.textureThrottle; @@ -317,8 +345,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); // State is a subcategory of task that we allocate a percentage to - int state = (int)((float)task * STATE_TASK_PERCENTAGE); - task -= state; + int state = 0; + // int state = (int)((float)task * STATE_TASK_PERCENTAGE); + // task -= state; // Make sure none of the throttles are set below our packet MTU, // otherwise a throttle could become permanently clogged @@ -339,40 +368,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Update the token buckets with new throttle values TokenBucket bucket; - bucket = m_throttle; - bucket.MaxBurst = total; + bucket = m_throttleCategory; + bucket.RequestedDripRate = total; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; - bucket.DripRate = resend; - bucket.MaxBurst = resend; + bucket.RequestedDripRate = resend; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; - bucket.DripRate = land; - bucket.MaxBurst = land; + bucket.RequestedDripRate = land; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; - bucket.DripRate = wind; - bucket.MaxBurst = wind; + bucket.RequestedDripRate = wind; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; - bucket.DripRate = cloud; - bucket.MaxBurst = cloud; + bucket.RequestedDripRate = cloud; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; - bucket.DripRate = asset; - bucket.MaxBurst = asset; + bucket.RequestedDripRate = asset; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; - bucket.DripRate = task + state; - bucket.MaxBurst = task + state; + bucket.RequestedDripRate = task; bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; - bucket.DripRate = state; - bucket.MaxBurst = state; + bucket.RequestedDripRate = state; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; - bucket.DripRate = texture; - bucket.MaxBurst = texture; + bucket.RequestedDripRate = texture; // Reset the packed throttles cached data m_packedThrottles = null; @@ -387,14 +408,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP data = new byte[7 * 4]; int i = 0; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + - m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate), 0, data, i, 4); i += 4; m_packedThrottles = data; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 583214c..d08b25f 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -228,7 +228,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } #endregion BinaryStats - m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); + m_throttle = new TokenBucket(null, sceneThrottleBps); ThrottleRates = new ThrottleRates(configSource); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 0a8331f..e4d59ff 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -26,6 +26,10 @@ */ using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using log4net; namespace OpenSim.Region.ClientStack.LindenUDP { @@ -35,89 +39,126 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// 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; + 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. + /// + private const Int32 m_ticksPerQuantum = 1000; + + /// + /// This is the number of quantums worth of packets that can + /// be accommodated during a burst + /// + private const Double m_quantumsPerBurst = 1.5; + + /// + /// + private const Int32 m_minimumDripRate = 1400; + /// Time of the last drip, in system ticks - int lastDrip; + private Int32 m_lastDrip; + + /// + /// The number of bytes that can be sent at this moment. This is the + /// current number of tokens in the bucket + /// + private Int64 m_tokenCount; - #region Properties + /// + /// Map of children buckets and their requested maximum burst rate + /// + private 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 /// + private TokenBucket m_parent; public TokenBucket Parent { - get { return parent; } + get { return m_parent; } + set { m_parent = value; } } /// /// Maximum burst rate in bytes per second. This is the maximum number - /// of tokens that can accumulate in the bucket at any one time + /// of tokens that can accumulate in the bucket at any one time. This + /// also sets the total request for leaf nodes /// - public int MaxBurst + private Int64 m_burstRate; + public Int64 RequestedBurstRate { - get { return maxBurst; } - set { maxBurst = (value >= 0 ? value : 0); } + get { return m_burstRate; } + set { m_burstRate = (value < 0 ? 0 : value); } } + public Int64 BurstRate + { + get { + double rate = RequestedBurstRate * BurstRateModifier(); + if (rate < m_minimumDripRate * m_quantumsPerBurst) + rate = m_minimumDripRate * m_quantumsPerBurst; + + return (Int64) rate; + } + } + /// /// The speed limit of this bucket in bytes per second. This is the - /// number of tokens that are added to the bucket per second + /// number of tokens that are added to the bucket per quantum /// /// 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 + private Int64 m_dripRate; + public Int64 RequestedDripRate { - 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; - } + get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } + set { + m_dripRate = (value < 0 ? 0 : value); + m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); + m_totalDripRequest = m_dripRate; + if (m_parent != null) + m_parent.RegisterRequest(this,m_dripRate); } } - /// - /// The speed limit of this bucket in bytes per millisecond - /// - public int DripPerMS + public Int64 DripRate { - get { return tokensPerMS; } + get { + if (m_parent == null) + return Math.Min(RequestedDripRate,TotalDripRequest); + + double rate = (double)RequestedDripRate * m_parent.DripRateModifier(); + if (rate < m_minimumDripRate) + rate = m_minimumDripRate; + + return (Int64)rate; + } } /// - /// 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 + /// The current total of the requested maximum burst rates of + /// this bucket's children buckets. /// - public int Content - { - get { return content; } - } + private Int64 m_totalDripRequest; + public Int64 TotalDripRequest + { + get { return m_totalDripRequest; } + set { m_totalDripRequest = value; } + } + +#endregion Properties - #endregion Properties +#region Constructor /// /// Default constructor @@ -128,56 +169,115 @@ 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, int maxBurst, int dripRate) + public TokenBucket(TokenBucket parent, Int64 dripRate) { - this.parent = parent; - MaxBurst = maxBurst; - DripRate = dripRate; - lastDrip = Environment.TickCount & Int32.MaxValue; + m_identifier = m_counter++; + + Parent = parent; + RequestedDripRate = dripRate; + // TotalDripRequest = dripRate; // this will be overwritten when a child node registers + // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); + m_lastDrip = Environment.TickCount & Int32.MaxValue; } +#endregion Constructor + /// - /// Remove a given number of tokens from the bucket + /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning + /// no modification if the requested bandwidth is less than the + /// max burst bandwidth all the way to the root of the throttle + /// hierarchy. However, if any of the parents is over-booked, then + /// the modifier will be less than 1. /// - /// 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) + private double DripRateModifier() + { + Int64 driprate = DripRate; + return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; + } + + /// + /// + private double BurstRateModifier() + { + // for now... burst rate is always m_quantumsPerBurst (constant) + // larger than drip rate so the ratio of burst requests is the + // same as the drip ratio + return DripRateModifier(); + } + + /// + /// Register drip rate requested by a child of this throttle. Pass the + /// changes up the hierarchy. + /// + public void RegisterRequest(TokenBucket child, Int64 request) { - bool dummy; - return RemoveTokens(amount, out dummy); + m_children[child] = request; + // m_totalDripRequest = m_children.Values.Sum(); + + m_totalDripRequest = 0; + foreach (KeyValuePair cref in m_children) + m_totalDripRequest += cref.Value; + + // Pass the new values up to the parent + if (m_parent != null) + m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); } /// + /// Remove the rate requested by a child of this throttle. Pass the + /// changes up the hierarchy. + /// + public void UnregisterRequest(TokenBucket child) + { + m_children.Remove(child); + // m_totalDripRequest = m_children.Values.Sum(); + + m_totalDripRequest = 0; + foreach (KeyValuePair cref in m_children) + m_totalDripRequest += cref.Value; + + // Pass the new values up to the parent + if (m_parent != null) + m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); + } + + /// /// 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) + public bool RemoveTokens(Int64 amount) { - if (maxBurst == 0) + // Deposit tokens for this interval + Drip(); + + // If we have enough tokens then remove them and return + if (m_tokenCount - amount >= 0) { - dripSucceeded = true; - return true; + if (m_parent == null || m_parent.RemoveTokens(amount)) + { + m_tokenCount -= amount; + return true; + } } - dripSucceeded = Drip(); + return false; + } - if (content - amount >= 0) - { - if (parent != null && !parent.RemoveTokens(amount)) - return false; + /// + /// Deposit tokens into the bucket from a child bucket that did + /// not use all of its available tokens + /// + private void Deposit(Int64 count) + { + m_tokenCount += count; - content -= amount; - return true; - } - else - { - return false; - } + // Deposit the overflow in the parent bucket, this is how we share + // unused bandwidth + Int64 burstrate = BurstRate; + if (m_tokenCount > burstrate) + m_tokenCount = burstrate; } /// @@ -186,37 +286,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// call to Drip /// /// True if tokens were added to the bucket, otherwise false - public bool Drip() + private void Drip() { - if (tokensPerMS == 0) + // This should never happen... means we are a leaf node and were created + // with no drip rate... + if (DripRate == 0) { - content = maxBurst; - return true; + m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0"); + return; } - else - { - int now = Environment.TickCount & Int32.MaxValue; - int deltaMS = now - lastDrip; - - if (deltaMS <= 0) - { - if (deltaMS < 0) - lastDrip = now; - return false; - } + + // Determine the interval over which we are adding tokens, never add + // more than a single quantum of tokens + Int32 now = Environment.TickCount & Int32.MaxValue; + Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum); - int dripAmount = deltaMS * tokensPerMS; + m_lastDrip = now; - content = Math.Min(content + dripAmount, maxBurst); - lastDrip = now; + // This can be 0 in the very unusual case that the timer wrapped + // It can be 0 if we try add tokens at a sub-tick rate + if (deltaMS <= 0) + return; - if (dripAmount < 0 || content < 0) - // sim has been idle for too long, integer has overflown - // previous calculation is meaningless, let's put it at correct max - content = maxBurst; - - return true; - } + Deposit(deltaMS * DripRate / m_ticksPerQuantum); } } } -- cgit v1.1 From 095e602c4ac6da315f3f6711e24f034b6c490a02 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 12:36:36 -0700 Subject: First pass at moving object property requests into a queue similar to the entity update queue. The number of property packets can become significant when selecting/deselecting large numbers of objects. This is experimental code. --- OpenSim/Client/MXP/ClientStack/MXPClientView.cs | 4 +- .../Client/VWoHTTP/ClientStack/VWHClientView.cs | 4 +- OpenSim/Framework/IClientAPI.cs | 39 +-- .../Region/ClientStack/LindenUDP/LLClientView.cs | 351 +++++++++++++-------- .../Region/ClientStack/LindenUDP/PriorityQueue.cs | 12 +- .../Region/Examples/SimpleModule/MyNpcCharacter.cs | 12 +- .../Region/Framework/Scenes/SceneObjectGroup.cs | 10 +- OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 10 +- .../Server/IRCClientView.cs | 5 +- .../Region/OptionalModules/World/NPC/NPCAvatar.cs | 12 +- OpenSim/Tests/Common/Mock/TestClient.cs | 11 +- 11 files changed, 262 insertions(+), 208 deletions(-) diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index d1a0440..a604a2e 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -1337,12 +1337,12 @@ namespace OpenSim.Client.MXP.ClientStack // Need to translate to MXP somehow } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { //throw new System.NotImplementedException(); } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { //throw new System.NotImplementedException(); } diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index fc27f01..d8cd0ac 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -884,12 +884,12 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { throw new System.NotImplementedException(); } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { throw new System.NotImplementedException(); } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 5bf0b7b..c56a756 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -570,16 +570,30 @@ namespace OpenSim.Framework public float dwell; } - public class EntityUpdate + public class IEntityUpdate { public ISceneEntity Entity; - public PrimUpdateFlags Flags; - public float TimeDilation; + public uint Flags; - public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) + public IEntityUpdate(ISceneEntity entity, uint flags) { Entity = entity; Flags = flags; + } + } + + + public class EntityUpdate : IEntityUpdate + { + // public ISceneEntity Entity; + // public PrimUpdateFlags Flags; + public float TimeDilation; + + public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) + : base(entity,(uint)flags) + { + //Entity = entity; + // Flags = flags; TimeDilation = timedilation; } } @@ -1211,20 +1225,9 @@ namespace OpenSim.Framework /// void SendSimStats(SimStats stats); - void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, - uint Category, - UUID LastOwnerID, string ObjectName, string Description); - - void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, - UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, - string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, - uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice); + void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags); + + void SendObjectPropertiesReply(ISceneEntity Entity); void SendAgentOffline(UUID[] agentIDs); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 934a2d5..b96343e 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -386,6 +386,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_cachedTextureSerial; private PriorityQueue m_entityUpdates; + private PriorityQueue m_entityProps; private Prioritizer m_prioritizer; private bool m_disableFacelights = false; @@ -433,9 +434,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected IAssetService m_assetService; private const bool m_checkPackets = true; - private Timer m_propertiesPacketTimer; - private List m_propertiesBlocks = new List(); - #endregion Class Members #region Properties @@ -511,6 +509,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene = scene; m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); + m_entityProps = new PriorityQueue(m_scene.Entities.Count); m_fullUpdateDataBlocksBuilder = new List(); m_killRecord = new HashSet(); // m_attachmentsSent = new HashSet(); @@ -534,9 +533,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_udpClient.OnQueueEmpty += HandleQueueEmpty; m_udpClient.OnPacketStats += PopulateStats; - m_propertiesPacketTimer = new Timer(100); - m_propertiesPacketTimer.Elapsed += ProcessObjectPropertiesPacket; - m_prioritizer = new Prioritizer(m_scene); RegisterLocalPacketHandlers(); @@ -3636,9 +3632,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); } - private Int32 m_LastQueueFill = 0; - private uint m_maxUpdates = 0; - private void ProcessEntityUpdates(int maxUpdates) { OpenSim.Framework.Lazy> objectUpdateBlocks = new OpenSim.Framework.Lazy>(); @@ -3646,46 +3639,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.Lazy> terseUpdateBlocks = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy>(); + // Check to see if this is a flush if (maxUpdates <= 0) { - m_maxUpdates = Int32.MaxValue; + maxUpdates = Int32.MaxValue; } - else - { - if (m_maxUpdates == 0 || m_LastQueueFill == 0) - { - m_maxUpdates = (uint)maxUpdates; - } - else - { - if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) - m_maxUpdates += 5; - else - m_maxUpdates = m_maxUpdates >> 1; - } - m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); - } - m_LastQueueFill = Util.EnvironmentTickCount(); - + int updatesThisCall = 0; -// -// DEBUGGING CODE... REMOVE -// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates); -// // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race // condition where a kill can be processed before an out-of-date update for the same object. lock (m_killRecord) { float avgTimeDilation = 1.0f; - EntityUpdate update; + IEntityUpdate iupdate; Int32 timeinqueue; // this is just debugging code & can be dropped later - while (updatesThisCall < m_maxUpdates) + while (updatesThisCall < maxUpdates) { lock (m_entityUpdates.SyncRoot) - if (!m_entityUpdates.TryDequeue(out update, out timeinqueue)) + if (!m_entityUpdates.TryDequeue(out iupdate, out timeinqueue)) break; + + EntityUpdate update = (EntityUpdate)iupdate; + avgTimeDilation += update.TimeDilation; avgTimeDilation *= 0.5f; @@ -3725,7 +3702,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region UpdateFlags to packet type conversion - PrimUpdateFlags updateFlags = update.Flags; + PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags; bool canUseCompressed = true; bool canUseImproved = true; @@ -3804,6 +3781,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Block Construction } + #region Packet Sending @@ -3904,12 +3882,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Primitive Packet/Data Sending Methods + // These are used to implement an adaptive backoff in the number + // of updates converted to packets. Since we don't want packets + // to sit in the queue with old data, only convert enough updates + // to packets that can be sent in 200ms. + private Int32 m_LastQueueFill = 0; + private Int32 m_maxUpdates = 0; + void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) { if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) { + if (m_maxUpdates == 0 || m_LastQueueFill == 0) + { + m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; + } + else + { + if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) + m_maxUpdates += 5; + else + m_maxUpdates = m_maxUpdates >> 1; + } + m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); + m_LastQueueFill = Util.EnvironmentTickCount(); + if (m_entityUpdates.Count > 0) - ProcessEntityUpdates(m_udpServer.PrimUpdatesPerCallback); + ProcessEntityUpdates(m_maxUpdates); + + if (m_entityProps.Count > 0) + ProcessEntityPropertyRequests(m_maxUpdates); } if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) @@ -4023,134 +4025,192 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(pack, ThrottleOutPacketType.Task); } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) - { - ObjectPropertiesFamilyPacket objPropFamilyPack = (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); - // TODO: don't create new blocks if recycling an old packet - - ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); - objPropDB.RequestFlags = RequestFlags; - objPropDB.ObjectID = ObjectUUID; - if (OwnerID == GroupID) - objPropDB.OwnerID = UUID.Zero; - else - objPropDB.OwnerID = OwnerID; - objPropDB.GroupID = GroupID; - objPropDB.BaseMask = BaseMask; - objPropDB.OwnerMask = OwnerMask; - objPropDB.GroupMask = GroupMask; - objPropDB.EveryoneMask = EveryoneMask; - objPropDB.NextOwnerMask = NextOwnerMask; +/// ----------------------------------------------------------------- +/// +/// ----------------------------------------------------------------- - // TODO: More properties are needed in SceneObjectPart! - objPropDB.OwnershipCost = OwnershipCost; - objPropDB.SaleType = SaleType; - objPropDB.SalePrice = SalePrice; - objPropDB.Category = Category; - objPropDB.LastOwnerID = LastOwnerID; - objPropDB.Name = Util.StringToBytes256(ObjectName); - objPropDB.Description = Util.StringToBytes256(Description); - objPropFamilyPack.ObjectData = objPropDB; - objPropFamilyPack.Header.Zerocoded = true; - OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task); - } - - public void SendObjectPropertiesReply( - UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + private class ObjectPropertyUpdate : IEntityUpdate { - //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); - // TODO: don't create new blocks if recycling an old packet + internal bool SendFamilyProps; + + public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam) + : base(entity,flags) + { + SendFamilyProps = sendfam; + } + } + + public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags) + { + uint priority = m_prioritizer.GetUpdatePriority(this, entity); + lock (m_entityProps.SyncRoot) + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true)); + } - ObjectPropertiesPacket.ObjectDataBlock block = - new ObjectPropertiesPacket.ObjectDataBlock(); + public void SendObjectPropertiesReply(ISceneEntity entity) + { + uint priority = m_prioritizer.GetUpdatePriority(this, entity); + lock (m_entityProps.SyncRoot) + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false)); + } - block.ItemID = ItemID; - block.CreationDate = CreationDate; - block.CreatorID = CreatorUUID; - block.FolderID = FolderUUID; - block.FromTaskID = FromTaskUUID; - block.GroupID = GroupUUID; - block.InventorySerial = InventorySerial; + private void ProcessEntityPropertyRequests(int maxUpdates) + { + OpenSim.Framework.Lazy> objectFamilyBlocks = + new OpenSim.Framework.Lazy>(); - block.LastOwnerID = LastOwnerUUID; - // proper.ObjectData[0].LastOwnerID = UUID.Zero; + OpenSim.Framework.Lazy> objectPropertiesBlocks = + new OpenSim.Framework.Lazy>(); - block.ObjectID = ObjectUUID; - if (OwnerUUID == GroupUUID) - block.OwnerID = UUID.Zero; - else - block.OwnerID = OwnerUUID; - block.TouchName = Util.StringToBytes256(TouchTitle); - block.TextureID = TextureID; - block.SitName = Util.StringToBytes256(SitTitle); - block.Name = Util.StringToBytes256(ItemName); - block.Description = Util.StringToBytes256(ItemDescription); - block.OwnerMask = OwnerMask; - block.NextOwnerMask = NextOwnerMask; - block.GroupMask = GroupMask; - block.EveryoneMask = EveryoneMask; - block.BaseMask = BaseMask; - // proper.ObjectData[0].AggregatePerms = 53; - // proper.ObjectData[0].AggregatePermTextures = 0; - // proper.ObjectData[0].AggregatePermTexturesOwner = 0; - block.SaleType = saleType; - block.SalePrice = salePrice; + IEntityUpdate iupdate; + Int32 timeinqueue; // this is just debugging code & can be dropped later - lock (m_propertiesPacketTimer) + int updatesThisCall = 0; + while (updatesThisCall < m_maxUpdates) { - m_propertiesBlocks.Add(block); + lock (m_entityProps.SyncRoot) + if (!m_entityProps.TryDequeue(out iupdate, out timeinqueue)) + break; - int length = 0; - foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) + ObjectPropertyUpdate update = (ObjectPropertyUpdate)iupdate; + if (update.SendFamilyProps) { - length += b.Length; + if (update.Entity is SceneObjectPart) + { + SceneObjectPart sop = (SceneObjectPart)update.Entity; + ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); + objectFamilyBlocks.Value.Add(objPropDB); + } } - if (length > 1100) // FIXME: use real MTU + else { - ProcessObjectPropertiesPacket(null, null); - m_propertiesPacketTimer.Stop(); - return; + if (update.Entity is SceneObjectPart) + { + SceneObjectPart sop = (SceneObjectPart)update.Entity; + ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); + objectPropertiesBlocks.Value.Add(objPropDB); + } } + } + + + if (objectPropertiesBlocks.IsValueCreated) + { + List blocks = objectPropertiesBlocks.Value; - m_propertiesPacketTimer.Stop(); - m_propertiesPacketTimer.Start(); + ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); + packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count]; + for (int i = 0; i < blocks.Count; i++) + packet.ObjectData[i] = blocks[i]; + + packet.Header.Zerocoded = true; + OutPacket(packet, ThrottleOutPacketType.Task, true); } + + + if (objectFamilyBlocks.IsValueCreated) + { + List blocks = objectFamilyBlocks.Value; + + // ObjectPropertiesFamilyPacket objPropFamilyPack = + // (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); + // + // objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count]; + // for (int i = 0; i < blocks.Count; i++) + // objPropFamilyPack.ObjectData[i] = blocks[i]; + // + // OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true); + + // one packet per object block... uggh... + for (int i = 0; i < blocks.Count; i++) + { + ObjectPropertiesFamilyPacket packet = + (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); - //proper.Header.Zerocoded = true; - //OutPacket(proper, ThrottleOutPacketType.Task); + packet.ObjectData = blocks[i]; + packet.Header.Zerocoded = true; + OutPacket(packet, ThrottleOutPacketType.Task); + } + + } + } - private void ProcessObjectPropertiesPacket(Object sender, ElapsedEventArgs e) + private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) { - ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); + ObjectPropertiesFamilyPacket.ObjectDataBlock block = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); - lock (m_propertiesPacketTimer) - { - m_propertiesPacketTimer.Stop(); + block.RequestFlags = requestFlags; + block.ObjectID = sop.UUID; + if (sop.OwnerID == sop.GroupID) + block.OwnerID = UUID.Zero; + else + block.OwnerID = sop.OwnerID; + block.GroupID = sop.GroupID; + block.BaseMask = sop.BaseMask; + block.OwnerMask = sop.OwnerMask; + block.GroupMask = sop.GroupMask; + block.EveryoneMask = sop.EveryoneMask; + block.NextOwnerMask = sop.NextOwnerMask; - proper.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[m_propertiesBlocks.Count]; + // TODO: More properties are needed in SceneObjectPart! + block.OwnershipCost = sop.OwnershipCost; + block.SaleType = sop.ObjectSaleType; + block.SalePrice = sop.SalePrice; + block.Category = sop.Category; + block.LastOwnerID = sop.CreatorID; // copied from old SOG call... is this right? + block.Name = Util.StringToBytes256(sop.Name); + block.Description = Util.StringToBytes256(sop.Description); - int index = 0; + return block; + } - foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) - { - proper.ObjectData[index++] = b; - } + private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop) + { + //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); + // TODO: don't create new blocks if recycling an old packet - m_propertiesBlocks.Clear(); - } + ObjectPropertiesPacket.ObjectDataBlock block = + new ObjectPropertiesPacket.ObjectDataBlock(); + + block.ObjectID = sop.UUID; + block.Name = Util.StringToBytes256(sop.Name); + block.Description = Util.StringToBytes256(sop.Description); - proper.Header.Zerocoded = true; - OutPacket(proper, ThrottleOutPacketType.Task); + block.CreationDate = (ulong)sop.CreationDate * 1000000; // viewer wants date in microseconds + block.CreatorID = sop.CreatorID; + block.GroupID = sop.GroupID; + block.LastOwnerID = sop.LastOwnerID; + if (sop.OwnerID == sop.GroupID) + block.OwnerID = UUID.Zero; + else + block.OwnerID = sop.OwnerID; + + block.ItemID = sop.FromUserInventoryItemID; + block.FolderID = UUID.Zero; // sop.FromFolderID ?? + block.FromTaskID = UUID.Zero; // ??? + block.InventorySerial = (short)sop.InventorySerial; + + SceneObjectPart root = sop.ParentGroup.RootPart; + + block.TouchName = Util.StringToBytes256(root.TouchName); + block.TextureID = new byte[0]; // TextureID ??? + block.SitName = Util.StringToBytes256(root.SitName); + block.OwnerMask = root.OwnerMask; + block.NextOwnerMask = root.NextOwnerMask; + block.GroupMask = root.GroupMask; + block.EveryoneMask = root.EveryoneMask; + block.BaseMask = root.BaseMask; + block.SaleType = root.ObjectSaleType; + block.SalePrice = root.SalePrice; + + return block; } +/// ----------------------------------------------------------------- +/// +/// ----------------------------------------------------------------- + #region Estate Data Sending Methods private static bool convertParamStringToBool(byte[] field) @@ -4482,6 +4542,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendForceClientSelectObjects(List ObjectIDs) { + m_log.WarnFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count); + bool firstCall = true; const int MAX_OBJECTS_PER_PACKET = 251; ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect); @@ -11374,6 +11436,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); } + if (throttlePacketType == ThrottleOutPacketType.Task) + { + System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); // get call stack + System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) + + string stack = ""; + for (int count = 1; count < stackFrames.Length; count++) + { + stack += (stack == "" ? "" : ",") + stackFrames[count].GetMethod().Name; + if (count > 5) break; + } + + // m_log.WarnFormat("[BADGUY] {0}", stack); + } + m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs index 364ce4b..6521a00 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs @@ -78,7 +78,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public bool Enqueue(uint pqueue, EntityUpdate value) + public bool Enqueue(uint pqueue, IEntityUpdate value) { LookupItem lookup; @@ -99,7 +99,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return true; } - internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) + internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue) { for (int i = 0; i < m_numberOfQueues; ++i) { @@ -122,7 +122,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } timeinqueue = 0; - value = default(EntityUpdate); + value = default(IEntityUpdate); return false; } @@ -175,8 +175,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region MinHeapItem private struct MinHeapItem : IComparable { - private EntityUpdate value; - internal EntityUpdate Value { + private IEntityUpdate value; + internal IEntityUpdate Value { get { return this.value; } @@ -212,7 +212,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP this.pqueue = pqueue; } - internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) + internal MinHeapItem(uint pqueue, UInt64 entryorder, IEntityUpdate value) { this.entrytime = Util.EnvironmentTickCount(); this.entryorder = entryorder; diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index d939329..89e9e20 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -702,18 +702,12 @@ namespace OpenSim.Region.Examples.SimpleModule { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { + } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index ca7d9d9..30563d4 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -1765,10 +1765,12 @@ namespace OpenSim.Region.Framework.Scenes /// public void ServiceObjectPropertiesFamilyRequest(IClientAPI remoteClient, UUID AgentID, uint RequestFlags) { - remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask, - RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask, - RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, - RootPart.CreatorID, RootPart.Name, RootPart.Description); + remoteClient.SendObjectPropertiesFamilyData(RootPart, RequestFlags); + +// remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask, +// RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask, +// RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, +// RootPart.CreatorID, RootPart.Name, RootPart.Description); } public void SetPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 4d5eedf..3d2eacd 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2055,15 +2055,7 @@ namespace OpenSim.Region.Framework.Scenes public void GetProperties(IClientAPI client) { - //Viewer wants date in microseconds so multiply it by 1,000,000. - client.SendObjectPropertiesReply( - m_fromUserInventoryItemID, (ulong)_creationDate*(ulong)1e6, _creatorID, UUID.Zero, UUID.Zero, - _groupID, (short)InventorySerial, _lastOwnerID, UUID, _ownerID, - ParentGroup.RootPart.TouchName, new byte[0], ParentGroup.RootPart.SitName, Name, Description, - ParentGroup.RootPart._ownerMask, ParentGroup.RootPart._nextOwnerMask, ParentGroup.RootPart._groupMask, ParentGroup.RootPart._everyoneMask, - ParentGroup.RootPart._baseMask, - ParentGroup.RootPart.ObjectSaleType, - ParentGroup.RootPart.SalePrice); + client.SendObjectPropertiesReply(this); } public UUID GetRootPartUUID() diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 821cd4b..4b6e52e 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -1332,14 +1332,13 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { - } public void SendAgentOffline(UUID[] agentIDs) diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 96760a2..2504e30 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -786,18 +786,12 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { + } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index d1dc17f..dca5626 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -816,18 +816,11 @@ namespace OpenSim.Tests.Common.Mock { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } -- cgit v1.1 From 7fa085b3fa8eacbfec4b9f8969bcbcc210ee50f0 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 14:40:57 -0700 Subject: fixed a couple bugs with the property queues --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 38 +++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index b96343e..4db5b22 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4091,9 +4091,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP objectPropertiesBlocks.Value.Add(objPropDB); } } + + updatesThisCall++; } + Int32 ppcnt = 0; + Int32 pbcnt = 0; + if (objectPropertiesBlocks.IsValueCreated) { List blocks = objectPropertiesBlocks.Value; @@ -4105,9 +4110,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Header.Zerocoded = true; OutPacket(packet, ThrottleOutPacketType.Task, true); + + pbcnt += blocks.Count; + ppcnt++; } + Int32 fpcnt = 0; + Int32 fbcnt = 0; + if (objectFamilyBlocks.IsValueCreated) { List blocks = objectFamilyBlocks.Value; @@ -4130,10 +4141,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.ObjectData = blocks[i]; packet.Header.Zerocoded = true; OutPacket(packet, ThrottleOutPacketType.Task); + + fpcnt++; + fbcnt++; } } + m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); + m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); } private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) @@ -11401,6 +11417,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, throttlePacketType, true); } +/// + Dictionary pktsrc = new Dictionary(); + uint pktcnt = 0; +/// + /// /// This is the starting point for sending a simulator packet out to the client /// @@ -11448,7 +11469,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (count > 5) break; } - // m_log.WarnFormat("[BADGUY] {0}", stack); + lock(pktsrc) + { + if (! pktsrc.ContainsKey(stack)) + pktsrc.Add(stack,0); + pktsrc[stack]++; + + if (++pktcnt > 500) + { + pktcnt = 0; + m_log.WarnFormat("[PACKETCOUNTS] START"); + foreach (KeyValuePair pkt in pktsrc) + m_log.WarnFormat("[PACKETCOUNTS] {0,8}, {1}", pkt.Value, pkt.Key); + pktsrc.Clear(); + m_log.WarnFormat("[PACKETCOUNTS] END"); + } + } } m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); -- cgit v1.1 From 80ba3de90240d190bad1bd15ff89dfdb390c5e24 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 15:40:57 -0700 Subject: Fixed the update of items in the priority queue to enable both types of property updates to be specified. Not sure if one form of property update should supercede another. But for now the old OpenSim behavior is preserved by sending both. --- OpenSim/Framework/IClientAPI.cs | 5 +++++ .../Region/ClientStack/LindenUDP/LLClientView.cs | 21 +++++++++++++++------ .../Region/ClientStack/LindenUDP/PriorityQueue.cs | 2 +- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index c56a756..f573c32 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -575,6 +575,11 @@ namespace OpenSim.Framework public ISceneEntity Entity; public uint Flags; + public virtual void Update(IEntityUpdate update) + { + this.Flags |= update.Flags; + } + public IEntityUpdate(ISceneEntity entity, uint flags) { Entity = entity; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 4db5b22..92a76ac 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4032,26 +4032,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP private class ObjectPropertyUpdate : IEntityUpdate { internal bool SendFamilyProps; + internal bool SendObjectProps; - public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam) + public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam, bool sendobj) : base(entity,flags) { SendFamilyProps = sendfam; + SendObjectProps = sendobj; + } + public void Update(ObjectPropertyUpdate update) + { + SendFamilyProps = SendFamilyProps || update.SendFamilyProps; + SendObjectProps = SendObjectProps || update.SendObjectProps; + Flags |= update.Flags; } } public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags) { - uint priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = 0; // time based ordering only lock (m_entityProps.SyncRoot) - m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true)); + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false)); } public void SendObjectPropertiesReply(ISceneEntity entity) { - uint priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = 0; // time based ordering only lock (m_entityProps.SyncRoot) - m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false)); + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false,true)); } private void ProcessEntityPropertyRequests(int maxUpdates) @@ -4082,7 +4090,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP objectFamilyBlocks.Value.Add(objPropDB); } } - else + + if (update.SendObjectProps) { if (update.Entity is SceneObjectPart) { diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs index 6521a00..b62ec07 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs @@ -87,7 +87,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_lookupTable.TryGetValue(localid, out lookup)) { entry = lookup.Heap[lookup.Handle].EntryOrder; - value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; + value.Update(lookup.Heap[lookup.Handle].Value); lookup.Heap.Remove(lookup.Handle); } -- cgit v1.1 From 317617cfda5356e9d375d00c93cd0d23a489107e Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 15:55:21 -0700 Subject: remove packet monitoring debugging code --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 118 +-------------------- 1 file changed, 2 insertions(+), 116 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 92a76ac..cf04f0d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -300,77 +300,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Used to adjust Sun Orbit values so Linden based viewers properly position sun private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; - // First log file or time has expired, start writing to a new log file -// -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// THIS IS DEBUGGING CODE & SHOULD BE REMOVED -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- - public class QueueLogger - { - public Int32 start = 0; - public StreamWriter Log = null; - private Dictionary m_idMap = new Dictionary(); - - public QueueLogger() - { - DateTime now = DateTime.Now; - String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss")); - Log = new StreamWriter(fname); - - start = Util.EnvironmentTickCount(); - } - - public int LookupID(UUID uuid) - { - int localid; - if (! m_idMap.TryGetValue(uuid,out localid)) - { - localid = m_idMap.Count + 1; - m_idMap[uuid] = localid; - } - - return localid; - } - } - - public static QueueLogger QueueLog = null; - - // ----------------------------------------------------------------- - public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - int aid = QueueLog.LookupID(avatar); - QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue); - } - } - - // ----------------------------------------------------------------- - public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString()); - } - } -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected static Dictionary PacketHandlers = new Dictionary(); //Global/static handlers for all clients @@ -4025,10 +3954,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(pack, ThrottleOutPacketType.Task); } -/// ----------------------------------------------------------------- -/// -/// ----------------------------------------------------------------- - private class ObjectPropertyUpdate : IEntityUpdate { internal bool SendFamilyProps; @@ -4157,8 +4082,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } - m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); - m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); + // m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); + // m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); } private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) @@ -4232,10 +4157,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return block; } -/// ----------------------------------------------------------------- -/// -/// ----------------------------------------------------------------- - #region Estate Data Sending Methods private static bool convertParamStringToBool(byte[] field) @@ -11426,11 +11347,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, throttlePacketType, true); } -/// - Dictionary pktsrc = new Dictionary(); - uint pktcnt = 0; -/// - /// /// This is the starting point for sending a simulator packet out to the client /// @@ -11465,36 +11381,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (logPacket) m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); } - - if (throttlePacketType == ThrottleOutPacketType.Task) - { - System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); // get call stack - System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) - - string stack = ""; - for (int count = 1; count < stackFrames.Length; count++) - { - stack += (stack == "" ? "" : ",") + stackFrames[count].GetMethod().Name; - if (count > 5) break; - } - - lock(pktsrc) - { - if (! pktsrc.ContainsKey(stack)) - pktsrc.Add(stack,0); - pktsrc[stack]++; - - if (++pktcnt > 500) - { - pktcnt = 0; - m_log.WarnFormat("[PACKETCOUNTS] START"); - foreach (KeyValuePair pkt in pktsrc) - m_log.WarnFormat("[PACKETCOUNTS] {0,8}, {1}", pkt.Value, pkt.Key); - pktsrc.Clear(); - m_log.WarnFormat("[PACKETCOUNTS] END"); - } - } - } m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); } -- cgit v1.1 From 2f436875899f432a88f432ab86a6858b3a4cc373 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 11 Apr 2011 09:06:28 -0700 Subject: New tokenbucket algorithm. This one provides fair sharing of the queues when client and simulator throttles are set. This algorithm also uses pre-defined burst rate of 150% of the sustained rate for each of the throttles. Removed the "state" queue. The state queue is not a Linden queue and appeared to be used just to get kill packets sent. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 6 +- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 96 ++++--- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 2 +- .../Region/ClientStack/LindenUDP/TokenBucket.cs | 300 ++++++++++++++------- 4 files changed, 259 insertions(+), 145 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 8de31d7..934a2d5 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -1610,7 +1610,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - OutPacket(kill, ThrottleOutPacketType.State); + // OutPacket(kill, ThrottleOutPacketType.State); + OutPacket(kill, ThrottleOutPacketType.Task); } } @@ -2440,7 +2441,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Effect = effectBlocks; - OutPacket(packet, ThrottleOutPacketType.State); + // OutPacket(packet, ThrottleOutPacketType.State); + OutPacket(packet, ThrottleOutPacketType.Task); } public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 9a8bfd3..5a69851 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -135,7 +135,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_nextOnQueueEmpty = 1; /// Throttle bucket for this agent's connection - private readonly TokenBucket m_throttle; + private readonly TokenBucket 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 @@ -174,7 +176,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_maxRTO = maxRTO; // Create a token bucket throttle for this client that has the scene token bucket as a parent - m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); + m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit); + // Create a token bucket throttle for the total categary with the client bucket as a throttle + m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit); // Create an array of token buckets for this clients different throttle categories m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; @@ -185,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Initialize the packet outboxes, where packets sit while they are waiting for tokens m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); // Initialize the token buckets that control the throttling for each category - m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); + m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetLimit(type)); } // Default the retransmission timeout to three seconds @@ -206,6 +210,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_packetOutboxes[i].Clear(); m_nextPackets[i] = null; } + + // pull the throttle out of the scene throttle + m_throttleClient.Parent.UnregisterRequest(m_throttleClient); OnPacketStats = null; OnQueueEmpty = null; } @@ -216,6 +223,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Information about the client connection public ClientInfo GetClientInfo() { +/// + TokenBucket tb; + + tb = m_throttleClient.Parent; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest,"ROOT"); + + tb = m_throttleClient; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CLIENT"); + + tb = m_throttleCategory; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CATEGORY"); + + for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) + { + tb = m_throttleCategories[i]; + m_log.WarnFormat("[TOKENS] {4} <{0}:{1}>: Actual={2},Requested={3}",AgentID,i,tb.DripRate,tb.RequestedDripRate," BUCKET"); + } + +/// + // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists // of pending and needed ACKs for every client every time some method wants information about // this connection is a recipe for poor performance @@ -223,13 +250,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP info.pendingAcks = new Dictionary(); info.needAck = new Dictionary(); - info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; - info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; - info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; - info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; - info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; - info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; - info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; + info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; + info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; + info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; + info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; + // info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; + info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + info.taskThrottle + info.assetThrottle + info.textureThrottle; @@ -317,8 +345,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); // State is a subcategory of task that we allocate a percentage to - int state = (int)((float)task * STATE_TASK_PERCENTAGE); - task -= state; + int state = 0; + // int state = (int)((float)task * STATE_TASK_PERCENTAGE); + // task -= state; // Make sure none of the throttles are set below our packet MTU, // otherwise a throttle could become permanently clogged @@ -339,40 +368,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Update the token buckets with new throttle values TokenBucket bucket; - bucket = m_throttle; - bucket.MaxBurst = total; + bucket = m_throttleCategory; + bucket.RequestedDripRate = total; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; - bucket.DripRate = resend; - bucket.MaxBurst = resend; + bucket.RequestedDripRate = resend; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; - bucket.DripRate = land; - bucket.MaxBurst = land; + bucket.RequestedDripRate = land; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; - bucket.DripRate = wind; - bucket.MaxBurst = wind; + bucket.RequestedDripRate = wind; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; - bucket.DripRate = cloud; - bucket.MaxBurst = cloud; + bucket.RequestedDripRate = cloud; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; - bucket.DripRate = asset; - bucket.MaxBurst = asset; + bucket.RequestedDripRate = asset; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; - bucket.DripRate = task + state; - bucket.MaxBurst = task + state; + bucket.RequestedDripRate = task; bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; - bucket.DripRate = state; - bucket.MaxBurst = state; + bucket.RequestedDripRate = state; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; - bucket.DripRate = texture; - bucket.MaxBurst = texture; + bucket.RequestedDripRate = texture; // Reset the packed throttles cached data m_packedThrottles = null; @@ -387,14 +408,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP data = new byte[7 * 4]; int i = 0; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + - m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate), 0, data, i, 4); i += 4; m_packedThrottles = data; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 583214c..d08b25f 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -228,7 +228,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } #endregion BinaryStats - m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); + m_throttle = new TokenBucket(null, sceneThrottleBps); ThrottleRates = new ThrottleRates(configSource); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 0a8331f..e4d59ff 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -26,6 +26,10 @@ */ using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using log4net; namespace OpenSim.Region.ClientStack.LindenUDP { @@ -35,89 +39,126 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// 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; + 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. + /// + private const Int32 m_ticksPerQuantum = 1000; + + /// + /// This is the number of quantums worth of packets that can + /// be accommodated during a burst + /// + private const Double m_quantumsPerBurst = 1.5; + + /// + /// + private const Int32 m_minimumDripRate = 1400; + /// Time of the last drip, in system ticks - int lastDrip; + private Int32 m_lastDrip; + + /// + /// The number of bytes that can be sent at this moment. This is the + /// current number of tokens in the bucket + /// + private Int64 m_tokenCount; - #region Properties + /// + /// Map of children buckets and their requested maximum burst rate + /// + private 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 /// + private TokenBucket m_parent; public TokenBucket Parent { - get { return parent; } + get { return m_parent; } + set { m_parent = value; } } /// /// Maximum burst rate in bytes per second. This is the maximum number - /// of tokens that can accumulate in the bucket at any one time + /// of tokens that can accumulate in the bucket at any one time. This + /// also sets the total request for leaf nodes /// - public int MaxBurst + private Int64 m_burstRate; + public Int64 RequestedBurstRate { - get { return maxBurst; } - set { maxBurst = (value >= 0 ? value : 0); } + get { return m_burstRate; } + set { m_burstRate = (value < 0 ? 0 : value); } } + public Int64 BurstRate + { + get { + double rate = RequestedBurstRate * BurstRateModifier(); + if (rate < m_minimumDripRate * m_quantumsPerBurst) + rate = m_minimumDripRate * m_quantumsPerBurst; + + return (Int64) rate; + } + } + /// /// The speed limit of this bucket in bytes per second. This is the - /// number of tokens that are added to the bucket per second + /// number of tokens that are added to the bucket per quantum /// /// 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 + private Int64 m_dripRate; + public Int64 RequestedDripRate { - 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; - } + get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } + set { + m_dripRate = (value < 0 ? 0 : value); + m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); + m_totalDripRequest = m_dripRate; + if (m_parent != null) + m_parent.RegisterRequest(this,m_dripRate); } } - /// - /// The speed limit of this bucket in bytes per millisecond - /// - public int DripPerMS + public Int64 DripRate { - get { return tokensPerMS; } + get { + if (m_parent == null) + return Math.Min(RequestedDripRate,TotalDripRequest); + + double rate = (double)RequestedDripRate * m_parent.DripRateModifier(); + if (rate < m_minimumDripRate) + rate = m_minimumDripRate; + + return (Int64)rate; + } } /// - /// 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 + /// The current total of the requested maximum burst rates of + /// this bucket's children buckets. /// - public int Content - { - get { return content; } - } + private Int64 m_totalDripRequest; + public Int64 TotalDripRequest + { + get { return m_totalDripRequest; } + set { m_totalDripRequest = value; } + } + +#endregion Properties - #endregion Properties +#region Constructor /// /// Default constructor @@ -128,56 +169,115 @@ 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, int maxBurst, int dripRate) + public TokenBucket(TokenBucket parent, Int64 dripRate) { - this.parent = parent; - MaxBurst = maxBurst; - DripRate = dripRate; - lastDrip = Environment.TickCount & Int32.MaxValue; + m_identifier = m_counter++; + + Parent = parent; + RequestedDripRate = dripRate; + // TotalDripRequest = dripRate; // this will be overwritten when a child node registers + // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); + m_lastDrip = Environment.TickCount & Int32.MaxValue; } +#endregion Constructor + /// - /// Remove a given number of tokens from the bucket + /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning + /// no modification if the requested bandwidth is less than the + /// max burst bandwidth all the way to the root of the throttle + /// hierarchy. However, if any of the parents is over-booked, then + /// the modifier will be less than 1. /// - /// 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) + private double DripRateModifier() + { + Int64 driprate = DripRate; + return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; + } + + /// + /// + private double BurstRateModifier() + { + // for now... burst rate is always m_quantumsPerBurst (constant) + // larger than drip rate so the ratio of burst requests is the + // same as the drip ratio + return DripRateModifier(); + } + + /// + /// Register drip rate requested by a child of this throttle. Pass the + /// changes up the hierarchy. + /// + public void RegisterRequest(TokenBucket child, Int64 request) { - bool dummy; - return RemoveTokens(amount, out dummy); + m_children[child] = request; + // m_totalDripRequest = m_children.Values.Sum(); + + m_totalDripRequest = 0; + foreach (KeyValuePair cref in m_children) + m_totalDripRequest += cref.Value; + + // Pass the new values up to the parent + if (m_parent != null) + m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); } /// + /// Remove the rate requested by a child of this throttle. Pass the + /// changes up the hierarchy. + /// + public void UnregisterRequest(TokenBucket child) + { + m_children.Remove(child); + // m_totalDripRequest = m_children.Values.Sum(); + + m_totalDripRequest = 0; + foreach (KeyValuePair cref in m_children) + m_totalDripRequest += cref.Value; + + // Pass the new values up to the parent + if (m_parent != null) + m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); + } + + /// /// 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) + public bool RemoveTokens(Int64 amount) { - if (maxBurst == 0) + // Deposit tokens for this interval + Drip(); + + // If we have enough tokens then remove them and return + if (m_tokenCount - amount >= 0) { - dripSucceeded = true; - return true; + if (m_parent == null || m_parent.RemoveTokens(amount)) + { + m_tokenCount -= amount; + return true; + } } - dripSucceeded = Drip(); + return false; + } - if (content - amount >= 0) - { - if (parent != null && !parent.RemoveTokens(amount)) - return false; + /// + /// Deposit tokens into the bucket from a child bucket that did + /// not use all of its available tokens + /// + private void Deposit(Int64 count) + { + m_tokenCount += count; - content -= amount; - return true; - } - else - { - return false; - } + // Deposit the overflow in the parent bucket, this is how we share + // unused bandwidth + Int64 burstrate = BurstRate; + if (m_tokenCount > burstrate) + m_tokenCount = burstrate; } /// @@ -186,37 +286,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// call to Drip /// /// True if tokens were added to the bucket, otherwise false - public bool Drip() + private void Drip() { - if (tokensPerMS == 0) + // This should never happen... means we are a leaf node and were created + // with no drip rate... + if (DripRate == 0) { - content = maxBurst; - return true; + m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0"); + return; } - else - { - int now = Environment.TickCount & Int32.MaxValue; - int deltaMS = now - lastDrip; - - if (deltaMS <= 0) - { - if (deltaMS < 0) - lastDrip = now; - return false; - } + + // Determine the interval over which we are adding tokens, never add + // more than a single quantum of tokens + Int32 now = Environment.TickCount & Int32.MaxValue; + Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum); - int dripAmount = deltaMS * tokensPerMS; + m_lastDrip = now; - content = Math.Min(content + dripAmount, maxBurst); - lastDrip = now; + // This can be 0 in the very unusual case that the timer wrapped + // It can be 0 if we try add tokens at a sub-tick rate + if (deltaMS <= 0) + return; - if (dripAmount < 0 || content < 0) - // sim has been idle for too long, integer has overflown - // previous calculation is meaningless, let's put it at correct max - content = maxBurst; - - return true; - } + Deposit(deltaMS * DripRate / m_ticksPerQuantum); } } } -- cgit v1.1 From 69d014e1dcb0e05a4ec927a5501156627856bccb Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 12:36:36 -0700 Subject: First pass at moving object property requests into a queue similar to the entity update queue. The number of property packets can become significant when selecting/deselecting large numbers of objects. This is experimental code. --- OpenSim/Client/MXP/ClientStack/MXPClientView.cs | 4 +- .../Client/VWoHTTP/ClientStack/VWHClientView.cs | 4 +- OpenSim/Framework/IClientAPI.cs | 39 +-- .../Region/ClientStack/LindenUDP/LLClientView.cs | 351 +++++++++++++-------- .../Region/ClientStack/LindenUDP/PriorityQueue.cs | 12 +- .../Region/Examples/SimpleModule/MyNpcCharacter.cs | 12 +- .../Region/Framework/Scenes/SceneObjectGroup.cs | 10 +- OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 10 +- .../Server/IRCClientView.cs | 5 +- .../Region/OptionalModules/World/NPC/NPCAvatar.cs | 12 +- OpenSim/Tests/Common/Mock/TestClient.cs | 11 +- 11 files changed, 262 insertions(+), 208 deletions(-) diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index d1a0440..a604a2e 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -1337,12 +1337,12 @@ namespace OpenSim.Client.MXP.ClientStack // Need to translate to MXP somehow } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { //throw new System.NotImplementedException(); } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { //throw new System.NotImplementedException(); } diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index fc27f01..d8cd0ac 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -884,12 +884,12 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { throw new System.NotImplementedException(); } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { throw new System.NotImplementedException(); } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 5bf0b7b..c56a756 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -570,16 +570,30 @@ namespace OpenSim.Framework public float dwell; } - public class EntityUpdate + public class IEntityUpdate { public ISceneEntity Entity; - public PrimUpdateFlags Flags; - public float TimeDilation; + public uint Flags; - public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) + public IEntityUpdate(ISceneEntity entity, uint flags) { Entity = entity; Flags = flags; + } + } + + + public class EntityUpdate : IEntityUpdate + { + // public ISceneEntity Entity; + // public PrimUpdateFlags Flags; + public float TimeDilation; + + public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) + : base(entity,(uint)flags) + { + //Entity = entity; + // Flags = flags; TimeDilation = timedilation; } } @@ -1211,20 +1225,9 @@ namespace OpenSim.Framework /// void SendSimStats(SimStats stats); - void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, - uint Category, - UUID LastOwnerID, string ObjectName, string Description); - - void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, - UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, - string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, - uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice); + void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags); + + void SendObjectPropertiesReply(ISceneEntity Entity); void SendAgentOffline(UUID[] agentIDs); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 934a2d5..b96343e 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -386,6 +386,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_cachedTextureSerial; private PriorityQueue m_entityUpdates; + private PriorityQueue m_entityProps; private Prioritizer m_prioritizer; private bool m_disableFacelights = false; @@ -433,9 +434,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected IAssetService m_assetService; private const bool m_checkPackets = true; - private Timer m_propertiesPacketTimer; - private List m_propertiesBlocks = new List(); - #endregion Class Members #region Properties @@ -511,6 +509,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene = scene; m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); + m_entityProps = new PriorityQueue(m_scene.Entities.Count); m_fullUpdateDataBlocksBuilder = new List(); m_killRecord = new HashSet(); // m_attachmentsSent = new HashSet(); @@ -534,9 +533,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_udpClient.OnQueueEmpty += HandleQueueEmpty; m_udpClient.OnPacketStats += PopulateStats; - m_propertiesPacketTimer = new Timer(100); - m_propertiesPacketTimer.Elapsed += ProcessObjectPropertiesPacket; - m_prioritizer = new Prioritizer(m_scene); RegisterLocalPacketHandlers(); @@ -3636,9 +3632,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); } - private Int32 m_LastQueueFill = 0; - private uint m_maxUpdates = 0; - private void ProcessEntityUpdates(int maxUpdates) { OpenSim.Framework.Lazy> objectUpdateBlocks = new OpenSim.Framework.Lazy>(); @@ -3646,46 +3639,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.Lazy> terseUpdateBlocks = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy>(); + // Check to see if this is a flush if (maxUpdates <= 0) { - m_maxUpdates = Int32.MaxValue; + maxUpdates = Int32.MaxValue; } - else - { - if (m_maxUpdates == 0 || m_LastQueueFill == 0) - { - m_maxUpdates = (uint)maxUpdates; - } - else - { - if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) - m_maxUpdates += 5; - else - m_maxUpdates = m_maxUpdates >> 1; - } - m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); - } - m_LastQueueFill = Util.EnvironmentTickCount(); - + int updatesThisCall = 0; -// -// DEBUGGING CODE... REMOVE -// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates); -// // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race // condition where a kill can be processed before an out-of-date update for the same object. lock (m_killRecord) { float avgTimeDilation = 1.0f; - EntityUpdate update; + IEntityUpdate iupdate; Int32 timeinqueue; // this is just debugging code & can be dropped later - while (updatesThisCall < m_maxUpdates) + while (updatesThisCall < maxUpdates) { lock (m_entityUpdates.SyncRoot) - if (!m_entityUpdates.TryDequeue(out update, out timeinqueue)) + if (!m_entityUpdates.TryDequeue(out iupdate, out timeinqueue)) break; + + EntityUpdate update = (EntityUpdate)iupdate; + avgTimeDilation += update.TimeDilation; avgTimeDilation *= 0.5f; @@ -3725,7 +3702,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region UpdateFlags to packet type conversion - PrimUpdateFlags updateFlags = update.Flags; + PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags; bool canUseCompressed = true; bool canUseImproved = true; @@ -3804,6 +3781,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Block Construction } + #region Packet Sending @@ -3904,12 +3882,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Primitive Packet/Data Sending Methods + // These are used to implement an adaptive backoff in the number + // of updates converted to packets. Since we don't want packets + // to sit in the queue with old data, only convert enough updates + // to packets that can be sent in 200ms. + private Int32 m_LastQueueFill = 0; + private Int32 m_maxUpdates = 0; + void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) { if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) { + if (m_maxUpdates == 0 || m_LastQueueFill == 0) + { + m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; + } + else + { + if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) + m_maxUpdates += 5; + else + m_maxUpdates = m_maxUpdates >> 1; + } + m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); + m_LastQueueFill = Util.EnvironmentTickCount(); + if (m_entityUpdates.Count > 0) - ProcessEntityUpdates(m_udpServer.PrimUpdatesPerCallback); + ProcessEntityUpdates(m_maxUpdates); + + if (m_entityProps.Count > 0) + ProcessEntityPropertyRequests(m_maxUpdates); } if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) @@ -4023,134 +4025,192 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(pack, ThrottleOutPacketType.Task); } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) - { - ObjectPropertiesFamilyPacket objPropFamilyPack = (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); - // TODO: don't create new blocks if recycling an old packet - - ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); - objPropDB.RequestFlags = RequestFlags; - objPropDB.ObjectID = ObjectUUID; - if (OwnerID == GroupID) - objPropDB.OwnerID = UUID.Zero; - else - objPropDB.OwnerID = OwnerID; - objPropDB.GroupID = GroupID; - objPropDB.BaseMask = BaseMask; - objPropDB.OwnerMask = OwnerMask; - objPropDB.GroupMask = GroupMask; - objPropDB.EveryoneMask = EveryoneMask; - objPropDB.NextOwnerMask = NextOwnerMask; +/// ----------------------------------------------------------------- +/// +/// ----------------------------------------------------------------- - // TODO: More properties are needed in SceneObjectPart! - objPropDB.OwnershipCost = OwnershipCost; - objPropDB.SaleType = SaleType; - objPropDB.SalePrice = SalePrice; - objPropDB.Category = Category; - objPropDB.LastOwnerID = LastOwnerID; - objPropDB.Name = Util.StringToBytes256(ObjectName); - objPropDB.Description = Util.StringToBytes256(Description); - objPropFamilyPack.ObjectData = objPropDB; - objPropFamilyPack.Header.Zerocoded = true; - OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task); - } - - public void SendObjectPropertiesReply( - UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + private class ObjectPropertyUpdate : IEntityUpdate { - //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); - // TODO: don't create new blocks if recycling an old packet + internal bool SendFamilyProps; + + public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam) + : base(entity,flags) + { + SendFamilyProps = sendfam; + } + } + + public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags) + { + uint priority = m_prioritizer.GetUpdatePriority(this, entity); + lock (m_entityProps.SyncRoot) + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true)); + } - ObjectPropertiesPacket.ObjectDataBlock block = - new ObjectPropertiesPacket.ObjectDataBlock(); + public void SendObjectPropertiesReply(ISceneEntity entity) + { + uint priority = m_prioritizer.GetUpdatePriority(this, entity); + lock (m_entityProps.SyncRoot) + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false)); + } - block.ItemID = ItemID; - block.CreationDate = CreationDate; - block.CreatorID = CreatorUUID; - block.FolderID = FolderUUID; - block.FromTaskID = FromTaskUUID; - block.GroupID = GroupUUID; - block.InventorySerial = InventorySerial; + private void ProcessEntityPropertyRequests(int maxUpdates) + { + OpenSim.Framework.Lazy> objectFamilyBlocks = + new OpenSim.Framework.Lazy>(); - block.LastOwnerID = LastOwnerUUID; - // proper.ObjectData[0].LastOwnerID = UUID.Zero; + OpenSim.Framework.Lazy> objectPropertiesBlocks = + new OpenSim.Framework.Lazy>(); - block.ObjectID = ObjectUUID; - if (OwnerUUID == GroupUUID) - block.OwnerID = UUID.Zero; - else - block.OwnerID = OwnerUUID; - block.TouchName = Util.StringToBytes256(TouchTitle); - block.TextureID = TextureID; - block.SitName = Util.StringToBytes256(SitTitle); - block.Name = Util.StringToBytes256(ItemName); - block.Description = Util.StringToBytes256(ItemDescription); - block.OwnerMask = OwnerMask; - block.NextOwnerMask = NextOwnerMask; - block.GroupMask = GroupMask; - block.EveryoneMask = EveryoneMask; - block.BaseMask = BaseMask; - // proper.ObjectData[0].AggregatePerms = 53; - // proper.ObjectData[0].AggregatePermTextures = 0; - // proper.ObjectData[0].AggregatePermTexturesOwner = 0; - block.SaleType = saleType; - block.SalePrice = salePrice; + IEntityUpdate iupdate; + Int32 timeinqueue; // this is just debugging code & can be dropped later - lock (m_propertiesPacketTimer) + int updatesThisCall = 0; + while (updatesThisCall < m_maxUpdates) { - m_propertiesBlocks.Add(block); + lock (m_entityProps.SyncRoot) + if (!m_entityProps.TryDequeue(out iupdate, out timeinqueue)) + break; - int length = 0; - foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) + ObjectPropertyUpdate update = (ObjectPropertyUpdate)iupdate; + if (update.SendFamilyProps) { - length += b.Length; + if (update.Entity is SceneObjectPart) + { + SceneObjectPart sop = (SceneObjectPart)update.Entity; + ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); + objectFamilyBlocks.Value.Add(objPropDB); + } } - if (length > 1100) // FIXME: use real MTU + else { - ProcessObjectPropertiesPacket(null, null); - m_propertiesPacketTimer.Stop(); - return; + if (update.Entity is SceneObjectPart) + { + SceneObjectPart sop = (SceneObjectPart)update.Entity; + ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); + objectPropertiesBlocks.Value.Add(objPropDB); + } } + } + + + if (objectPropertiesBlocks.IsValueCreated) + { + List blocks = objectPropertiesBlocks.Value; - m_propertiesPacketTimer.Stop(); - m_propertiesPacketTimer.Start(); + ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); + packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count]; + for (int i = 0; i < blocks.Count; i++) + packet.ObjectData[i] = blocks[i]; + + packet.Header.Zerocoded = true; + OutPacket(packet, ThrottleOutPacketType.Task, true); } + + + if (objectFamilyBlocks.IsValueCreated) + { + List blocks = objectFamilyBlocks.Value; + + // ObjectPropertiesFamilyPacket objPropFamilyPack = + // (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); + // + // objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count]; + // for (int i = 0; i < blocks.Count; i++) + // objPropFamilyPack.ObjectData[i] = blocks[i]; + // + // OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true); + + // one packet per object block... uggh... + for (int i = 0; i < blocks.Count; i++) + { + ObjectPropertiesFamilyPacket packet = + (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); - //proper.Header.Zerocoded = true; - //OutPacket(proper, ThrottleOutPacketType.Task); + packet.ObjectData = blocks[i]; + packet.Header.Zerocoded = true; + OutPacket(packet, ThrottleOutPacketType.Task); + } + + } + } - private void ProcessObjectPropertiesPacket(Object sender, ElapsedEventArgs e) + private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) { - ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); + ObjectPropertiesFamilyPacket.ObjectDataBlock block = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); - lock (m_propertiesPacketTimer) - { - m_propertiesPacketTimer.Stop(); + block.RequestFlags = requestFlags; + block.ObjectID = sop.UUID; + if (sop.OwnerID == sop.GroupID) + block.OwnerID = UUID.Zero; + else + block.OwnerID = sop.OwnerID; + block.GroupID = sop.GroupID; + block.BaseMask = sop.BaseMask; + block.OwnerMask = sop.OwnerMask; + block.GroupMask = sop.GroupMask; + block.EveryoneMask = sop.EveryoneMask; + block.NextOwnerMask = sop.NextOwnerMask; - proper.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[m_propertiesBlocks.Count]; + // TODO: More properties are needed in SceneObjectPart! + block.OwnershipCost = sop.OwnershipCost; + block.SaleType = sop.ObjectSaleType; + block.SalePrice = sop.SalePrice; + block.Category = sop.Category; + block.LastOwnerID = sop.CreatorID; // copied from old SOG call... is this right? + block.Name = Util.StringToBytes256(sop.Name); + block.Description = Util.StringToBytes256(sop.Description); - int index = 0; + return block; + } - foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) - { - proper.ObjectData[index++] = b; - } + private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop) + { + //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); + // TODO: don't create new blocks if recycling an old packet - m_propertiesBlocks.Clear(); - } + ObjectPropertiesPacket.ObjectDataBlock block = + new ObjectPropertiesPacket.ObjectDataBlock(); + + block.ObjectID = sop.UUID; + block.Name = Util.StringToBytes256(sop.Name); + block.Description = Util.StringToBytes256(sop.Description); - proper.Header.Zerocoded = true; - OutPacket(proper, ThrottleOutPacketType.Task); + block.CreationDate = (ulong)sop.CreationDate * 1000000; // viewer wants date in microseconds + block.CreatorID = sop.CreatorID; + block.GroupID = sop.GroupID; + block.LastOwnerID = sop.LastOwnerID; + if (sop.OwnerID == sop.GroupID) + block.OwnerID = UUID.Zero; + else + block.OwnerID = sop.OwnerID; + + block.ItemID = sop.FromUserInventoryItemID; + block.FolderID = UUID.Zero; // sop.FromFolderID ?? + block.FromTaskID = UUID.Zero; // ??? + block.InventorySerial = (short)sop.InventorySerial; + + SceneObjectPart root = sop.ParentGroup.RootPart; + + block.TouchName = Util.StringToBytes256(root.TouchName); + block.TextureID = new byte[0]; // TextureID ??? + block.SitName = Util.StringToBytes256(root.SitName); + block.OwnerMask = root.OwnerMask; + block.NextOwnerMask = root.NextOwnerMask; + block.GroupMask = root.GroupMask; + block.EveryoneMask = root.EveryoneMask; + block.BaseMask = root.BaseMask; + block.SaleType = root.ObjectSaleType; + block.SalePrice = root.SalePrice; + + return block; } +/// ----------------------------------------------------------------- +/// +/// ----------------------------------------------------------------- + #region Estate Data Sending Methods private static bool convertParamStringToBool(byte[] field) @@ -4482,6 +4542,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendForceClientSelectObjects(List ObjectIDs) { + m_log.WarnFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count); + bool firstCall = true; const int MAX_OBJECTS_PER_PACKET = 251; ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect); @@ -11374,6 +11436,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); } + if (throttlePacketType == ThrottleOutPacketType.Task) + { + System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); // get call stack + System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) + + string stack = ""; + for (int count = 1; count < stackFrames.Length; count++) + { + stack += (stack == "" ? "" : ",") + stackFrames[count].GetMethod().Name; + if (count > 5) break; + } + + // m_log.WarnFormat("[BADGUY] {0}", stack); + } + m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs index 364ce4b..6521a00 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs @@ -78,7 +78,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public bool Enqueue(uint pqueue, EntityUpdate value) + public bool Enqueue(uint pqueue, IEntityUpdate value) { LookupItem lookup; @@ -99,7 +99,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return true; } - internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) + internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue) { for (int i = 0; i < m_numberOfQueues; ++i) { @@ -122,7 +122,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } timeinqueue = 0; - value = default(EntityUpdate); + value = default(IEntityUpdate); return false; } @@ -175,8 +175,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region MinHeapItem private struct MinHeapItem : IComparable { - private EntityUpdate value; - internal EntityUpdate Value { + private IEntityUpdate value; + internal IEntityUpdate Value { get { return this.value; } @@ -212,7 +212,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP this.pqueue = pqueue; } - internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) + internal MinHeapItem(uint pqueue, UInt64 entryorder, IEntityUpdate value) { this.entrytime = Util.EnvironmentTickCount(); this.entryorder = entryorder; diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index d939329..89e9e20 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -702,18 +702,12 @@ namespace OpenSim.Region.Examples.SimpleModule { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { + } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index ca7d9d9..30563d4 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -1765,10 +1765,12 @@ namespace OpenSim.Region.Framework.Scenes /// public void ServiceObjectPropertiesFamilyRequest(IClientAPI remoteClient, UUID AgentID, uint RequestFlags) { - remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask, - RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask, - RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, - RootPart.CreatorID, RootPart.Name, RootPart.Description); + remoteClient.SendObjectPropertiesFamilyData(RootPart, RequestFlags); + +// remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask, +// RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask, +// RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, +// RootPart.CreatorID, RootPart.Name, RootPart.Description); } public void SetPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 4d5eedf..3d2eacd 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2055,15 +2055,7 @@ namespace OpenSim.Region.Framework.Scenes public void GetProperties(IClientAPI client) { - //Viewer wants date in microseconds so multiply it by 1,000,000. - client.SendObjectPropertiesReply( - m_fromUserInventoryItemID, (ulong)_creationDate*(ulong)1e6, _creatorID, UUID.Zero, UUID.Zero, - _groupID, (short)InventorySerial, _lastOwnerID, UUID, _ownerID, - ParentGroup.RootPart.TouchName, new byte[0], ParentGroup.RootPart.SitName, Name, Description, - ParentGroup.RootPart._ownerMask, ParentGroup.RootPart._nextOwnerMask, ParentGroup.RootPart._groupMask, ParentGroup.RootPart._everyoneMask, - ParentGroup.RootPart._baseMask, - ParentGroup.RootPart.ObjectSaleType, - ParentGroup.RootPart.SalePrice); + client.SendObjectPropertiesReply(this); } public UUID GetRootPartUUID() diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 821cd4b..4b6e52e 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -1332,14 +1332,13 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { - } public void SendAgentOffline(UUID[] agentIDs) diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 96760a2..2504e30 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -786,18 +786,12 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { + } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index d1dc17f..dca5626 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -816,18 +816,11 @@ namespace OpenSim.Tests.Common.Mock { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } -- cgit v1.1 From 1a0f107012ae96f5030b564cc51b6f36b949a467 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 14:40:57 -0700 Subject: fixed a couple bugs with the property queues --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 38 +++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index b96343e..4db5b22 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4091,9 +4091,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP objectPropertiesBlocks.Value.Add(objPropDB); } } + + updatesThisCall++; } + Int32 ppcnt = 0; + Int32 pbcnt = 0; + if (objectPropertiesBlocks.IsValueCreated) { List blocks = objectPropertiesBlocks.Value; @@ -4105,9 +4110,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Header.Zerocoded = true; OutPacket(packet, ThrottleOutPacketType.Task, true); + + pbcnt += blocks.Count; + ppcnt++; } + Int32 fpcnt = 0; + Int32 fbcnt = 0; + if (objectFamilyBlocks.IsValueCreated) { List blocks = objectFamilyBlocks.Value; @@ -4130,10 +4141,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.ObjectData = blocks[i]; packet.Header.Zerocoded = true; OutPacket(packet, ThrottleOutPacketType.Task); + + fpcnt++; + fbcnt++; } } + m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); + m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); } private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) @@ -11401,6 +11417,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, throttlePacketType, true); } +/// + Dictionary pktsrc = new Dictionary(); + uint pktcnt = 0; +/// + /// /// This is the starting point for sending a simulator packet out to the client /// @@ -11448,7 +11469,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (count > 5) break; } - // m_log.WarnFormat("[BADGUY] {0}", stack); + lock(pktsrc) + { + if (! pktsrc.ContainsKey(stack)) + pktsrc.Add(stack,0); + pktsrc[stack]++; + + if (++pktcnt > 500) + { + pktcnt = 0; + m_log.WarnFormat("[PACKETCOUNTS] START"); + foreach (KeyValuePair pkt in pktsrc) + m_log.WarnFormat("[PACKETCOUNTS] {0,8}, {1}", pkt.Value, pkt.Key); + pktsrc.Clear(); + m_log.WarnFormat("[PACKETCOUNTS] END"); + } + } } m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); -- cgit v1.1 From 78c04d61caed7dc62eb299a09148934d95a1c882 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 15:40:57 -0700 Subject: Fixed the update of items in the priority queue to enable both types of property updates to be specified. Not sure if one form of property update should supercede another. But for now the old OpenSim behavior is preserved by sending both. --- OpenSim/Framework/IClientAPI.cs | 5 +++++ .../Region/ClientStack/LindenUDP/LLClientView.cs | 21 +++++++++++++++------ .../Region/ClientStack/LindenUDP/PriorityQueue.cs | 2 +- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index c56a756..f573c32 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -575,6 +575,11 @@ namespace OpenSim.Framework public ISceneEntity Entity; public uint Flags; + public virtual void Update(IEntityUpdate update) + { + this.Flags |= update.Flags; + } + public IEntityUpdate(ISceneEntity entity, uint flags) { Entity = entity; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 4db5b22..92a76ac 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4032,26 +4032,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP private class ObjectPropertyUpdate : IEntityUpdate { internal bool SendFamilyProps; + internal bool SendObjectProps; - public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam) + public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam, bool sendobj) : base(entity,flags) { SendFamilyProps = sendfam; + SendObjectProps = sendobj; + } + public void Update(ObjectPropertyUpdate update) + { + SendFamilyProps = SendFamilyProps || update.SendFamilyProps; + SendObjectProps = SendObjectProps || update.SendObjectProps; + Flags |= update.Flags; } } public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags) { - uint priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = 0; // time based ordering only lock (m_entityProps.SyncRoot) - m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true)); + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false)); } public void SendObjectPropertiesReply(ISceneEntity entity) { - uint priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = 0; // time based ordering only lock (m_entityProps.SyncRoot) - m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false)); + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false,true)); } private void ProcessEntityPropertyRequests(int maxUpdates) @@ -4082,7 +4090,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP objectFamilyBlocks.Value.Add(objPropDB); } } - else + + if (update.SendObjectProps) { if (update.Entity is SceneObjectPart) { diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs index 6521a00..b62ec07 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs @@ -87,7 +87,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_lookupTable.TryGetValue(localid, out lookup)) { entry = lookup.Heap[lookup.Handle].EntryOrder; - value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; + value.Update(lookup.Heap[lookup.Handle].Value); lookup.Heap.Remove(lookup.Handle); } -- cgit v1.1 From b84ad81740ea1c732711b7f555d44c56b3858178 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 15:55:21 -0700 Subject: remove packet monitoring debugging code --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 118 +-------------------- 1 file changed, 2 insertions(+), 116 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 92a76ac..cf04f0d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -300,77 +300,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Used to adjust Sun Orbit values so Linden based viewers properly position sun private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; - // First log file or time has expired, start writing to a new log file -// -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// THIS IS DEBUGGING CODE & SHOULD BE REMOVED -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- - public class QueueLogger - { - public Int32 start = 0; - public StreamWriter Log = null; - private Dictionary m_idMap = new Dictionary(); - - public QueueLogger() - { - DateTime now = DateTime.Now; - String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss")); - Log = new StreamWriter(fname); - - start = Util.EnvironmentTickCount(); - } - - public int LookupID(UUID uuid) - { - int localid; - if (! m_idMap.TryGetValue(uuid,out localid)) - { - localid = m_idMap.Count + 1; - m_idMap[uuid] = localid; - } - - return localid; - } - } - - public static QueueLogger QueueLog = null; - - // ----------------------------------------------------------------- - public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - int aid = QueueLog.LookupID(avatar); - QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue); - } - } - - // ----------------------------------------------------------------- - public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString()); - } - } -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected static Dictionary PacketHandlers = new Dictionary(); //Global/static handlers for all clients @@ -4025,10 +3954,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(pack, ThrottleOutPacketType.Task); } -/// ----------------------------------------------------------------- -/// -/// ----------------------------------------------------------------- - private class ObjectPropertyUpdate : IEntityUpdate { internal bool SendFamilyProps; @@ -4157,8 +4082,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } - m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); - m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); + // m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); + // m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); } private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) @@ -4232,10 +4157,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return block; } -/// ----------------------------------------------------------------- -/// -/// ----------------------------------------------------------------- - #region Estate Data Sending Methods private static bool convertParamStringToBool(byte[] field) @@ -11426,11 +11347,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, throttlePacketType, true); } -/// - Dictionary pktsrc = new Dictionary(); - uint pktcnt = 0; -/// - /// /// This is the starting point for sending a simulator packet out to the client /// @@ -11465,36 +11381,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (logPacket) m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); } - - if (throttlePacketType == ThrottleOutPacketType.Task) - { - System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); // get call stack - System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) - - string stack = ""; - for (int count = 1; count < stackFrames.Length; count++) - { - stack += (stack == "" ? "" : ",") + stackFrames[count].GetMethod().Name; - if (count > 5) break; - } - - lock(pktsrc) - { - if (! pktsrc.ContainsKey(stack)) - pktsrc.Add(stack,0); - pktsrc[stack]++; - - if (++pktcnt > 500) - { - pktcnt = 0; - m_log.WarnFormat("[PACKETCOUNTS] START"); - foreach (KeyValuePair pkt in pktsrc) - m_log.WarnFormat("[PACKETCOUNTS] {0,8}, {1}", pkt.Value, pkt.Key); - pktsrc.Clear(); - m_log.WarnFormat("[PACKETCOUNTS] END"); - } - } - } m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); } -- cgit v1.1 From 5b89c66c9704f2bd219a35f8a70ac60e751d7f49 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 11 Apr 2011 09:06:28 -0700 Subject: New tokenbucket algorithm. This one provides fair sharing of the queues when client and simulator throttles are set. This algorithm also uses pre-defined burst rate of 150% of the sustained rate for each of the throttles. Removed the "state" queue. The state queue is not a Linden queue and appeared to be used just to get kill packets sent. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 6 +- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 96 ++++--- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 2 +- .../Region/ClientStack/LindenUDP/TokenBucket.cs | 300 ++++++++++++++------- 4 files changed, 259 insertions(+), 145 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 76d7f79..5980669 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -1610,7 +1610,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - OutPacket(kill, ThrottleOutPacketType.State); + // OutPacket(kill, ThrottleOutPacketType.State); + OutPacket(kill, ThrottleOutPacketType.Task); } } @@ -2440,7 +2441,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Effect = effectBlocks; - OutPacket(packet, ThrottleOutPacketType.State); + // OutPacket(packet, ThrottleOutPacketType.State); + OutPacket(packet, ThrottleOutPacketType.Task); } public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 9a8bfd3..5a69851 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -135,7 +135,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_nextOnQueueEmpty = 1; /// Throttle bucket for this agent's connection - private readonly TokenBucket m_throttle; + private readonly TokenBucket 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 @@ -174,7 +176,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_maxRTO = maxRTO; // Create a token bucket throttle for this client that has the scene token bucket as a parent - m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); + m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit); + // Create a token bucket throttle for the total categary with the client bucket as a throttle + m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit); // Create an array of token buckets for this clients different throttle categories m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; @@ -185,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Initialize the packet outboxes, where packets sit while they are waiting for tokens m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); // Initialize the token buckets that control the throttling for each category - m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); + m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetLimit(type)); } // Default the retransmission timeout to three seconds @@ -206,6 +210,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_packetOutboxes[i].Clear(); m_nextPackets[i] = null; } + + // pull the throttle out of the scene throttle + m_throttleClient.Parent.UnregisterRequest(m_throttleClient); OnPacketStats = null; OnQueueEmpty = null; } @@ -216,6 +223,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Information about the client connection public ClientInfo GetClientInfo() { +/// + TokenBucket tb; + + tb = m_throttleClient.Parent; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest,"ROOT"); + + tb = m_throttleClient; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CLIENT"); + + tb = m_throttleCategory; + m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CATEGORY"); + + for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) + { + tb = m_throttleCategories[i]; + m_log.WarnFormat("[TOKENS] {4} <{0}:{1}>: Actual={2},Requested={3}",AgentID,i,tb.DripRate,tb.RequestedDripRate," BUCKET"); + } + +/// + // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists // of pending and needed ACKs for every client every time some method wants information about // this connection is a recipe for poor performance @@ -223,13 +250,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP info.pendingAcks = new Dictionary(); info.needAck = new Dictionary(); - info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; - info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; - info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; - info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; - info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; - info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; - info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; + info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; + info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; + info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; + info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; + // info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; + info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + info.taskThrottle + info.assetThrottle + info.textureThrottle; @@ -317,8 +345,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); // State is a subcategory of task that we allocate a percentage to - int state = (int)((float)task * STATE_TASK_PERCENTAGE); - task -= state; + int state = 0; + // int state = (int)((float)task * STATE_TASK_PERCENTAGE); + // task -= state; // Make sure none of the throttles are set below our packet MTU, // otherwise a throttle could become permanently clogged @@ -339,40 +368,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Update the token buckets with new throttle values TokenBucket bucket; - bucket = m_throttle; - bucket.MaxBurst = total; + bucket = m_throttleCategory; + bucket.RequestedDripRate = total; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; - bucket.DripRate = resend; - bucket.MaxBurst = resend; + bucket.RequestedDripRate = resend; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land]; - bucket.DripRate = land; - bucket.MaxBurst = land; + bucket.RequestedDripRate = land; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind]; - bucket.DripRate = wind; - bucket.MaxBurst = wind; + bucket.RequestedDripRate = wind; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud]; - bucket.DripRate = cloud; - bucket.MaxBurst = cloud; + bucket.RequestedDripRate = cloud; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset]; - bucket.DripRate = asset; - bucket.MaxBurst = asset; + bucket.RequestedDripRate = asset; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task]; - bucket.DripRate = task + state; - bucket.MaxBurst = task + state; + bucket.RequestedDripRate = task; bucket = m_throttleCategories[(int)ThrottleOutPacketType.State]; - bucket.DripRate = state; - bucket.MaxBurst = state; + bucket.RequestedDripRate = state; bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture]; - bucket.DripRate = texture; - bucket.MaxBurst = texture; + bucket.RequestedDripRate = texture; // Reset the packed throttles cached data m_packedThrottles = null; @@ -387,14 +408,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP data = new byte[7 * 4]; int i = 0; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + - m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate), 0, data, i, 4); i += 4; m_packedThrottles = data; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 583214c..d08b25f 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -228,7 +228,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } #endregion BinaryStats - m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); + m_throttle = new TokenBucket(null, sceneThrottleBps); ThrottleRates = new ThrottleRates(configSource); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 0a8331f..e4d59ff 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -26,6 +26,10 @@ */ using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using log4net; namespace OpenSim.Region.ClientStack.LindenUDP { @@ -35,89 +39,126 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// 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; + 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. + /// + private const Int32 m_ticksPerQuantum = 1000; + + /// + /// This is the number of quantums worth of packets that can + /// be accommodated during a burst + /// + private const Double m_quantumsPerBurst = 1.5; + + /// + /// + private const Int32 m_minimumDripRate = 1400; + /// Time of the last drip, in system ticks - int lastDrip; + private Int32 m_lastDrip; + + /// + /// The number of bytes that can be sent at this moment. This is the + /// current number of tokens in the bucket + /// + private Int64 m_tokenCount; - #region Properties + /// + /// Map of children buckets and their requested maximum burst rate + /// + private 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 /// + private TokenBucket m_parent; public TokenBucket Parent { - get { return parent; } + get { return m_parent; } + set { m_parent = value; } } /// /// Maximum burst rate in bytes per second. This is the maximum number - /// of tokens that can accumulate in the bucket at any one time + /// of tokens that can accumulate in the bucket at any one time. This + /// also sets the total request for leaf nodes /// - public int MaxBurst + private Int64 m_burstRate; + public Int64 RequestedBurstRate { - get { return maxBurst; } - set { maxBurst = (value >= 0 ? value : 0); } + get { return m_burstRate; } + set { m_burstRate = (value < 0 ? 0 : value); } } + public Int64 BurstRate + { + get { + double rate = RequestedBurstRate * BurstRateModifier(); + if (rate < m_minimumDripRate * m_quantumsPerBurst) + rate = m_minimumDripRate * m_quantumsPerBurst; + + return (Int64) rate; + } + } + /// /// The speed limit of this bucket in bytes per second. This is the - /// number of tokens that are added to the bucket per second + /// number of tokens that are added to the bucket per quantum /// /// 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 + private Int64 m_dripRate; + public Int64 RequestedDripRate { - 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; - } + get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } + set { + m_dripRate = (value < 0 ? 0 : value); + m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); + m_totalDripRequest = m_dripRate; + if (m_parent != null) + m_parent.RegisterRequest(this,m_dripRate); } } - /// - /// The speed limit of this bucket in bytes per millisecond - /// - public int DripPerMS + public Int64 DripRate { - get { return tokensPerMS; } + get { + if (m_parent == null) + return Math.Min(RequestedDripRate,TotalDripRequest); + + double rate = (double)RequestedDripRate * m_parent.DripRateModifier(); + if (rate < m_minimumDripRate) + rate = m_minimumDripRate; + + return (Int64)rate; + } } /// - /// 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 + /// The current total of the requested maximum burst rates of + /// this bucket's children buckets. /// - public int Content - { - get { return content; } - } + private Int64 m_totalDripRequest; + public Int64 TotalDripRequest + { + get { return m_totalDripRequest; } + set { m_totalDripRequest = value; } + } + +#endregion Properties - #endregion Properties +#region Constructor /// /// Default constructor @@ -128,56 +169,115 @@ 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, int maxBurst, int dripRate) + public TokenBucket(TokenBucket parent, Int64 dripRate) { - this.parent = parent; - MaxBurst = maxBurst; - DripRate = dripRate; - lastDrip = Environment.TickCount & Int32.MaxValue; + m_identifier = m_counter++; + + Parent = parent; + RequestedDripRate = dripRate; + // TotalDripRequest = dripRate; // this will be overwritten when a child node registers + // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); + m_lastDrip = Environment.TickCount & Int32.MaxValue; } +#endregion Constructor + /// - /// Remove a given number of tokens from the bucket + /// Compute a modifier for the MaxBurst rate. This is 1.0, meaning + /// no modification if the requested bandwidth is less than the + /// max burst bandwidth all the way to the root of the throttle + /// hierarchy. However, if any of the parents is over-booked, then + /// the modifier will be less than 1. /// - /// 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) + private double DripRateModifier() + { + Int64 driprate = DripRate; + return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; + } + + /// + /// + private double BurstRateModifier() + { + // for now... burst rate is always m_quantumsPerBurst (constant) + // larger than drip rate so the ratio of burst requests is the + // same as the drip ratio + return DripRateModifier(); + } + + /// + /// Register drip rate requested by a child of this throttle. Pass the + /// changes up the hierarchy. + /// + public void RegisterRequest(TokenBucket child, Int64 request) { - bool dummy; - return RemoveTokens(amount, out dummy); + m_children[child] = request; + // m_totalDripRequest = m_children.Values.Sum(); + + m_totalDripRequest = 0; + foreach (KeyValuePair cref in m_children) + m_totalDripRequest += cref.Value; + + // Pass the new values up to the parent + if (m_parent != null) + m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); } /// + /// Remove the rate requested by a child of this throttle. Pass the + /// changes up the hierarchy. + /// + public void UnregisterRequest(TokenBucket child) + { + m_children.Remove(child); + // m_totalDripRequest = m_children.Values.Sum(); + + m_totalDripRequest = 0; + foreach (KeyValuePair cref in m_children) + m_totalDripRequest += cref.Value; + + // Pass the new values up to the parent + if (m_parent != null) + m_parent.RegisterRequest(this,Math.Min(RequestedDripRate, TotalDripRequest)); + } + + /// /// 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) + public bool RemoveTokens(Int64 amount) { - if (maxBurst == 0) + // Deposit tokens for this interval + Drip(); + + // If we have enough tokens then remove them and return + if (m_tokenCount - amount >= 0) { - dripSucceeded = true; - return true; + if (m_parent == null || m_parent.RemoveTokens(amount)) + { + m_tokenCount -= amount; + return true; + } } - dripSucceeded = Drip(); + return false; + } - if (content - amount >= 0) - { - if (parent != null && !parent.RemoveTokens(amount)) - return false; + /// + /// Deposit tokens into the bucket from a child bucket that did + /// not use all of its available tokens + /// + private void Deposit(Int64 count) + { + m_tokenCount += count; - content -= amount; - return true; - } - else - { - return false; - } + // Deposit the overflow in the parent bucket, this is how we share + // unused bandwidth + Int64 burstrate = BurstRate; + if (m_tokenCount > burstrate) + m_tokenCount = burstrate; } /// @@ -186,37 +286,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// call to Drip /// /// True if tokens were added to the bucket, otherwise false - public bool Drip() + private void Drip() { - if (tokensPerMS == 0) + // This should never happen... means we are a leaf node and were created + // with no drip rate... + if (DripRate == 0) { - content = maxBurst; - return true; + m_log.WarnFormat("[TOKENBUCKET] something odd is happening and drip rate is 0"); + return; } - else - { - int now = Environment.TickCount & Int32.MaxValue; - int deltaMS = now - lastDrip; - - if (deltaMS <= 0) - { - if (deltaMS < 0) - lastDrip = now; - return false; - } + + // Determine the interval over which we are adding tokens, never add + // more than a single quantum of tokens + Int32 now = Environment.TickCount & Int32.MaxValue; + Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum); - int dripAmount = deltaMS * tokensPerMS; + m_lastDrip = now; - content = Math.Min(content + dripAmount, maxBurst); - lastDrip = now; + // This can be 0 in the very unusual case that the timer wrapped + // It can be 0 if we try add tokens at a sub-tick rate + if (deltaMS <= 0) + return; - if (dripAmount < 0 || content < 0) - // sim has been idle for too long, integer has overflown - // previous calculation is meaningless, let's put it at correct max - content = maxBurst; - - return true; - } + Deposit(deltaMS * DripRate / m_ticksPerQuantum); } } } -- cgit v1.1 From 3fe22126ca3974b2a10365121ee0141fb54806e6 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 12:36:36 -0700 Subject: First pass at moving object property requests into a queue similar to the entity update queue. The number of property packets can become significant when selecting/deselecting large numbers of objects. This is experimental code. --- OpenSim/Client/MXP/ClientStack/MXPClientView.cs | 4 +- .../Client/VWoHTTP/ClientStack/VWHClientView.cs | 4 +- OpenSim/Framework/IClientAPI.cs | 39 +-- .../Region/ClientStack/LindenUDP/LLClientView.cs | 351 +++++++++++++-------- .../Region/ClientStack/LindenUDP/PriorityQueue.cs | 12 +- .../Region/Examples/SimpleModule/MyNpcCharacter.cs | 12 +- .../Region/Framework/Scenes/SceneObjectGroup.cs | 10 +- OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 10 +- .../Server/IRCClientView.cs | 5 +- .../Region/OptionalModules/World/NPC/NPCAvatar.cs | 12 +- OpenSim/Tests/Common/Mock/TestClient.cs | 11 +- 11 files changed, 262 insertions(+), 208 deletions(-) diff --git a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs index d1a0440..a604a2e 100644 --- a/OpenSim/Client/MXP/ClientStack/MXPClientView.cs +++ b/OpenSim/Client/MXP/ClientStack/MXPClientView.cs @@ -1337,12 +1337,12 @@ namespace OpenSim.Client.MXP.ClientStack // Need to translate to MXP somehow } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { //throw new System.NotImplementedException(); } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { //throw new System.NotImplementedException(); } diff --git a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs index fc27f01..d8cd0ac 100644 --- a/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs +++ b/OpenSim/Client/VWoHTTP/ClientStack/VWHClientView.cs @@ -884,12 +884,12 @@ namespace OpenSim.Client.VWoHTTP.ClientStack throw new System.NotImplementedException(); } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { throw new System.NotImplementedException(); } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { throw new System.NotImplementedException(); } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 5bf0b7b..c56a756 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -570,16 +570,30 @@ namespace OpenSim.Framework public float dwell; } - public class EntityUpdate + public class IEntityUpdate { public ISceneEntity Entity; - public PrimUpdateFlags Flags; - public float TimeDilation; + public uint Flags; - public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) + public IEntityUpdate(ISceneEntity entity, uint flags) { Entity = entity; Flags = flags; + } + } + + + public class EntityUpdate : IEntityUpdate + { + // public ISceneEntity Entity; + // public PrimUpdateFlags Flags; + public float TimeDilation; + + public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) + : base(entity,(uint)flags) + { + //Entity = entity; + // Flags = flags; TimeDilation = timedilation; } } @@ -1211,20 +1225,9 @@ namespace OpenSim.Framework /// void SendSimStats(SimStats stats); - void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, - uint Category, - UUID LastOwnerID, string ObjectName, string Description); - - void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, - UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, - string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, - uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice); + void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags); + + void SendObjectPropertiesReply(ISceneEntity Entity); void SendAgentOffline(UUID[] agentIDs); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 5980669..33b9909 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -386,6 +386,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_cachedTextureSerial; private PriorityQueue m_entityUpdates; + private PriorityQueue m_entityProps; private Prioritizer m_prioritizer; private bool m_disableFacelights = false; @@ -433,9 +434,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected IAssetService m_assetService; private const bool m_checkPackets = true; - private Timer m_propertiesPacketTimer; - private List m_propertiesBlocks = new List(); - #endregion Class Members #region Properties @@ -511,6 +509,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene = scene; m_entityUpdates = new PriorityQueue(m_scene.Entities.Count); + m_entityProps = new PriorityQueue(m_scene.Entities.Count); m_fullUpdateDataBlocksBuilder = new List(); m_killRecord = new HashSet(); // m_attachmentsSent = new HashSet(); @@ -534,9 +533,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_udpClient.OnQueueEmpty += HandleQueueEmpty; m_udpClient.OnPacketStats += PopulateStats; - m_propertiesPacketTimer = new Timer(100); - m_propertiesPacketTimer.Elapsed += ProcessObjectPropertiesPacket; - m_prioritizer = new Prioritizer(m_scene); RegisterLocalPacketHandlers(); @@ -3636,9 +3632,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); } - private Int32 m_LastQueueFill = 0; - private uint m_maxUpdates = 0; - private void ProcessEntityUpdates(int maxUpdates) { OpenSim.Framework.Lazy> objectUpdateBlocks = new OpenSim.Framework.Lazy>(); @@ -3646,46 +3639,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.Lazy> terseUpdateBlocks = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy>(); + // Check to see if this is a flush if (maxUpdates <= 0) { - m_maxUpdates = Int32.MaxValue; + maxUpdates = Int32.MaxValue; } - else - { - if (m_maxUpdates == 0 || m_LastQueueFill == 0) - { - m_maxUpdates = (uint)maxUpdates; - } - else - { - if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) - m_maxUpdates += 5; - else - m_maxUpdates = m_maxUpdates >> 1; - } - m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); - } - m_LastQueueFill = Util.EnvironmentTickCount(); - + int updatesThisCall = 0; -// -// DEBUGGING CODE... REMOVE -// LogQueueProcessEvent(this.m_agentId,m_entityUpdates,m_maxUpdates); -// // We must lock for both manipulating the kill record and sending the packet, in order to avoid a race // condition where a kill can be processed before an out-of-date update for the same object. lock (m_killRecord) { float avgTimeDilation = 1.0f; - EntityUpdate update; + IEntityUpdate iupdate; Int32 timeinqueue; // this is just debugging code & can be dropped later - while (updatesThisCall < m_maxUpdates) + while (updatesThisCall < maxUpdates) { lock (m_entityUpdates.SyncRoot) - if (!m_entityUpdates.TryDequeue(out update, out timeinqueue)) + if (!m_entityUpdates.TryDequeue(out iupdate, out timeinqueue)) break; + + EntityUpdate update = (EntityUpdate)iupdate; + avgTimeDilation += update.TimeDilation; avgTimeDilation *= 0.5f; @@ -3725,7 +3702,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region UpdateFlags to packet type conversion - PrimUpdateFlags updateFlags = update.Flags; + PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags; bool canUseCompressed = true; bool canUseImproved = true; @@ -3804,6 +3781,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Block Construction } + #region Packet Sending @@ -3904,12 +3882,36 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Primitive Packet/Data Sending Methods + // These are used to implement an adaptive backoff in the number + // of updates converted to packets. Since we don't want packets + // to sit in the queue with old data, only convert enough updates + // to packets that can be sent in 200ms. + private Int32 m_LastQueueFill = 0; + private Int32 m_maxUpdates = 0; + void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) { if ((categories & ThrottleOutPacketTypeFlags.Task) != 0) { + if (m_maxUpdates == 0 || m_LastQueueFill == 0) + { + m_maxUpdates = m_udpServer.PrimUpdatesPerCallback; + } + else + { + if (Util.EnvironmentTickCountSubtract(m_LastQueueFill) < 200) + m_maxUpdates += 5; + else + m_maxUpdates = m_maxUpdates >> 1; + } + m_maxUpdates = Util.Clamp(m_maxUpdates,10,500); + m_LastQueueFill = Util.EnvironmentTickCount(); + if (m_entityUpdates.Count > 0) - ProcessEntityUpdates(m_udpServer.PrimUpdatesPerCallback); + ProcessEntityUpdates(m_maxUpdates); + + if (m_entityProps.Count > 0) + ProcessEntityPropertyRequests(m_maxUpdates); } if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0) @@ -4023,134 +4025,192 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(pack, ThrottleOutPacketType.Task); } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) - { - ObjectPropertiesFamilyPacket objPropFamilyPack = (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); - // TODO: don't create new blocks if recycling an old packet - - ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); - objPropDB.RequestFlags = RequestFlags; - objPropDB.ObjectID = ObjectUUID; - if (OwnerID == GroupID) - objPropDB.OwnerID = UUID.Zero; - else - objPropDB.OwnerID = OwnerID; - objPropDB.GroupID = GroupID; - objPropDB.BaseMask = BaseMask; - objPropDB.OwnerMask = OwnerMask; - objPropDB.GroupMask = GroupMask; - objPropDB.EveryoneMask = EveryoneMask; - objPropDB.NextOwnerMask = NextOwnerMask; +/// ----------------------------------------------------------------- +/// +/// ----------------------------------------------------------------- - // TODO: More properties are needed in SceneObjectPart! - objPropDB.OwnershipCost = OwnershipCost; - objPropDB.SaleType = SaleType; - objPropDB.SalePrice = SalePrice; - objPropDB.Category = Category; - objPropDB.LastOwnerID = LastOwnerID; - objPropDB.Name = Util.StringToBytes256(ObjectName); - objPropDB.Description = Util.StringToBytes256(Description); - objPropFamilyPack.ObjectData = objPropDB; - objPropFamilyPack.Header.Zerocoded = true; - OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task); - } - - public void SendObjectPropertiesReply( - UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + private class ObjectPropertyUpdate : IEntityUpdate { - //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); - // TODO: don't create new blocks if recycling an old packet + internal bool SendFamilyProps; + + public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam) + : base(entity,flags) + { + SendFamilyProps = sendfam; + } + } + + public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags) + { + uint priority = m_prioritizer.GetUpdatePriority(this, entity); + lock (m_entityProps.SyncRoot) + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true)); + } - ObjectPropertiesPacket.ObjectDataBlock block = - new ObjectPropertiesPacket.ObjectDataBlock(); + public void SendObjectPropertiesReply(ISceneEntity entity) + { + uint priority = m_prioritizer.GetUpdatePriority(this, entity); + lock (m_entityProps.SyncRoot) + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false)); + } - block.ItemID = ItemID; - block.CreationDate = CreationDate; - block.CreatorID = CreatorUUID; - block.FolderID = FolderUUID; - block.FromTaskID = FromTaskUUID; - block.GroupID = GroupUUID; - block.InventorySerial = InventorySerial; + private void ProcessEntityPropertyRequests(int maxUpdates) + { + OpenSim.Framework.Lazy> objectFamilyBlocks = + new OpenSim.Framework.Lazy>(); - block.LastOwnerID = LastOwnerUUID; - // proper.ObjectData[0].LastOwnerID = UUID.Zero; + OpenSim.Framework.Lazy> objectPropertiesBlocks = + new OpenSim.Framework.Lazy>(); - block.ObjectID = ObjectUUID; - if (OwnerUUID == GroupUUID) - block.OwnerID = UUID.Zero; - else - block.OwnerID = OwnerUUID; - block.TouchName = Util.StringToBytes256(TouchTitle); - block.TextureID = TextureID; - block.SitName = Util.StringToBytes256(SitTitle); - block.Name = Util.StringToBytes256(ItemName); - block.Description = Util.StringToBytes256(ItemDescription); - block.OwnerMask = OwnerMask; - block.NextOwnerMask = NextOwnerMask; - block.GroupMask = GroupMask; - block.EveryoneMask = EveryoneMask; - block.BaseMask = BaseMask; - // proper.ObjectData[0].AggregatePerms = 53; - // proper.ObjectData[0].AggregatePermTextures = 0; - // proper.ObjectData[0].AggregatePermTexturesOwner = 0; - block.SaleType = saleType; - block.SalePrice = salePrice; + IEntityUpdate iupdate; + Int32 timeinqueue; // this is just debugging code & can be dropped later - lock (m_propertiesPacketTimer) + int updatesThisCall = 0; + while (updatesThisCall < m_maxUpdates) { - m_propertiesBlocks.Add(block); + lock (m_entityProps.SyncRoot) + if (!m_entityProps.TryDequeue(out iupdate, out timeinqueue)) + break; - int length = 0; - foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) + ObjectPropertyUpdate update = (ObjectPropertyUpdate)iupdate; + if (update.SendFamilyProps) { - length += b.Length; + if (update.Entity is SceneObjectPart) + { + SceneObjectPart sop = (SceneObjectPart)update.Entity; + ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); + objectFamilyBlocks.Value.Add(objPropDB); + } } - if (length > 1100) // FIXME: use real MTU + else { - ProcessObjectPropertiesPacket(null, null); - m_propertiesPacketTimer.Stop(); - return; + if (update.Entity is SceneObjectPart) + { + SceneObjectPart sop = (SceneObjectPart)update.Entity; + ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); + objectPropertiesBlocks.Value.Add(objPropDB); + } } + } + + + if (objectPropertiesBlocks.IsValueCreated) + { + List blocks = objectPropertiesBlocks.Value; - m_propertiesPacketTimer.Stop(); - m_propertiesPacketTimer.Start(); + ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); + packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count]; + for (int i = 0; i < blocks.Count; i++) + packet.ObjectData[i] = blocks[i]; + + packet.Header.Zerocoded = true; + OutPacket(packet, ThrottleOutPacketType.Task, true); } + + + if (objectFamilyBlocks.IsValueCreated) + { + List blocks = objectFamilyBlocks.Value; + + // ObjectPropertiesFamilyPacket objPropFamilyPack = + // (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); + // + // objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count]; + // for (int i = 0; i < blocks.Count; i++) + // objPropFamilyPack.ObjectData[i] = blocks[i]; + // + // OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true); + + // one packet per object block... uggh... + for (int i = 0; i < blocks.Count; i++) + { + ObjectPropertiesFamilyPacket packet = + (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily); - //proper.Header.Zerocoded = true; - //OutPacket(proper, ThrottleOutPacketType.Task); + packet.ObjectData = blocks[i]; + packet.Header.Zerocoded = true; + OutPacket(packet, ThrottleOutPacketType.Task); + } + + } + } - private void ProcessObjectPropertiesPacket(Object sender, ElapsedEventArgs e) + private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) { - ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); + ObjectPropertiesFamilyPacket.ObjectDataBlock block = new ObjectPropertiesFamilyPacket.ObjectDataBlock(); - lock (m_propertiesPacketTimer) - { - m_propertiesPacketTimer.Stop(); + block.RequestFlags = requestFlags; + block.ObjectID = sop.UUID; + if (sop.OwnerID == sop.GroupID) + block.OwnerID = UUID.Zero; + else + block.OwnerID = sop.OwnerID; + block.GroupID = sop.GroupID; + block.BaseMask = sop.BaseMask; + block.OwnerMask = sop.OwnerMask; + block.GroupMask = sop.GroupMask; + block.EveryoneMask = sop.EveryoneMask; + block.NextOwnerMask = sop.NextOwnerMask; - proper.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[m_propertiesBlocks.Count]; + // TODO: More properties are needed in SceneObjectPart! + block.OwnershipCost = sop.OwnershipCost; + block.SaleType = sop.ObjectSaleType; + block.SalePrice = sop.SalePrice; + block.Category = sop.Category; + block.LastOwnerID = sop.CreatorID; // copied from old SOG call... is this right? + block.Name = Util.StringToBytes256(sop.Name); + block.Description = Util.StringToBytes256(sop.Description); - int index = 0; + return block; + } - foreach (ObjectPropertiesPacket.ObjectDataBlock b in m_propertiesBlocks) - { - proper.ObjectData[index++] = b; - } + private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop) + { + //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); + // TODO: don't create new blocks if recycling an old packet - m_propertiesBlocks.Clear(); - } + ObjectPropertiesPacket.ObjectDataBlock block = + new ObjectPropertiesPacket.ObjectDataBlock(); + + block.ObjectID = sop.UUID; + block.Name = Util.StringToBytes256(sop.Name); + block.Description = Util.StringToBytes256(sop.Description); - proper.Header.Zerocoded = true; - OutPacket(proper, ThrottleOutPacketType.Task); + block.CreationDate = (ulong)sop.CreationDate * 1000000; // viewer wants date in microseconds + block.CreatorID = sop.CreatorID; + block.GroupID = sop.GroupID; + block.LastOwnerID = sop.LastOwnerID; + if (sop.OwnerID == sop.GroupID) + block.OwnerID = UUID.Zero; + else + block.OwnerID = sop.OwnerID; + + block.ItemID = sop.FromUserInventoryItemID; + block.FolderID = UUID.Zero; // sop.FromFolderID ?? + block.FromTaskID = UUID.Zero; // ??? + block.InventorySerial = (short)sop.InventorySerial; + + SceneObjectPart root = sop.ParentGroup.RootPart; + + block.TouchName = Util.StringToBytes256(root.TouchName); + block.TextureID = new byte[0]; // TextureID ??? + block.SitName = Util.StringToBytes256(root.SitName); + block.OwnerMask = root.OwnerMask; + block.NextOwnerMask = root.NextOwnerMask; + block.GroupMask = root.GroupMask; + block.EveryoneMask = root.EveryoneMask; + block.BaseMask = root.BaseMask; + block.SaleType = root.ObjectSaleType; + block.SalePrice = root.SalePrice; + + return block; } +/// ----------------------------------------------------------------- +/// +/// ----------------------------------------------------------------- + #region Estate Data Sending Methods private static bool convertParamStringToBool(byte[] field) @@ -4489,6 +4549,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendForceClientSelectObjects(List ObjectIDs) { + m_log.WarnFormat("[LLCLIENTVIEW] sending select with {0} objects", ObjectIDs.Count); + bool firstCall = true; const int MAX_OBJECTS_PER_PACKET = 251; ForceObjectSelectPacket pack = (ForceObjectSelectPacket)PacketPool.Instance.GetPacket(PacketType.ForceObjectSelect); @@ -11381,6 +11443,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); } + if (throttlePacketType == ThrottleOutPacketType.Task) + { + System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); // get call stack + System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) + + string stack = ""; + for (int count = 1; count < stackFrames.Length; count++) + { + stack += (stack == "" ? "" : ",") + stackFrames[count].GetMethod().Name; + if (count > 5) break; + } + + // m_log.WarnFormat("[BADGUY] {0}", stack); + } + m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs index 364ce4b..6521a00 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs @@ -78,7 +78,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public bool Enqueue(uint pqueue, EntityUpdate value) + public bool Enqueue(uint pqueue, IEntityUpdate value) { LookupItem lookup; @@ -99,7 +99,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return true; } - internal bool TryDequeue(out EntityUpdate value, out Int32 timeinqueue) + internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue) { for (int i = 0; i < m_numberOfQueues; ++i) { @@ -122,7 +122,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } timeinqueue = 0; - value = default(EntityUpdate); + value = default(IEntityUpdate); return false; } @@ -175,8 +175,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region MinHeapItem private struct MinHeapItem : IComparable { - private EntityUpdate value; - internal EntityUpdate Value { + private IEntityUpdate value; + internal IEntityUpdate Value { get { return this.value; } @@ -212,7 +212,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP this.pqueue = pqueue; } - internal MinHeapItem(uint pqueue, UInt64 entryorder, EntityUpdate value) + internal MinHeapItem(uint pqueue, UInt64 entryorder, IEntityUpdate value) { this.entrytime = Util.EnvironmentTickCount(); this.entryorder = entryorder; diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index d939329..89e9e20 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -702,18 +702,12 @@ namespace OpenSim.Region.Examples.SimpleModule { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { + } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index ca7d9d9..30563d4 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -1765,10 +1765,12 @@ namespace OpenSim.Region.Framework.Scenes /// public void ServiceObjectPropertiesFamilyRequest(IClientAPI remoteClient, UUID AgentID, uint RequestFlags) { - remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask, - RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask, - RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, - RootPart.CreatorID, RootPart.Name, RootPart.Description); + remoteClient.SendObjectPropertiesFamilyData(RootPart, RequestFlags); + +// remoteClient.SendObjectPropertiesFamilyData(RequestFlags, RootPart.UUID, RootPart.OwnerID, RootPart.GroupID, RootPart.BaseMask, +// RootPart.OwnerMask, RootPart.GroupMask, RootPart.EveryoneMask, RootPart.NextOwnerMask, +// RootPart.OwnershipCost, RootPart.ObjectSaleType, RootPart.SalePrice, RootPart.Category, +// RootPart.CreatorID, RootPart.Name, RootPart.Description); } public void SetPartOwner(SceneObjectPart part, UUID cAgentID, UUID cGroupID) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index ce1e6b8..8a8a699 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2055,15 +2055,7 @@ namespace OpenSim.Region.Framework.Scenes public void GetProperties(IClientAPI client) { - //Viewer wants date in microseconds so multiply it by 1,000,000. - client.SendObjectPropertiesReply( - m_fromUserInventoryItemID, (ulong)_creationDate*(ulong)1e6, _creatorID, UUID.Zero, UUID.Zero, - _groupID, (short)InventorySerial, _lastOwnerID, UUID, _ownerID, - ParentGroup.RootPart.TouchName, new byte[0], ParentGroup.RootPart.SitName, Name, Description, - ParentGroup.RootPart._ownerMask, ParentGroup.RootPart._nextOwnerMask, ParentGroup.RootPart._groupMask, ParentGroup.RootPart._everyoneMask, - ParentGroup.RootPart._baseMask, - ParentGroup.RootPart.ObjectSaleType, - ParentGroup.RootPart.SalePrice); + client.SendObjectPropertiesReply(this); } public UUID GetRootPartUUID() diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 821cd4b..4b6e52e 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -1332,14 +1332,13 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { - } public void SendAgentOffline(UUID[] agentIDs) diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 96760a2..2504e30 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -786,18 +786,12 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType, int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { + } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index d1dc17f..dca5626 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -816,18 +816,11 @@ namespace OpenSim.Tests.Common.Mock { } - public void SendObjectPropertiesFamilyData(uint RequestFlags, UUID ObjectUUID, UUID OwnerID, UUID GroupID, - uint BaseMask, uint OwnerMask, uint GroupMask, uint EveryoneMask, - uint NextOwnerMask, int OwnershipCost, byte SaleType,int SalePrice, uint Category, - UUID LastOwnerID, string ObjectName, string Description) + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) { } - public void SendObjectPropertiesReply(UUID ItemID, ulong CreationDate, UUID CreatorUUID, UUID FolderUUID, UUID FromTaskUUID, - UUID GroupUUID, short InventorySerial, UUID LastOwnerUUID, UUID ObjectUUID, - UUID OwnerUUID, string TouchTitle, byte[] TextureID, string SitTitle, string ItemName, - string ItemDescription, uint OwnerMask, uint NextOwnerMask, uint GroupMask, uint EveryoneMask, - uint BaseMask, byte saleType, int salePrice) + public void SendObjectPropertiesReply(ISceneEntity entity) { } -- cgit v1.1 From 5e7aba4f88a90e266e01b5509c4ba04730815475 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 14:40:57 -0700 Subject: fixed a couple bugs with the property queues --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 38 +++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 33b9909..f926c35 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4091,9 +4091,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP objectPropertiesBlocks.Value.Add(objPropDB); } } + + updatesThisCall++; } + Int32 ppcnt = 0; + Int32 pbcnt = 0; + if (objectPropertiesBlocks.IsValueCreated) { List blocks = objectPropertiesBlocks.Value; @@ -4105,9 +4110,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Header.Zerocoded = true; OutPacket(packet, ThrottleOutPacketType.Task, true); + + pbcnt += blocks.Count; + ppcnt++; } + Int32 fpcnt = 0; + Int32 fbcnt = 0; + if (objectFamilyBlocks.IsValueCreated) { List blocks = objectFamilyBlocks.Value; @@ -4130,10 +4141,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.ObjectData = blocks[i]; packet.Header.Zerocoded = true; OutPacket(packet, ThrottleOutPacketType.Task); + + fpcnt++; + fbcnt++; } } + m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); + m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); } private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) @@ -11408,6 +11424,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, throttlePacketType, true); } +/// + Dictionary pktsrc = new Dictionary(); + uint pktcnt = 0; +/// + /// /// This is the starting point for sending a simulator packet out to the client /// @@ -11455,7 +11476,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (count > 5) break; } - // m_log.WarnFormat("[BADGUY] {0}", stack); + lock(pktsrc) + { + if (! pktsrc.ContainsKey(stack)) + pktsrc.Add(stack,0); + pktsrc[stack]++; + + if (++pktcnt > 500) + { + pktcnt = 0; + m_log.WarnFormat("[PACKETCOUNTS] START"); + foreach (KeyValuePair pkt in pktsrc) + m_log.WarnFormat("[PACKETCOUNTS] {0,8}, {1}", pkt.Value, pkt.Key); + pktsrc.Clear(); + m_log.WarnFormat("[PACKETCOUNTS] END"); + } + } } m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); -- cgit v1.1 From b33aac737a3e7ae28216cb98de99d9c6eba1d563 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 15:40:57 -0700 Subject: Fixed the update of items in the priority queue to enable both types of property updates to be specified. Not sure if one form of property update should supercede another. But for now the old OpenSim behavior is preserved by sending both. --- OpenSim/Framework/IClientAPI.cs | 5 +++++ .../Region/ClientStack/LindenUDP/LLClientView.cs | 21 +++++++++++++++------ .../Region/ClientStack/LindenUDP/PriorityQueue.cs | 2 +- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index c56a756..f573c32 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -575,6 +575,11 @@ namespace OpenSim.Framework public ISceneEntity Entity; public uint Flags; + public virtual void Update(IEntityUpdate update) + { + this.Flags |= update.Flags; + } + public IEntityUpdate(ISceneEntity entity, uint flags) { Entity = entity; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index f926c35..a6f2d09 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -4032,26 +4032,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP private class ObjectPropertyUpdate : IEntityUpdate { internal bool SendFamilyProps; + internal bool SendObjectProps; - public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam) + public ObjectPropertyUpdate(ISceneEntity entity, uint flags, bool sendfam, bool sendobj) : base(entity,flags) { SendFamilyProps = sendfam; + SendObjectProps = sendobj; + } + public void Update(ObjectPropertyUpdate update) + { + SendFamilyProps = SendFamilyProps || update.SendFamilyProps; + SendObjectProps = SendObjectProps || update.SendObjectProps; + Flags |= update.Flags; } } public void SendObjectPropertiesFamilyData(ISceneEntity entity, uint requestFlags) { - uint priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = 0; // time based ordering only lock (m_entityProps.SyncRoot) - m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true)); + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false)); } public void SendObjectPropertiesReply(ISceneEntity entity) { - uint priority = m_prioritizer.GetUpdatePriority(this, entity); + uint priority = 0; // time based ordering only lock (m_entityProps.SyncRoot) - m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false)); + m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,0,false,true)); } private void ProcessEntityPropertyRequests(int maxUpdates) @@ -4082,7 +4090,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP objectFamilyBlocks.Value.Add(objPropDB); } } - else + + if (update.SendObjectProps) { if (update.Entity is SceneObjectPart) { diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs index 6521a00..b62ec07 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs @@ -87,7 +87,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_lookupTable.TryGetValue(localid, out lookup)) { entry = lookup.Heap[lookup.Handle].EntryOrder; - value.Flags |= lookup.Heap[lookup.Handle].Value.Flags; + value.Update(lookup.Heap[lookup.Handle].Value); lookup.Heap.Remove(lookup.Handle); } -- cgit v1.1 From bf91d1b07784f3c03744efd2d6dea1b621620cce Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 12 Apr 2011 15:55:21 -0700 Subject: remove packet monitoring debugging code --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 118 +-------------------- 1 file changed, 2 insertions(+), 116 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index a6f2d09..613a921 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -300,77 +300,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Used to adjust Sun Orbit values so Linden based viewers properly position sun private const float m_sunPainDaHalfOrbitalCutoff = 4.712388980384689858f; - // First log file or time has expired, start writing to a new log file -// -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// THIS IS DEBUGGING CODE & SHOULD BE REMOVED -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- - public class QueueLogger - { - public Int32 start = 0; - public StreamWriter Log = null; - private Dictionary m_idMap = new Dictionary(); - - public QueueLogger() - { - DateTime now = DateTime.Now; - String fname = String.Format("queue-{0}.log", now.ToString("yyyyMMddHHmmss")); - Log = new StreamWriter(fname); - - start = Util.EnvironmentTickCount(); - } - - public int LookupID(UUID uuid) - { - int localid; - if (! m_idMap.TryGetValue(uuid,out localid)) - { - localid = m_idMap.Count + 1; - m_idMap[uuid] = localid; - } - - return localid; - } - } - - public static QueueLogger QueueLog = null; - - // ----------------------------------------------------------------- - public void LogAvatarUpdateEvent(UUID client, UUID avatar, Int32 timeinqueue) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - int aid = QueueLog.LookupID(avatar); - QueueLog.Log.WriteLine("{0},AU,AV{1:D4},AV{2:D4},{3}",ticks,cid,aid,timeinqueue); - } - } - - // ----------------------------------------------------------------- - public void LogQueueProcessEvent(UUID client, PriorityQueue queue, uint maxup) - { - if (QueueLog == null) - QueueLog = new QueueLogger(); - - Int32 ticks = Util.EnvironmentTickCountSubtract(QueueLog.start); - lock(QueueLog) - { - int cid = QueueLog.LookupID(client); - QueueLog.Log.WriteLine("{0},PQ,AV{1:D4},{2},{3}",ticks,cid,maxup,queue.ToString()); - } - } -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// ----------------------------------------------------------------- -// - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); protected static Dictionary PacketHandlers = new Dictionary(); //Global/static handlers for all clients @@ -4025,10 +3954,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(pack, ThrottleOutPacketType.Task); } -/// ----------------------------------------------------------------- -/// -/// ----------------------------------------------------------------- - private class ObjectPropertyUpdate : IEntityUpdate { internal bool SendFamilyProps; @@ -4157,8 +4082,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } - m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); - m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); + // m_log.WarnFormat("[PACKETCOUNTS] queued {0} property packets with {1} blocks",ppcnt,pbcnt); + // m_log.WarnFormat("[PACKETCOUNTS] queued {0} family property packets with {1} blocks",fpcnt,fbcnt); } private ObjectPropertiesFamilyPacket.ObjectDataBlock CreateObjectPropertiesFamilyBlock(SceneObjectPart sop, uint requestFlags) @@ -4232,10 +4157,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return block; } -/// ----------------------------------------------------------------- -/// -/// ----------------------------------------------------------------- - #region Estate Data Sending Methods private static bool convertParamStringToBool(byte[] field) @@ -11433,11 +11354,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, throttlePacketType, true); } -/// - Dictionary pktsrc = new Dictionary(); - uint pktcnt = 0; -/// - /// /// This is the starting point for sending a simulator packet out to the client /// @@ -11472,36 +11388,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (logPacket) m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); } - - if (throttlePacketType == ThrottleOutPacketType.Task) - { - System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace(); // get call stack - System.Diagnostics.StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) - - string stack = ""; - for (int count = 1; count < stackFrames.Length; count++) - { - stack += (stack == "" ? "" : ",") + stackFrames[count].GetMethod().Name; - if (count > 5) break; - } - - lock(pktsrc) - { - if (! pktsrc.ContainsKey(stack)) - pktsrc.Add(stack,0); - pktsrc[stack]++; - - if (++pktcnt > 500) - { - pktcnt = 0; - m_log.WarnFormat("[PACKETCOUNTS] START"); - foreach (KeyValuePair pkt in pktsrc) - m_log.WarnFormat("[PACKETCOUNTS] {0,8}, {1}", pkt.Value, pkt.Key); - pktsrc.Clear(); - m_log.WarnFormat("[PACKETCOUNTS] END"); - } - } - } m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); } -- cgit v1.1 From 3e0e1057acffa2c92a6f949cef0183193033d8c2 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Fri, 15 Apr 2011 16:44:53 -0700 Subject: Remove the call to remove tokens from the parent. Under heavy load this appears to cause problems with the system timer resolution. This caused a problem with tokens going into the root throttle as bursts leading to some starvation. Also changed EnqueueOutgoing to always queue a packet if there are already packets in the queue. Ensures consistent ordering of packet sends. --- OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 10 ++++++++++ OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs | 9 ++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 5a69851..7be8a0a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -440,6 +440,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.LocklessQueue queue = m_packetOutboxes[category]; TokenBucket bucket = m_throttleCategories[category]; + // Don't send this packet if there is already a packet waiting in the queue + // even if we have the tokens to send it, tokens should go to the already + // queued packets + if (queue.Count > 0) + { + queue.Enqueue(packet); + return true; + } + + if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength)) { // Enough tokens were removed from the bucket, the packet will not be queued diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index e4d59ff..07b0a1d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -255,11 +255,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP // If we have enough tokens then remove them and return if (m_tokenCount - amount >= 0) { - if (m_parent == null || m_parent.RemoveTokens(amount)) - { - m_tokenCount -= amount; - return true; - } + // we don't have to remove from the parent, the drip rate is already + // reflective of the drip rate limits in the parent + m_tokenCount -= amount; + return true; } return false; -- cgit v1.1