From b017d985ab05044bf196f108516e6b0185c172f5 Mon Sep 17 00:00:00 2001 From: meta7 Date: Sat, 7 Aug 2010 08:06:41 -0700 Subject: [PATCH] Add some nullref checks to the UnackedPacketCollection. --- .../LindenUDP/UnackedPacketCollection.cs | 353 +++++++++--------- 1 file changed, 184 insertions(+), 169 deletions(-) diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index e43f7cf57a..c120a120fe 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -1,169 +1,184 @@ -/* - * 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.Generic; -using System.Net; -using OpenMetaverse; - -namespace OpenSim.Region.ClientStack.LindenUDP -{ - /// - /// Special collection that is optimized for tracking unacknowledged packets - /// - public sealed class UnackedPacketCollection - { - /// - /// Holds information about a pending acknowledgement - /// - 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; - - public PendingAck(uint sequenceNumber, int currentTime, bool fromResend) - { - SequenceNumber = sequenceNumber; - RemoveTime = currentTime; - FromResend = fromResend; - } - } - - /// Holds the actual unacked packet data, sorted by sequence number - 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 - private LocklessQueue m_pendingRemoves = new LocklessQueue(); - - /// - /// 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 - /// 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) - { - m_pendingAdds.Enqueue(packet); - } - - /// - /// Marks a packet as acknowledged - /// - /// 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)); - } - - /// - /// Returns a list of all of the packets with a TickCount older than - /// the specified timeout - /// - /// Number of ticks (milliseconds) before a - /// 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; - - if (m_packets.Count > 0) - { - int now = Environment.TickCount & Int32.MaxValue; - - 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 - if (packet.TickCount == 0) - continue; - - if (now - packet.TickCount >= timeoutMS) - { - 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); - } - } - } - - 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); - } - } - } - } - } -} +/* + * 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.Generic; +using System.Net; +using OpenMetaverse; + +namespace OpenSim.Region.ClientStack.LindenUDP +{ + /// + /// Special collection that is optimized for tracking unacknowledged packets + /// + public sealed class UnackedPacketCollection + { + /// + /// Holds information about a pending acknowledgement + /// + 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; + + public PendingAck(uint sequenceNumber, int currentTime, bool fromResend) + { + SequenceNumber = sequenceNumber; + RemoveTime = currentTime; + FromResend = fromResend; + } + } + + /// Holds the actual unacked packet data, sorted by sequence number + 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 + private LocklessQueue m_pendingRemoves = new LocklessQueue(); + + /// + /// 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 + /// 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) + { + m_pendingAdds.Enqueue(packet); + } + + /// + /// Marks a packet as acknowledged + /// + /// 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)); + } + + /// + /// Returns a list of all of the packets with a TickCount older than + /// the specified timeout + /// + /// Number of ticks (milliseconds) before a + /// 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; + + if (m_packets.Count > 0) + { + int now = Environment.TickCount & Int32.MaxValue; + + 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 + if (packet.TickCount == 0) + continue; + + if (now - packet.TickCount >= timeoutMS) + { + 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); + } + } + } + + return expiredPackets; + } + + private void ProcessQueues() + { + // Process all the pending adds + + OutgoingPacket pendingAdd; + if (m_pendingAdds != null) + { + while (m_pendingAdds.Dequeue(out pendingAdd)) + { + if (pendingAdd != null && m_packets != null) + { + m_packets[pendingAdd.SequenceNumber] = pendingAdd; + } + } + } + + // Process all the pending removes, including updating statistics and round-trip times + PendingAck pendingRemove; + OutgoingPacket ackedPacket; + if (m_pendingRemoves != null) + { + while (m_pendingRemoves.Dequeue(out pendingRemove)) + { + if (m_pendingRemoves != null && m_packets != null) + { + 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); + } + } + } + } + } + } + } +}