From 08d8a3e5808b790fbbd7ba3f460603db66aeaff2 Mon Sep 17 00:00:00 2001 From: Dan Lake Date: Mon, 18 Apr 2011 16:48:49 -0700 Subject: Requeue unacknowledged entity updates rather than resend then "as is". Often, by the time the UDPServer realizes that an entity update packet has not been acknowledged, there is a newer update for the same entity already queued up or there is a higher priority update that should be sent first. This patch eliminates 1:1 packet resends for unacked entity update packets. Insteawd, unacked update packets are decomposed into the original entity updates and those updates are placed back into the priority queues based on their new priority but the original update timestamp. This will generally place them at the head of the line to be put back on the wire as a new outgoing packet but prevents the resend queue from filling up with multiple stale updates for the same entity. This new approach takes advantage of the UDP nature of the Linden protocol in that the intent of a reliable update packet is that if it goes unacknowledge, SOMETHING has to happen to get the update to the client. We are simply making sure that we are resending current object state rather than stale object state. Additionally, this patch includes a generalized callback mechanism so that any caller can specify their own method to call when a packet expires without being acknowledged. We use this mechanism to requeue update packets and otherwise use the UDPServer default method of just putting expired packets in the resend queue. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 96 +++++++++++++++++----- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 64 +++++++-------- .../Region/ClientStack/LindenUDP/OutgoingPacket.cs | 6 +- 3 files changed, 112 insertions(+), 54 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 1f7e66d..14c5d6c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3561,6 +3561,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); } + /// + /// Requeue an EntityUpdate when it was not acknowledged by the client. + /// We will update the priority and put it in the correct queue, merging update flags + /// with any other updates that may be queued for the same entity. + /// The original update time is used for the merged update. + /// + public void ResendPrimUpdate(EntityUpdate update) + { + // If the update exists in priority queue, it will be updated. + // If it does not exist then it will be added with the current (rather than its original) priority + uint priority = m_prioritizer.GetUpdatePriority(this, update.Entity); + + lock (m_entityUpdates.SyncRoot) + m_entityUpdates.Enqueue(priority, update); + } + + /// + /// Requeue a list of EntityUpdates when they were not acknowledged by the client. + /// We will update the priority and put it in the correct queue, merging update flags + /// with any other updates that may be queued for the same entity. + /// The original update time is used for the merged update. + /// + void ResendPrimUpdates(List updates) + { + foreach (EntityUpdate update in updates) + ResendPrimUpdate(update); + } + private void ProcessEntityUpdates(int maxUpdates) { OpenSim.Framework.Lazy> objectUpdateBlocks = new OpenSim.Framework.Lazy>(); @@ -3568,6 +3596,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.Lazy> terseUpdateBlocks = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy>(); + OpenSim.Framework.Lazy> objectUpdates = new OpenSim.Framework.Lazy>(); + OpenSim.Framework.Lazy> compressedUpdates = new OpenSim.Framework.Lazy>(); + OpenSim.Framework.Lazy> terseUpdates = new OpenSim.Framework.Lazy>(); + OpenSim.Framework.Lazy> terseAgentUpdates = new OpenSim.Framework.Lazy>(); + // Check to see if this is a flush if (maxUpdates <= 0) { @@ -3583,7 +3616,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP float avgTimeDilation = 1.0f; IEntityUpdate iupdate; Int32 timeinqueue; // this is just debugging code & can be dropped later - + while (updatesThisCall < maxUpdates) { lock (m_entityUpdates.SyncRoot) @@ -3688,24 +3721,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (update.Entity is ScenePresence) { objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity)); + objectUpdates.Value.Add(update); } else { objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId)); + objectUpdates.Value.Add(update); } } else if (!canUseImproved) { compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags)); + compressedUpdates.Value.Add(update); } else { if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId) + { // Self updates go into a special list terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); + terseAgentUpdates.Value.Add(update); + } else + { // Everything else goes here terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); + terseUpdates.Value.Add(update); + } } #endregion Block Construction @@ -3713,28 +3755,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Packet Sending - - //const float TIME_DILATION = 1.0f; - - ushort timeDilation = Utils.FloatToUInt16(avgTimeDilation, 0.0f, 1.0f); - + if (terseAgentUpdateBlocks.IsValueCreated) { List blocks = terseAgentUpdateBlocks.Value; - + ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; packet.RegionData.TimeDilation = timeDilation; packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; - + for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; - - - OutPacket(packet, ThrottleOutPacketType.Unknown, true); + // If any of the packets created from this call go unacknowledged, all of the updates will be resent + OutPacket(packet, ThrottleOutPacketType.Unknown, true, delegate() { ResendPrimUpdates(terseAgentUpdates.Value); }); } - + if (objectUpdateBlocks.IsValueCreated) { List blocks = objectUpdateBlocks.Value; @@ -3746,8 +3783,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; - - OutPacket(packet, ThrottleOutPacketType.Task, true); + // If any of the packets created from this call go unacknowledged, all of the updates will be resent + OutPacket(packet, ThrottleOutPacketType.Task, true, delegate() { ResendPrimUpdates(objectUpdates.Value); }); } if (compressedUpdateBlocks.IsValueCreated) @@ -3761,10 +3798,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; - - OutPacket(packet, ThrottleOutPacketType.Task, true); + // If any of the packets created from this call go unacknowledged, all of the updates will be resent + OutPacket(packet, ThrottleOutPacketType.Task, true, delegate() { ResendPrimUpdates(compressedUpdates.Value); }); } - + if (terseUpdateBlocks.IsValueCreated) { List blocks = terseUpdateBlocks.Value; @@ -3776,8 +3813,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; - - OutPacket(packet, ThrottleOutPacketType.Task, true); + // If any of the packets created from this call go unacknowledged, all of the updates will be resent + OutPacket(packet, ThrottleOutPacketType.Task, true, delegate() { ResendPrimUpdates(terseUpdates.Value); }); } } @@ -3969,7 +4006,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP { SendFamilyProps = SendFamilyProps || update.SendFamilyProps; SendObjectProps = SendObjectProps || update.SendObjectProps; - Flags |= update.Flags; + // other properties may need to be updated by base class + base.Update(update); } } @@ -11363,6 +11401,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// handles splitting manually protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting) { + OutPacket(packet, throttlePacketType, doAutomaticSplitting, null); + } + + /// + /// This is the starting point for sending a simulator packet out to the client + /// + /// Packet to send + /// Throttling category for the packet + /// True to automatically split oversized + /// packets (the default), or false to disable splitting if the calling code + /// handles splitting manually + /// The method to be called in the event this packet is reliable + /// and unacknowledged. The server will provide normal resend capability if you do not + /// provide your own method. + protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting, UnackedPacketMethod method) + { if (m_debugPacketLevel > 0) { bool logPacket = true; @@ -11388,7 +11442,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); } - m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); + m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting, method); } public bool AddMoney(int debit) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index d08b25f..0848979 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -297,7 +297,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP delegate(IClientAPI client) { if (client is LLClientView) - SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); + SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null); } ); } @@ -309,7 +309,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP delegate(IClientAPI client) { if (client is LLClientView) - SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); + SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null); } ); } @@ -322,7 +322,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// /// - public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting) + public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting, UnackedPacketMethod method) { // CoarseLocationUpdate packets cannot be split in an automated way if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) @@ -339,13 +339,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < packetCount; i++) { byte[] data = datas[i]; - SendPacketData(udpClient, data, packet.Type, category); + SendPacketData(udpClient, data, packet.Type, category, method); } } else { byte[] data = packet.ToBytes(); - SendPacketData(udpClient, data, packet.Type, category); + SendPacketData(udpClient, data, packet.Type, category, method); } } @@ -356,7 +356,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// /// - public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category) + public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category, UnackedPacketMethod method) { int dataLength = data.Length; bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; @@ -411,7 +411,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Queue or Send - OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); + OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, null); + // If we were not provided a method for handling unacked, use the UDPServer default method + outgoingPacket.UnackedMethod = ((method == null) ? delegate() { ResendUnacked(outgoingPacket); } : method); // If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will // continue to display the deleted object until relog. Therefore, we need to always queue a kill object @@ -445,7 +447,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Header.Reliable = false; packet.Packets = blocks.ToArray(); - SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true); + SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true, null); } } @@ -458,17 +460,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit pc.PingID.OldestUnacked = 0; - SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false); + SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false, null); } public void CompletePing(LLUDPClient udpClient, byte pingID) { CompletePingCheckPacket completePing = new CompletePingCheckPacket(); completePing.PingID.PingID = pingID; - SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false); + SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false, null); } - public void ResendUnacked(LLUDPClient udpClient) + public void HandleUnacked(LLUDPClient udpClient) { if (!udpClient.IsConnected) return; @@ -488,31 +490,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (expiredPackets != null) { - //m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO); - + //m_log.Debug("[LLUDPSERVER]: Handling " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO); // Exponential backoff of the retransmission timeout udpClient.BackoffRTO(); + for (int i = 0; i < expiredPackets.Count; ++i) + expiredPackets[i].UnackedMethod(); + } + } - // Resend packets - for (int i = 0; i < expiredPackets.Count; i++) - { - OutgoingPacket outgoingPacket = expiredPackets[i]; - - //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed", - // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount); + public void ResendUnacked(OutgoingPacket outgoingPacket) + { + //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed", + // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount); - // Set the resent flag - outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); - outgoingPacket.Category = ThrottleOutPacketType.Resend; + // Set the resent flag + outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); + outgoingPacket.Category = ThrottleOutPacketType.Resend; - // Bump up the resend count on this packet - Interlocked.Increment(ref outgoingPacket.ResendCount); + // Bump up the resend count on this packet + Interlocked.Increment(ref outgoingPacket.ResendCount); - // Requeue or resend the packet - if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, false)) - SendPacketFinal(outgoingPacket); - } - } + // Requeue or resend the packet + if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, false)) + SendPacketFinal(outgoingPacket); } public void Flush(LLUDPClient udpClient) @@ -1096,7 +1096,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (udpClient.IsConnected) { if (m_resendUnacked) - ResendUnacked(udpClient); + HandleUnacked(udpClient); if (m_sendAcks) SendAcks(udpClient); @@ -1152,7 +1152,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP nticksUnack++; watch2.Start(); - ResendUnacked(udpClient); + HandleUnacked(udpClient); watch2.Stop(); avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack); diff --git a/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs index 1a1a1cb..f4f024b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs @@ -31,6 +31,7 @@ using OpenMetaverse; namespace OpenSim.Region.ClientStack.LindenUDP { + public delegate void UnackedPacketMethod(); /// /// Holds a reference to the this packet is /// destined for, along with the serialized packet data, sequence number @@ -52,6 +53,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public int TickCount; /// Category this packet belongs to public ThrottleOutPacketType Category; + /// The delegate to be called if this packet is determined to be unacknowledged + public UnackedPacketMethod UnackedMethod; /// /// Default constructor @@ -60,11 +63,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Serialized packet data. If the flags or sequence number /// need to be updated, they will be injected directly into this binary buffer /// Throttling category for this packet - public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category) + public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category, UnackedPacketMethod method) { Client = client; Buffer = buffer; Category = category; + UnackedMethod = method; } } } -- cgit v1.1 From 82de87ce99a513debabe16af41fcb2fda3db0484 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Tue, 19 Apr 2011 11:22:04 -0700 Subject: Converted the property request queue to use the same retransmission mechanism as the entity update queues. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 75 +++++++++++++++------- 1 file changed, 52 insertions(+), 23 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 14c5d6c..87b86eb 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3567,7 +3567,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// with any other updates that may be queued for the same entity. /// The original update time is used for the merged update. /// - public void ResendPrimUpdate(EntityUpdate update) + private void ResendPrimUpdate(EntityUpdate update) { // If the update exists in priority queue, it will be updated. // If it does not exist then it will be added with the current (rather than its original) priority @@ -3583,7 +3583,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// with any other updates that may be queued for the same entity. /// The original update time is used for the merged update. /// - void ResendPrimUpdates(List updates) + private void ResendPrimUpdates(List updates) { foreach (EntityUpdate update in updates) ResendPrimUpdate(update); @@ -4018,6 +4018,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false)); } + private void ResendPropertyUpdate(ObjectPropertyUpdate update) + { + uint priority = 0; + lock (m_entityProps.SyncRoot) + m_entityProps.Enqueue(priority, update); + } + + private void ResendPropertyUpdates(List updates) + { + foreach (ObjectPropertyUpdate update in updates) + ResendPropertyUpdate(update); + } + public void SendObjectPropertiesReply(ISceneEntity entity) { uint priority = 0; // time based ordering only @@ -4033,6 +4046,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.Lazy> objectPropertiesBlocks = new OpenSim.Framework.Lazy>(); + OpenSim.Framework.Lazy> familyUpdates = + new OpenSim.Framework.Lazy>(); + + OpenSim.Framework.Lazy> propertyUpdates = + new OpenSim.Framework.Lazy>(); + IEntityUpdate iupdate; Int32 timeinqueue; // this is just debugging code & can be dropped later @@ -4051,6 +4070,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP SceneObjectPart sop = (SceneObjectPart)update.Entity; ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); objectFamilyBlocks.Value.Add(objPropDB); + familyUpdates.Value.Add(update); } } @@ -4061,6 +4081,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP SceneObjectPart sop = (SceneObjectPart)update.Entity; ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); objectPropertiesBlocks.Value.Add(objPropDB); + propertyUpdates.Value.Add(update); } } @@ -4068,12 +4089,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP } - Int32 ppcnt = 0; - Int32 pbcnt = 0; + // Int32 ppcnt = 0; + // Int32 pbcnt = 0; if (objectPropertiesBlocks.IsValueCreated) { List blocks = objectPropertiesBlocks.Value; + List updates = propertyUpdates.Value; ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count]; @@ -4081,28 +4103,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.ObjectData[i] = blocks[i]; packet.Header.Zerocoded = true; - OutPacket(packet, ThrottleOutPacketType.Task, true); - pbcnt += blocks.Count; - ppcnt++; + // Pass in the delegate so that if this packet needs to be resent, we send the current properties + // of the object rather than the properties when the packet was created + OutPacket(packet, ThrottleOutPacketType.Task, true, + delegate() + { + ResendPropertyUpdates(updates); + }); + + // pbcnt += blocks.Count; + // ppcnt++; } - Int32 fpcnt = 0; - Int32 fbcnt = 0; + // Int32 fpcnt = 0; + // Int32 fbcnt = 0; 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); - + List updates = familyUpdates.Value; + // one packet per object block... uggh... for (int i = 0; i < blocks.Count; i++) { @@ -4111,10 +4132,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.ObjectData = blocks[i]; packet.Header.Zerocoded = true; - OutPacket(packet, ThrottleOutPacketType.Task); - fpcnt++; - fbcnt++; + // Pass in the delegate so that if this packet needs to be resent, we send the current properties + // of the object rather than the properties when the packet was created + ObjectPropertyUpdate update = updates[i]; + OutPacket(packet, ThrottleOutPacketType.Task, true, + delegate() + { + ResendPropertyUpdate(update); + }); + + // fpcnt++; + // fbcnt++; } } @@ -4151,7 +4180,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return block; } - + private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop) { //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); -- cgit v1.1 From 2b737c9cc2e4a4ef3520d80225381a010bd1dc80 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Wed, 20 Apr 2011 16:23:33 -0700 Subject: Adds the first pass at an adaptive throttle to slow start new clients. If the sent packets are ack'ed successfully the throttle will open quickly up to the maximum specified by the client and/or the sims client throttle. This still needs a lot of adjustment to get the rates correct. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 4 + .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 9 ++- .../Region/ClientStack/LindenUDP/TokenBucket.cs | 92 ++++++++++++++++++---- .../LindenUDP/UnackedPacketCollection.cs | 8 ++ 4 files changed, 95 insertions(+), 18 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 87b86eb..1108863 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3585,6 +3585,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// private void ResendPrimUpdates(List updates) { + // m_log.WarnFormat("[CLIENT] resending prim update {0}",updates[0].UpdateTime); + foreach (EntityUpdate update in updates) ResendPrimUpdate(update); } @@ -4027,6 +4029,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void ResendPropertyUpdates(List updates) { + // m_log.WarnFormat("[CLIENT] resending object property {0}",updates[0].UpdateTime); + foreach (ObjectPropertyUpdate update in updates) ResendPropertyUpdate(update); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 7be8a0a..20bfec8 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -135,7 +135,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_nextOnQueueEmpty = 1; /// Throttle bucket for this agent's connection - private readonly TokenBucket m_throttleClient; + private readonly AdaptiveTokenBucket m_throttleClient; + public AdaptiveTokenBucket FlowThrottle + { + get { return m_throttleClient; } + } + /// Throttle bucket for this agent's connection private readonly TokenBucket m_throttleCategory; /// Throttle buckets for each packet category @@ -176,7 +181,7 @@ 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_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit); + m_throttleClient = new AdaptiveTokenBucket(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 diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 07b0a1d..4ee6d3a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -48,31 +48,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Number of ticks (ms) per quantum, drip rate and max burst /// are defined over this interval. /// - private const Int32 m_ticksPerQuantum = 1000; + protected 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; + protected const Double m_quantumsPerBurst = 1.5; /// /// - private const Int32 m_minimumDripRate = 1400; + protected const Int32 m_minimumDripRate = 1400; /// Time of the last drip, in system ticks - private Int32 m_lastDrip; + protected 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; + protected Int64 m_tokenCount; /// /// Map of children buckets and their requested maximum burst rate /// - private Dictionary m_children = new Dictionary(); + protected Dictionary m_children = new Dictionary(); #region Properties @@ -81,7 +81,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// parent. The parent bucket will limit the aggregate bandwidth of all /// of its children buckets /// - private TokenBucket m_parent; + protected TokenBucket m_parent; public TokenBucket Parent { get { return m_parent; } @@ -93,7 +93,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// of tokens that can accumulate in the bucket at any one time. This /// also sets the total request for leaf nodes /// - private Int64 m_burstRate; + protected Int64 m_burstRate; public Int64 RequestedBurstRate { get { return m_burstRate; } @@ -118,8 +118,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Tokens are added to the bucket any time /// is called, at the granularity of /// the system tick interval (typically around 15-22ms) - private Int64 m_dripRate; - public Int64 RequestedDripRate + protected Int64 m_dripRate; + public virtual Int64 RequestedDripRate { get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } set { @@ -131,7 +131,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public Int64 DripRate + public virtual Int64 DripRate { get { if (m_parent == null) @@ -149,7 +149,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// The current total of the requested maximum burst rates of /// this bucket's children buckets. /// - private Int64 m_totalDripRequest; + protected Int64 m_totalDripRequest; public Int64 TotalDripRequest { get { return m_totalDripRequest; } @@ -189,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// hierarchy. However, if any of the parents is over-booked, then /// the modifier will be less than 1. /// - private double DripRateModifier() + protected double DripRateModifier() { Int64 driprate = DripRate; return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; @@ -197,7 +197,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// - private double BurstRateModifier() + protected double BurstRateModifier() { // for now... burst rate is always m_quantumsPerBurst (constant) // larger than drip rate so the ratio of burst requests is the @@ -268,7 +268,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Deposit tokens into the bucket from a child bucket that did /// not use all of its available tokens /// - private void Deposit(Int64 count) + protected void Deposit(Int64 count) { m_tokenCount += count; @@ -285,7 +285,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// call to Drip /// /// True if tokens were added to the bucket, otherwise false - private void Drip() + protected void Drip() { // This should never happen... means we are a leaf node and were created // with no drip rate... @@ -310,4 +310,64 @@ namespace OpenSim.Region.ClientStack.LindenUDP Deposit(deltaMS * DripRate / m_ticksPerQuantum); } } + + public class AdaptiveTokenBucket : TokenBucket + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // + // The minimum rate for flow control. + // + protected const Int64 m_minimumFlow = m_minimumDripRate * 10; + + // + // The maximum rate for flow control. Drip rate can never be + // greater than this. + // + protected Int64 m_maxDripRate = 0; + protected Int64 MaxDripRate + { + get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); } + set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); } + } + + // + // + // + public virtual Int64 AdjustedDripRate + { + get { return m_dripRate; } + set { + m_dripRate = OpenSim.Framework.Util.Clamp(value,m_minimumFlow,MaxDripRate); + m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); + if (m_parent != null) + m_parent.RegisterRequest(this,m_dripRate); + } + } + + // + // + // + public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate) : base(parent,m_minimumFlow) + { + MaxDripRate = maxDripRate; + } + + // + // + // + public void ExpirePackets(Int32 count) + { + // m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count); + AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count)); + } + + // + // + // + public void AcknowledgePackets(Int32 count) + { + AdjustedDripRate = AdjustedDripRate + count; + } + } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index d195110..b170964 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -130,6 +130,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP // is actually sent out again packet.TickCount = 0; + // As with other network applications, assume that an expired packet is + // an indication of some network problem, slow transmission + packet.Client.FlowThrottle.ExpirePackets(1); + expiredPackets.Add(packet); } } @@ -157,6 +161,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP { m_packets.Remove(pendingRemove.SequenceNumber); + // As with other network applications, assume that an acknowledged packet is an + // indication that the network can handle a little more load, speed up the transmission + ackedPacket.Client.FlowThrottle.AcknowledgePackets(ackedPacket.Buffer.DataLength); + // Update stats Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); -- cgit v1.1 From 7759bda833c03f4c29500dce32b835a7aef8285b Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Wed, 20 Apr 2011 21:58:49 -0700 Subject: Added an "immediate" queue to the priority queue. This is per Melanie's very good suggestion. The immediate queue is serviced completely before all others, making it a very good place to put avatar updates & attachments. Moved the priority queue out of the LLUDP directory and into the framework. It is now a fairly general utility. --- .../Region/ClientStack/LindenUDP/PriorityQueue.cs | 245 --------------------- 1 file changed, 245 deletions(-) delete mode 100644 OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs deleted file mode 100644 index b62ec07..0000000 --- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; - -using OpenSim.Framework; -using OpenSim.Framework.Client; -using log4net; - -namespace OpenSim.Region.ClientStack.LindenUDP -{ - public class PriorityQueue - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - internal delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity); - - // Heap[0] for self updates - // Heap[1..12] for entity updates - - internal const uint m_numberOfQueues = 12; - - private MinHeap[] m_heaps = new MinHeap[m_numberOfQueues]; - private Dictionary m_lookupTable; - private uint m_nextQueue = 0; - private UInt64 m_nextRequest = 0; - - private object m_syncRoot = new object(); - public object SyncRoot { - get { return this.m_syncRoot; } - } - - internal PriorityQueue() : this(MinHeap.DEFAULT_CAPACITY) { } - - internal PriorityQueue(int capacity) - { - m_lookupTable = new Dictionary(capacity); - - for (int i = 0; i < m_heaps.Length; ++i) - m_heaps[i] = new MinHeap(capacity); - } - - internal int Count - { - get - { - int count = 0; - for (int i = 0; i < m_heaps.Length; ++i) - count += m_heaps[i].Count; - return count; - } - } - - public bool Enqueue(uint pqueue, IEntityUpdate value) - { - LookupItem lookup; - - uint localid = value.Entity.LocalId; - UInt64 entry = m_nextRequest++; - if (m_lookupTable.TryGetValue(localid, out lookup)) - { - entry = lookup.Heap[lookup.Handle].EntryOrder; - value.Update(lookup.Heap[lookup.Handle].Value); - lookup.Heap.Remove(lookup.Handle); - } - - pqueue = Util.Clamp(pqueue, 0, m_numberOfQueues - 1); - lookup.Heap = m_heaps[pqueue]; - lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle); - m_lookupTable[localid] = lookup; - - return true; - } - - internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue) - { - for (int i = 0; i < m_numberOfQueues; ++i) - { - // To get the fair queing, we cycle through each of the - // queues when finding an element to dequeue, this code - // assumes that the distribution of updates in the queues - // is polynomial, probably quadractic (eg distance of PI * R^2) - uint h = (uint)((m_nextQueue + i) % m_numberOfQueues); - if (m_heaps[h].Count > 0) - { - m_nextQueue = (uint)((h + 1) % m_numberOfQueues); - - MinHeapItem item = m_heaps[h].RemoveMin(); - m_lookupTable.Remove(item.Value.Entity.LocalId); - timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime); - value = item.Value; - - return true; - } - } - - timeinqueue = 0; - value = default(IEntityUpdate); - return false; - } - - internal void Reprioritize(UpdatePriorityHandler handler) - { - MinHeapItem item; - foreach (LookupItem lookup in new List(this.m_lookupTable.Values)) - { - if (lookup.Heap.TryGetValue(lookup.Handle, out item)) - { - uint pqueue = item.PriorityQueue; - uint localid = item.Value.Entity.LocalId; - - if (handler(ref pqueue, item.Value.Entity)) - { - // unless the priority queue has changed, there is no need to modify - // the entry - pqueue = Util.Clamp(pqueue, 0, m_numberOfQueues - 1); - if (pqueue != item.PriorityQueue) - { - lookup.Heap.Remove(lookup.Handle); - - LookupItem litem = lookup; - litem.Heap = m_heaps[pqueue]; - litem.Heap.Add(new MinHeapItem(pqueue, item), ref litem.Handle); - m_lookupTable[localid] = litem; - } - } - else - { - // m_log.WarnFormat("[PQUEUE]: UpdatePriorityHandler returned false for {0}",item.Value.Entity.UUID); - lookup.Heap.Remove(lookup.Handle); - this.m_lookupTable.Remove(localid); - } - } - } - } - - public override string ToString() - { - string s = ""; - for (int i = 0; i < m_numberOfQueues; i++) - { - if (s != "") s += ","; - s += m_heaps[i].Count.ToString(); - } - return s; - } - -#region MinHeapItem - private struct MinHeapItem : IComparable - { - private IEntityUpdate value; - internal IEntityUpdate Value { - get { - return this.value; - } - } - - private uint pqueue; - internal uint PriorityQueue { - get { - return this.pqueue; - } - } - - private Int32 entrytime; - internal Int32 EntryTime { - get { - return this.entrytime; - } - } - - private UInt64 entryorder; - internal UInt64 EntryOrder - { - get { - return this.entryorder; - } - } - - internal MinHeapItem(uint pqueue, MinHeapItem other) - { - this.entrytime = other.entrytime; - this.entryorder = other.entryorder; - this.value = other.value; - this.pqueue = pqueue; - } - - internal MinHeapItem(uint pqueue, UInt64 entryorder, IEntityUpdate value) - { - this.entrytime = Util.EnvironmentTickCount(); - this.entryorder = entryorder; - this.value = value; - this.pqueue = pqueue; - } - - public override string ToString() - { - return String.Format("[{0},{1},{2}]",pqueue,entryorder,value.Entity.LocalId); - } - - public int CompareTo(MinHeapItem other) - { - // I'm assuming that the root part of an SOG is added to the update queue - // before the component parts - return Comparer.Default.Compare(this.EntryOrder, other.EntryOrder); - } - } -#endregion - -#region LookupItem - private struct LookupItem - { - internal MinHeap Heap; - internal IHandle Handle; - } -#endregion - } -} -- cgit v1.1 From b5ab33b5e190e79789df3f44b24959fd229a8c6d Mon Sep 17 00:00:00 2001 From: Dan Lake Date: Wed, 20 Apr 2011 23:08:51 -0700 Subject: bug fix. Now when an unacked update packet is handled through ResendPrimUpdates, it is removed from the UnackedPacketCollection. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 34 +++++++++++++--------- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 4 +-- .../Region/ClientStack/LindenUDP/OutgoingPacket.cs | 3 +- 3 files changed, 25 insertions(+), 16 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 1108863..1e8bbb8 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3583,10 +3583,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// with any other updates that may be queued for the same entity. /// The original update time is used for the merged update. /// - private void ResendPrimUpdates(List updates) + private void ResendPrimUpdates(List updates, OutgoingPacket oPacket) { // m_log.WarnFormat("[CLIENT] resending prim update {0}",updates[0].UpdateTime); - + + // Remove the update packet from the list of packets waiting for acknowledgement + // because we are requeuing the list of updates. They will be resent in new packets + // with the most recent state and priority. + m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber, 0, true); foreach (EntityUpdate update in updates) ResendPrimUpdate(update); } @@ -3771,7 +3775,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; // If any of the packets created from this call go unacknowledged, all of the updates will be resent - OutPacket(packet, ThrottleOutPacketType.Unknown, true, delegate() { ResendPrimUpdates(terseAgentUpdates.Value); }); + OutPacket(packet, ThrottleOutPacketType.Unknown, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseAgentUpdates.Value, oPacket); }); } if (objectUpdateBlocks.IsValueCreated) @@ -3786,7 +3790,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; // If any of the packets created from this call go unacknowledged, all of the updates will be resent - OutPacket(packet, ThrottleOutPacketType.Task, true, delegate() { ResendPrimUpdates(objectUpdates.Value); }); + OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(objectUpdates.Value, oPacket); }); } if (compressedUpdateBlocks.IsValueCreated) @@ -3801,7 +3805,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; // If any of the packets created from this call go unacknowledged, all of the updates will be resent - OutPacket(packet, ThrottleOutPacketType.Task, true, delegate() { ResendPrimUpdates(compressedUpdates.Value); }); + OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(compressedUpdates.Value, oPacket); }); } if (terseUpdateBlocks.IsValueCreated) @@ -3816,7 +3820,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < blocks.Count; i++) packet.ObjectData[i] = blocks[i]; // If any of the packets created from this call go unacknowledged, all of the updates will be resent - OutPacket(packet, ThrottleOutPacketType.Task, true, delegate() { ResendPrimUpdates(terseUpdates.Value); }); + OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseUpdates.Value, oPacket); }); } } @@ -4027,10 +4031,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_entityProps.Enqueue(priority, update); } - private void ResendPropertyUpdates(List updates) + private void ResendPropertyUpdates(List updates, OutgoingPacket oPacket) { // m_log.WarnFormat("[CLIENT] resending object property {0}",updates[0].UpdateTime); + // Remove the update packet from the list of packets waiting for acknowledgement + // because we are requeuing the list of updates. They will be resent in new packets + // with the most recent state. + m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber, 0, true); foreach (ObjectPropertyUpdate update in updates) ResendPropertyUpdate(update); } @@ -4111,9 +4119,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Pass in the delegate so that if this packet needs to be resent, we send the current properties // of the object rather than the properties when the packet was created OutPacket(packet, ThrottleOutPacketType.Task, true, - delegate() + delegate(OutgoingPacket oPacket) { - ResendPropertyUpdates(updates); + ResendPropertyUpdates(updates, oPacket); }); // pbcnt += blocks.Count; @@ -4126,7 +4134,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (objectFamilyBlocks.IsValueCreated) { List blocks = objectFamilyBlocks.Value; - List updates = familyUpdates.Value; // one packet per object block... uggh... for (int i = 0; i < blocks.Count; i++) @@ -4139,11 +4146,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Pass in the delegate so that if this packet needs to be resent, we send the current properties // of the object rather than the properties when the packet was created - ObjectPropertyUpdate update = updates[i]; + List updates = new List(); + updates.Add(familyUpdates.Value[i]); OutPacket(packet, ThrottleOutPacketType.Task, true, - delegate() + delegate(OutgoingPacket oPacket) { - ResendPropertyUpdate(update); + ResendPropertyUpdates(updates, oPacket); }); // fpcnt++; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 0848979..bd58ddc 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -413,7 +413,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, null); // If we were not provided a method for handling unacked, use the UDPServer default method - outgoingPacket.UnackedMethod = ((method == null) ? delegate() { ResendUnacked(outgoingPacket); } : method); + outgoingPacket.UnackedMethod = ((method == null) ? delegate(OutgoingPacket oPacket) { ResendUnacked(oPacket); } : method); // If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will // continue to display the deleted object until relog. Therefore, we need to always queue a kill object @@ -494,7 +494,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Exponential backoff of the retransmission timeout udpClient.BackoffRTO(); for (int i = 0; i < expiredPackets.Count; ++i) - expiredPackets[i].UnackedMethod(); + expiredPackets[i].UnackedMethod(expiredPackets[i]); } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs index f4f024b..76c6c14 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs @@ -31,7 +31,8 @@ using OpenMetaverse; namespace OpenSim.Region.ClientStack.LindenUDP { - public delegate void UnackedPacketMethod(); + + public delegate void UnackedPacketMethod(OutgoingPacket oPacket); /// /// Holds a reference to the this packet is /// destined for, along with the serialized packet data, sequence number -- cgit v1.1 From 3640d0204f77dff3b1c4bd50229b60a49d2745e5 Mon Sep 17 00:00:00 2001 From: Dan Lake Date: Thu, 21 Apr 2011 01:51:08 -0700 Subject: Added ability to remove unacked packet from UnackedPacketCollection without an acknowledgement from the network. This prevents RTT and throttles from being updated as they would when an ACK is actually received. Also fixed stats logging for unacked bytes and resent packets in this case. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 12 ++++++++-- .../LindenUDP/UnackedPacketCollection.cs | 28 ++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 1e8bbb8..6129e10 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -3590,7 +3590,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Remove the update packet from the list of packets waiting for acknowledgement // because we are requeuing the list of updates. They will be resent in new packets // with the most recent state and priority. - m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber, 0, true); + m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber); + + // Count this as a resent packet since we are going to requeue all of the updates contained in it + Interlocked.Increment(ref m_udpClient.PacketsResent); + foreach (EntityUpdate update in updates) ResendPrimUpdate(update); } @@ -4038,7 +4042,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Remove the update packet from the list of packets waiting for acknowledgement // because we are requeuing the list of updates. They will be resent in new packets // with the most recent state. - m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber, 0, true); + m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber); + + // Count this as a resent packet since we are going to requeue all of the updates contained in it + Interlocked.Increment(ref m_udpClient.PacketsResent); + foreach (ObjectPropertyUpdate update in updates) ResendPropertyUpdate(update); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index b170964..90a87fa 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -83,6 +83,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// Marks a packet as acknowledged + /// This method is used when an acknowledgement is received from the network for a previously + /// sent packet. Effects of removal this way are to update unacked byte count, adjust RTT + /// and increase throttle to the coresponding client. /// /// Sequence number of the packet to /// acknowledge @@ -95,6 +98,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP } /// + /// Marks a packet as no longer needing acknowledgement without a received acknowledgement. + /// This method is called when a packet expires and we no longer need an acknowledgement. + /// When some reliable packet types expire, they are handled in a way other than simply + /// resending them. The only effect of removal this way is to update unacked byte count. + /// + /// Sequence number of the packet to + /// acknowledge + /// The packet is removed from the collection immediately. + /// This function is not threadsafe. It must be called by the thread calling GetExpiredPackets. + public void Remove(uint sequenceNumber) + { + OutgoingPacket removedPacket; + if (m_packets.TryGetValue(sequenceNumber, out removedPacket)) + { + if (removedPacket != null) + { + m_packets.Remove(sequenceNumber); + + // Update stats + Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength); + } + } + } + + /// /// Returns a list of all of the packets with a TickCount older than /// the specified timeout /// -- cgit v1.1 From 7f28dd4b3195e020eed51fa1229083123935125b Mon Sep 17 00:00:00 2001 From: Dan Lake Date: Thu, 21 Apr 2011 15:40:32 -0700 Subject: Refactor UnackedPacketCollection so ProcessQueues will handle Adds, Acks, and Removes in that order. --- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 4 +- .../LindenUDP/UnackedPacketCollection.cs | 56 ++++++++++++---------- 2 files changed, 34 insertions(+), 26 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index bd58ddc..aff90c5 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -672,7 +672,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (packet.Header.AppendedAcks && packet.Header.AckList != null) { for (int i = 0; i < packet.Header.AckList.Length; i++) - udpClient.NeedAcks.Remove(packet.Header.AckList[i], now, packet.Header.Resent); + udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent); } // Handle PacketAck packets @@ -681,7 +681,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP PacketAckPacket ackPacket = (PacketAckPacket)packet; for (int i = 0; i < ackPacket.Packets.Length; i++) - udpClient.NeedAcks.Remove(ackPacket.Packets[i].ID, now, packet.Header.Resent); + udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent); // We don't need to do anything else with PacketAck packets return; diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index 90a87fa..793aefe 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -65,7 +65,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Holds packets that need to be added to the unacknowledged list private LocklessQueue m_pendingAdds = new LocklessQueue(); /// Holds information about pending acknowledgements - private LocklessQueue m_pendingRemoves = new LocklessQueue(); + private LocklessQueue m_pendingAcknowledgements = new LocklessQueue(); + /// Holds information about pending removals + private LocklessQueue m_pendingRemoves = new LocklessQueue(); /// /// Add an unacked packet to the collection @@ -92,9 +94,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Current value of Environment.TickCount /// This does not immediately acknowledge the packet, it only /// queues the ack so it can be handled in a thread-safe way later - public void Remove(uint sequenceNumber, int currentTime, bool fromResend) + public void Acknowledge(uint sequenceNumber, int currentTime, bool fromResend) { - m_pendingRemoves.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend)); + m_pendingAcknowledgements.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend)); } /// @@ -105,21 +107,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// /// Sequence number of the packet to /// acknowledge - /// The packet is removed from the collection immediately. - /// This function is not threadsafe. It must be called by the thread calling GetExpiredPackets. + /// The does not immediately remove the packet, it only queues the removal + /// so it can be handled in a thread safe way later public void Remove(uint sequenceNumber) { - OutgoingPacket removedPacket; - if (m_packets.TryGetValue(sequenceNumber, out removedPacket)) - { - if (removedPacket != null) - { - m_packets.Remove(sequenceNumber); - - // Update stats - Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength); - } - } + m_pendingRemoves.Enqueue(sequenceNumber); } /// @@ -179,15 +171,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_packets[pendingAdd.SequenceNumber] = pendingAdd; // Process all the pending removes, including updating statistics and round-trip times - PendingAck pendingRemove; - OutgoingPacket ackedPacket; - while (m_pendingRemoves.TryDequeue(out pendingRemove)) + PendingAck pendingAcknowledgement; + while (m_pendingAcknowledgements.TryDequeue(out pendingAcknowledgement)) { - if (m_packets.TryGetValue(pendingRemove.SequenceNumber, out ackedPacket)) + OutgoingPacket ackedPacket; + if (m_packets.TryGetValue(pendingAcknowledgement.SequenceNumber, out ackedPacket)) { if (ackedPacket != null) { - m_packets.Remove(pendingRemove.SequenceNumber); + m_packets.Remove(pendingAcknowledgement.SequenceNumber); // As with other network applications, assume that an acknowledged packet is an // indication that the network can handle a little more load, speed up the transmission @@ -196,16 +188,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Update stats Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); - if (!pendingRemove.FromResend) + if (!pendingAcknowledgement.FromResend) { // Calculate the round-trip time for this packet and its ACK - int rtt = pendingRemove.RemoveTime - ackedPacket.TickCount; + int rtt = pendingAcknowledgement.RemoveTime - ackedPacket.TickCount; if (rtt > 0) ackedPacket.Client.UpdateRoundTrip(rtt); } } } } + + uint pendingRemove; + while(m_pendingRemoves.TryDequeue(out pendingRemove)) + { + OutgoingPacket removedPacket; + if (m_packets.TryGetValue(pendingRemove, out removedPacket)) + { + if (removedPacket != null) + { + m_packets.Remove(pendingRemove); + + // Update stats + Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength); + } + } + } } } -} \ No newline at end of file +} -- cgit v1.1 From c5159ad8d00e5b2dc19eb100ec2e74a7a605e139 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Thu, 21 Apr 2011 15:40:38 -0700 Subject: Add some locking on the child list for the token bucket hiearchy. A few other cosmetic changes. --- .../Region/ClientStack/LindenUDP/TokenBucket.cs | 37 +++++++++++++--------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 4ee6d3a..8ce64d5 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -29,6 +29,8 @@ using System; using System.Collections; using System.Collections.Generic; using System.Reflection; +using OpenSim.Framework; + using log4net; namespace OpenSim.Region.ClientStack.LindenUDP @@ -177,7 +179,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP 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; + m_lastDrip = Util.EnvironmentTickCount(); } #endregion Constructor @@ -211,12 +213,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public void RegisterRequest(TokenBucket child, Int64 request) { - m_children[child] = request; - // m_totalDripRequest = m_children.Values.Sum(); + lock (m_children) + { + m_children[child] = request; + // m_totalDripRequest = m_children.Values.Sum(); - m_totalDripRequest = 0; - foreach (KeyValuePair cref in m_children) - m_totalDripRequest += cref.Value; + 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) @@ -229,12 +234,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public void UnregisterRequest(TokenBucket child) { - m_children.Remove(child); - // m_totalDripRequest = m_children.Values.Sum(); + lock (m_children) + { + m_children.Remove(child); + // m_totalDripRequest = m_children.Values.Sum(); - m_totalDripRequest = 0; - foreach (KeyValuePair cref in m_children) - m_totalDripRequest += cref.Value; + 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) @@ -297,10 +306,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // 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); - - m_lastDrip = now; + Int32 deltaMS = Math.Min(Util.EnvironmentTickCountSubtract(m_lastDrip), m_ticksPerQuantum); + m_lastDrip = Util.EnvironmentTickCount(); // 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 -- cgit v1.1 From 3534f4492ae747baff492f4bc10bf06994ee1bc6 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Fri, 22 Apr 2011 14:01:12 -0700 Subject: Various clean ups. Removed some debugging code. Added a new "show pqueues" command to look at the entity update priority queue. Added a "name" parameter to show queues, show pqueues and show throttles to look at data for a specific user. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 5 +++++ .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 24 +--------------------- 2 files changed, 6 insertions(+), 23 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 6129e10..32a075a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -385,6 +385,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP public bool IsGroupMember(UUID groupID) { return m_groupPowers.ContainsKey(groupID); } /// + /// Entity update queues + /// + public PriorityQueue EntityUpdateQueue { get { return m_entityUpdates; } } + + /// /// First name of the agent/avatar represented by the client /// public string FirstName { get { return m_firstName; } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 20bfec8..103ec66 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -228,26 +228,6 @@ 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 @@ -259,12 +239,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP 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; + info.totalThrottle = (int)m_throttleCategory.DripRate; return info; } -- cgit v1.1 From 08e58e7ca6602782e1727bed9ade89fa42e9b55b Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Fri, 22 Apr 2011 14:02:34 -0700 Subject: Set the initial rate for the adaptive throttle to 160Kpbs or about 15 packets per second. --- OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 8ce64d5..677d3d1 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -322,10 +322,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - // - // The minimum rate for flow control. - // - protected const Int64 m_minimumFlow = m_minimumDripRate * 10; + /// + /// The minimum rate for flow control. Minimum drip rate is one + /// packet per second. Open the throttle to 15 packets per second + /// or about 160kbps. + /// + protected const Int64 m_minimumFlow = m_minimumDripRate * 15; // // The maximum rate for flow control. Drip rate can never be -- cgit v1.1 From 024c12abc3aa42432e55e322141cad1edeb5bad1 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 25 Apr 2011 10:44:41 -0700 Subject: Cleaned up various configuration options. Removed the category throttle limits because the only ones used now are the defaults (which are overwritten by the client throttles anyway). Updated the default rates to correspond to about 350kbps. Also added a configuration to disable adaptive throttle. The default is the previous behavior (no adaptation). --- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 13 ++-- .../Region/ClientStack/LindenUDP/ThrottleRates.cs | 80 ++++------------------ .../Region/ClientStack/LindenUDP/TokenBucket.cs | 19 +++-- 3 files changed, 34 insertions(+), 78 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 103ec66..494cb0c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -181,9 +181,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_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.TotalLimit); + m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled); // Create a token bucket throttle for the total categary with the client bucket as a throttle - m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit); + m_throttleCategory = new TokenBucket(m_throttleClient, rates.Total); // Create an array of token buckets for this clients different throttle categories m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; @@ -194,7 +194,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_throttleCategory, rates.GetLimit(type)); + m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetRate(type)); } // Default the retransmission timeout to three seconds @@ -341,12 +341,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP task = Math.Max(task, LLUDPServer.MTU); texture = Math.Max(texture, LLUDPServer.MTU); asset = Math.Max(asset, LLUDPServer.MTU); - state = Math.Max(state, LLUDPServer.MTU); - int total = resend + land + wind + cloud + task + texture + asset + state; + int total = resend + land + wind + cloud + task + texture + asset; - //m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}", - // AgentID, resend, land, wind, cloud, task, texture, asset, state, total); + //m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, Total={8}", + // AgentID, resend, land, wind, cloud, task, texture, asset, total); // Update the token buckets with new throttle values TokenBucket bucket; diff --git a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs index aaf6e26..c9aac0b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs @@ -52,30 +52,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP public int Texture; /// Drip rate for asset packets public int Asset; - /// Drip rate for state packets - public int State; + /// Drip rate for the parent token bucket public int Total; - /// Maximum burst rate for resent packets - public int ResendLimit; - /// Maximum burst rate for land packets - public int LandLimit; - /// Maximum burst rate for wind packets - public int WindLimit; - /// Maximum burst rate for cloud packets - public int CloudLimit; - /// Maximum burst rate for task (state and transaction) packets - public int TaskLimit; - /// Maximum burst rate for texture packets - public int TextureLimit; - /// Maximum burst rate for asset packets - public int AssetLimit; - /// Maximum burst rate for state packets - public int StateLimit; - /// Burst rate for the parent token bucket - public int TotalLimit; - + /// Flag used to enable adaptive throttles + public bool AdaptiveThrottlesEnabled; + /// /// Default constructor /// @@ -86,26 +69,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP { IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"]; - Resend = throttleConfig.GetInt("resend_default", 12500); - Land = throttleConfig.GetInt("land_default", 1000); - Wind = throttleConfig.GetInt("wind_default", 1000); - Cloud = throttleConfig.GetInt("cloud_default", 1000); - Task = throttleConfig.GetInt("task_default", 1000); - Texture = throttleConfig.GetInt("texture_default", 1000); - Asset = throttleConfig.GetInt("asset_default", 1000); - State = throttleConfig.GetInt("state_default", 1000); - - ResendLimit = throttleConfig.GetInt("resend_limit", 18750); - LandLimit = throttleConfig.GetInt("land_limit", 29750); - WindLimit = throttleConfig.GetInt("wind_limit", 18750); - CloudLimit = throttleConfig.GetInt("cloud_limit", 18750); - TaskLimit = throttleConfig.GetInt("task_limit", 18750); - TextureLimit = throttleConfig.GetInt("texture_limit", 55750); - AssetLimit = throttleConfig.GetInt("asset_limit", 27500); - StateLimit = throttleConfig.GetInt("state_limit", 37000); + Resend = throttleConfig.GetInt("resend_default", 6625); + Land = throttleConfig.GetInt("land_default", 9125); + Wind = throttleConfig.GetInt("wind_default", 1750); + Cloud = throttleConfig.GetInt("cloud_default", 1750); + Task = throttleConfig.GetInt("task_default", 18500); + Texture = throttleConfig.GetInt("texture_default", 18500); + Asset = throttleConfig.GetInt("asset_default", 10500); Total = throttleConfig.GetInt("client_throttle_max_bps", 0); - TotalLimit = Total; + + AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false); } catch (Exception) { } } @@ -128,34 +102,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return Texture; case ThrottleOutPacketType.Asset: return Asset; - case ThrottleOutPacketType.State: - return State; - case ThrottleOutPacketType.Unknown: - default: - return 0; - } - } - - public int GetLimit(ThrottleOutPacketType type) - { - switch (type) - { - case ThrottleOutPacketType.Resend: - return ResendLimit; - case ThrottleOutPacketType.Land: - return LandLimit; - case ThrottleOutPacketType.Wind: - return WindLimit; - case ThrottleOutPacketType.Cloud: - return CloudLimit; - case ThrottleOutPacketType.Task: - return TaskLimit; - case ThrottleOutPacketType.Texture: - return TextureLimit; - case ThrottleOutPacketType.Asset: - return AssetLimit; - case ThrottleOutPacketType.State: - return StateLimit; case ThrottleOutPacketType.Unknown: default: return 0; diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 677d3d1..2ec79ab 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -340,6 +340,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); } } + private bool m_enabled = false; + // // // @@ -357,9 +359,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP // // // - public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate) : base(parent,m_minimumFlow) + public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate, bool enabled) : base(parent,maxDripRate) { - MaxDripRate = maxDripRate; + m_enabled = enabled; + + if (m_enabled) + { + m_log.WarnFormat("[TOKENBUCKET] Adaptive throttle enabled"); + MaxDripRate = maxDripRate; + AdjustedDripRate = m_minimumFlow; + } } // @@ -368,7 +377,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void ExpirePackets(Int32 count) { // m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count); - AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count)); + if (m_enabled) + AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count)); } // @@ -376,7 +386,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // public void AcknowledgePackets(Int32 count) { - AdjustedDripRate = AdjustedDripRate + count; + if (m_enabled) + AdjustedDripRate = AdjustedDripRate + count; } } } -- cgit v1.1 From 77ab7ce084d32c45273d79e9ec4f52c43e3a1d97 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 25 Apr 2011 15:11:29 -0700 Subject: Fixed the transmission of throttles from root agent to child agents. Child throttles are based on the number of child agents known to the root and at least 1/4 of the throttle given to the root. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 2 +- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 40 ++++++++++++++-------- 2 files changed, 26 insertions(+), 16 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 32a075a..cdd4224 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -11420,7 +11420,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public byte[] GetThrottlesPacked(float multiplier) { - return m_udpClient.GetThrottlesPacked(); + return m_udpClient.GetThrottlesPacked(multiplier); } /// diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 494cb0c..50f1e2c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -329,8 +329,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); // State is a subcategory of task that we allocate a percentage to 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 @@ -342,17 +340,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP texture = Math.Max(texture, LLUDPServer.MTU); asset = Math.Max(asset, LLUDPServer.MTU); - int total = resend + land + wind + cloud + task + texture + asset; - + //int total = resend + land + wind + cloud + task + texture + asset; //m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, Total={8}", // AgentID, resend, land, wind, cloud, task, texture, asset, total); // Update the token buckets with new throttle values TokenBucket bucket; - bucket = m_throttleCategory; - bucket.RequestedDripRate = total; - bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; bucket.RequestedDripRate = resend; @@ -381,22 +375,38 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_packedThrottles = null; } - public byte[] GetThrottlesPacked() + public byte[] GetThrottlesPacked(float multiplier) { byte[] data = m_packedThrottles; if (data == null) { + float rate; + data = new byte[7 * 4]; int i = 0; - 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; + // multiply by 8 to convert bytes back to bits + rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate * 8 * multiplier; + Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; + + rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate * 8 * multiplier; + Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; + + rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate * 8 * multiplier; + Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; + + rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate * 8 * multiplier; + Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; + + rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate * 8 * multiplier; + Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; + + rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate * 8 * multiplier; + Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; + + rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate * 8 * multiplier; + Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4; m_packedThrottles = data; } -- cgit v1.1 From 13f141a4d521fc15618c2983c989805d9174b969 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 25 Apr 2011 15:36:59 -0700 Subject: Fix the totals shown by show throttle --- OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 50f1e2c..ca5501d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -183,7 +183,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Create a token bucket throttle for this client that has the scene token bucket as a parent m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled); // Create a token bucket throttle for the total categary with the client bucket as a throttle - m_throttleCategory = new TokenBucket(m_throttleClient, rates.Total); + m_throttleCategory = new TokenBucket(m_throttleClient, 0); // Create an array of token buckets for this clients different throttle categories m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; -- cgit v1.1 From b9bca893efaeea3dd76dd3cb2082c181e5510b59 Mon Sep 17 00:00:00 2001 From: Mic Bowman Date: Mon, 25 Apr 2011 16:13:16 -0700 Subject: Removed debug message in the token bucket code --- OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 2ec79ab..29fd1a4 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs @@ -365,7 +365,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_enabled) { - m_log.WarnFormat("[TOKENBUCKET] Adaptive throttle enabled"); + // m_log.DebugFormat("[TOKENBUCKET] Adaptive throttle enabled"); MaxDripRate = maxDripRate; AdjustedDripRate = m_minimumFlow; } -- cgit v1.1 From 549dc5aeb9e693f1f575896796d19b03ec3a732f Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Thu, 28 Apr 2011 08:58:04 -0700 Subject: Eliminated sAgentCircuitData, a data structure that has been obsolete for quite some time. --- OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 22bad99..43903ce 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -11635,7 +11635,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP info.userEP = m_userEndPoint; info.proxyEP = null; - info.agentcircuit = new sAgentCircuitData(RequestClientInfo()); + info.agentcircuit = RequestClientInfo(); return info; } -- cgit v1.1