From 45dc4e0a5442d1d03f7387164070145386a9b4e1 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 20 Oct 2009 18:19:17 -0700 Subject: * Added a sanity check to GetScriptAssemblies() and GetScriptStates() for the case where no scripting engine is enabled * Added TokenBucket.cs to OpenSim, with some fixes for setting a more accurate MaxBurst value and getting a more accurate Content value (by Drip()ing each get) --- OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs') diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index f3242c1..87c7df4 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -147,6 +147,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP int now = Environment.TickCount; foreach (OutgoingPacket packet in packets.Values) { + // TickCount of zero means a packet is in the resend queue + // but hasn't actually been sent over the wire yet if (packet.TickCount == 0) continue; -- cgit v1.1 From 9178537e9414478f0a9bd84bb5e106b2f15640c3 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 11:59:48 -0700 Subject: * Replaced the UnackedPacketCollection with a lockless implementation. The tiny amount of time spent in the locks turned into a lot of time when the rest of the LLUDP implementation went lockless * Changed the timer tracking numbers for each client to not have "memory". It will no longer queue up calls to functions like ResendUnacked * Reverted Jim's WaitHandle code. Although it was technically more correct, it exhibited the exact same behavior as the old code but spent more cycles. The 20ms has been replaced with the minimum amount of time before a token bucket could receive a drip, and an else { sleep(0); } was added to make sure the outgoing packet handler always yields at least a minimum amount --- .../LindenUDP/UnackedPacketCollection.cs | 147 ++++++++++----------- 1 file changed, 71 insertions(+), 76 deletions(-) (limited to 'OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs') diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index 87c7df4..12f0c0a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -37,97 +37,57 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public sealed class UnackedPacketCollection { - /// Synchronization primitive. A lock must be acquired on this - /// object before calling any of the unsafe methods - public object SyncRoot = new object(); - - /// Holds the actual unacked packet data, sorted by sequence number - private SortedDictionary packets = new SortedDictionary(); - - /// Gets the total number of unacked packets - public int Count { get { return packets.Count; } } - /// - /// Default constructor + /// Holds information about a pending acknowledgement /// - public UnackedPacketCollection() + private struct PendingAck { - } + /// Sequence number of the packet to remove + public uint SequenceNumber; + /// Environment.TickCount value when the remove was queued. + /// This is used to update round-trip times for packets + public int RemoveTime; + /// Whether or not this acknowledgement was attached to a + /// resent packet. If so, round-trip time will not be calculated + public bool FromResend; - /// - /// Add an unacked packet to the collection - /// - /// Packet that is awaiting acknowledgement - /// True if the packet was successfully added, false if the - /// packet already existed in the collection - public bool Add(OutgoingPacket packet) - { - lock (SyncRoot) + public PendingAck(uint sequenceNumber, int currentTime, bool fromResend) { - if (!packets.ContainsKey(packet.SequenceNumber)) - { - packets.Add(packet.SequenceNumber, packet); - return true; - } - return false; + SequenceNumber = sequenceNumber; + RemoveTime = currentTime; + FromResend = fromResend; } } - /// - /// Removes a packet from the collection without attempting to obtain a - /// lock first - /// - /// Sequence number of the packet to remove - /// True if the packet was found and removed, otherwise false - public bool RemoveUnsafe(uint sequenceNumber) - { - return packets.Remove(sequenceNumber); - } - - /// - /// Removes a packet from the collection without attempting to obtain a - /// lock first - /// - /// Sequence number of the packet to remove - /// Returns the removed packet - /// True if the packet was found and removed, otherwise false - public bool RemoveUnsafe(uint sequenceNumber, out OutgoingPacket packet) - { - if (packets.TryGetValue(sequenceNumber, out packet)) - { - packets.Remove(sequenceNumber); - return true; - } - - return false; - } + /// Holds the actual unacked packet data, sorted by sequence number + private SortedDictionary m_packets = new SortedDictionary(); + /// 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(); /// - /// Removes all elements from the collection + /// Add an unacked packet to the collection /// - public void Clear() + /// Packet that is awaiting acknowledgement + /// True if the packet was successfully added, false if the + /// packet already existed in the collection + /// This does not immediately add the ACK to the collection, + /// it only queues it so it can be added in a thread-safe way later + public void Add(OutgoingPacket packet) { - lock (SyncRoot) - packets.Clear(); + m_pendingAdds.Enqueue(packet); } /// - /// Gets the packet with the lowest sequence number + /// Marks a packet as acknowledged /// - /// The packet with the lowest sequence number, or null if the - /// collection is empty - public OutgoingPacket GetOldest() + /// Sequence number of the packet to + /// acknowledge + /// Current value of Environment.TickCount + public void Remove(uint sequenceNumber, int currentTime, bool fromResend) { - lock (SyncRoot) - { - using (SortedDictionary.ValueCollection.Enumerator e = packets.Values.GetEnumerator()) - { - if (e.MoveNext()) - return e.Current; - else - return null; - } - } + m_pendingRemoves.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend)); } /// @@ -138,14 +98,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// packet is considered expired /// A list of all expired packets according to the given /// expiration timeout + /// This function is not thread safe, and cannot be called + /// multiple times concurrently public List GetExpiredPackets(int timeoutMS) { + ProcessQueues(); + List expiredPackets = null; - lock (SyncRoot) + if (m_packets.Count > 0) { int now = Environment.TickCount; - foreach (OutgoingPacket packet in packets.Values) + + foreach (OutgoingPacket packet in m_packets.Values) { // TickCount of zero means a packet is in the resend queue // but hasn't actually been sent over the wire yet @@ -167,5 +132,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP return expiredPackets; } + + private void ProcessQueues() + { + // Process all the pending adds + OutgoingPacket pendingAdd; + while (m_pendingAdds.Dequeue(out pendingAdd)) + m_packets[pendingAdd.SequenceNumber] = pendingAdd; + + // Process all the pending removes, including updating statistics and round-trip times + PendingAck pendingRemove; + OutgoingPacket ackedPacket; + while (m_pendingRemoves.Dequeue(out pendingRemove)) + { + if (m_packets.TryGetValue(pendingRemove.SequenceNumber, out ackedPacket)) + { + m_packets.Remove(pendingRemove.SequenceNumber); + + // Update stats + System.Threading.Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); + + if (!pendingRemove.FromResend) + { + // Calculate the round-trip time for this packet and its ACK + int rtt = pendingRemove.RemoveTime - ackedPacket.TickCount; + if (rtt > 0) + ackedPacket.Client.UpdateRoundTrip(rtt); + } + } + } + } } } -- cgit v1.1 From 7ee422a344ff22cf988aea2355628d2dee831983 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 13:47:16 -0700 Subject: * Handle UseCircuitCode packets asynchronously. Adding an agent to a scene can take several seconds, and was blocking up packet handling in the meantime * Clamp retransmission timeout values between three and 10 seconds * Log outgoing time for a packet right after it is sent instead of well before * Loop through the entire UnackedPacketCollection when looking for expired packets --- OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs') diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index 12f0c0a..bd5fe1c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -85,6 +85,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Sequence number of the packet to /// acknowledge /// 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) { m_pendingRemoves.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend)); @@ -108,7 +110,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_packets.Count > 0) { - int now = Environment.TickCount; + int now = Environment.TickCount & Int32.MaxValue; foreach (OutgoingPacket packet in m_packets.Values) { @@ -123,10 +125,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP expiredPackets = new List(); expiredPackets.Add(packet); } - else + /*else { break; - } + }*/ } } -- cgit v1.1 From b06f258319f088bcf6658880ed371ef313bac3b6 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 16:21:08 -0700 Subject: * FireQueueEmpty now checks if a measurable amount of time has passed, and if not it sleeps for a small amount of time. This throttles OnQueueEmpty calls where there is no callback or the callback is doing very little work * Changed HandleQueueEmpty()'s Monitor.TryEnter() calls to locks. We want to take our time in this function and do all the work necessary, since returning too fast will induce a sleep anyways --- OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs | 4 ---- 1 file changed, 4 deletions(-) (limited to 'OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs') diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index bd5fe1c..016712f 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -125,10 +125,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP expiredPackets = new List(); expiredPackets.Add(packet); } - /*else - { - break; - }*/ } } -- cgit v1.1 From 4e04f6b3a5a875c7d8820c679bcbcdcfba1227bf Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 17:02:55 -0700 Subject: * Clarified what FireQueueEmpty is doing with a MIN_CALLBACK_MS constant and upped it to 30ms * Removed the unused PacketSent() function * Switched UnackedPacketCollection from a SortedDictionary to a Dictionary now that the sorting is no longer needed. Big performance improvement for ResendUnacked() --- OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs') diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index 016712f..3e2e81c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -60,7 +60,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } /// Holds the actual unacked packet data, sorted by sequence number - private SortedDictionary m_packets = new SortedDictionary(); + private Dictionary m_packets = new Dictionary(); /// Holds packets that need to be added to the unacknowledged list private LocklessQueue m_pendingAdds = new LocklessQueue(); /// Holds information about pending acknowledgements -- cgit v1.1 From 6492640e72776d9f0a015e6a719c8cef28ccb7e3 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 18:03:41 -0700 Subject: * Change the OnQueueEmpty signature to send the flags of the queues that are empty instead of firing once per empty queue * Change the OnQueueEmpty firing to use a minimum time until next fire instead of a sleep * Set OutgoingPacket.TickCount = 0 earlier to avoid extra resends when things are running slowly (inside a profiler, for example) --- OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs') diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index 3e2e81c..e43f7cf 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -123,6 +123,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP { if (expiredPackets == null) expiredPackets = new List(); + + // The TickCount will be set to the current time when the packet + // is actually sent out again + packet.TickCount = 0; + expiredPackets.Add(packet); } } -- cgit v1.1