aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs147
1 files changed, 71 insertions, 76 deletions
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
37 /// </summary> 37 /// </summary>
38 public sealed class UnackedPacketCollection 38 public sealed class UnackedPacketCollection
39 { 39 {
40 /// <summary>Synchronization primitive. A lock must be acquired on this
41 /// object before calling any of the unsafe methods</summary>
42 public object SyncRoot = new object();
43
44 /// <summary>Holds the actual unacked packet data, sorted by sequence number</summary>
45 private SortedDictionary<uint, OutgoingPacket> packets = new SortedDictionary<uint, OutgoingPacket>();
46
47 /// <summary>Gets the total number of unacked packets</summary>
48 public int Count { get { return packets.Count; } }
49
50 /// <summary> 40 /// <summary>
51 /// Default constructor 41 /// Holds information about a pending acknowledgement
52 /// </summary> 42 /// </summary>
53 public UnackedPacketCollection() 43 private struct PendingAck
54 { 44 {
55 } 45 /// <summary>Sequence number of the packet to remove</summary>
46 public uint SequenceNumber;
47 /// <summary>Environment.TickCount value when the remove was queued.
48 /// This is used to update round-trip times for packets</summary>
49 public int RemoveTime;
50 /// <summary>Whether or not this acknowledgement was attached to a
51 /// resent packet. If so, round-trip time will not be calculated</summary>
52 public bool FromResend;
56 53
57 /// <summary> 54 public PendingAck(uint sequenceNumber, int currentTime, bool fromResend)
58 /// Add an unacked packet to the collection
59 /// </summary>
60 /// <param name="packet">Packet that is awaiting acknowledgement</param>
61 /// <returns>True if the packet was successfully added, false if the
62 /// packet already existed in the collection</returns>
63 public bool Add(OutgoingPacket packet)
64 {
65 lock (SyncRoot)
66 { 55 {
67 if (!packets.ContainsKey(packet.SequenceNumber)) 56 SequenceNumber = sequenceNumber;
68 { 57 RemoveTime = currentTime;
69 packets.Add(packet.SequenceNumber, packet); 58 FromResend = fromResend;
70 return true;
71 }
72 return false;
73 } 59 }
74 } 60 }
75 61
76 /// <summary> 62 /// <summary>Holds the actual unacked packet data, sorted by sequence number</summary>
77 /// Removes a packet from the collection without attempting to obtain a 63 private SortedDictionary<uint, OutgoingPacket> m_packets = new SortedDictionary<uint, OutgoingPacket>();
78 /// lock first 64 /// <summary>Holds packets that need to be added to the unacknowledged list</summary>
79 /// </summary> 65 private LocklessQueue<OutgoingPacket> m_pendingAdds = new LocklessQueue<OutgoingPacket>();
80 /// <param name="sequenceNumber">Sequence number of the packet to remove</param> 66 /// <summary>Holds information about pending acknowledgements</summary>
81 /// <returns>True if the packet was found and removed, otherwise false</returns> 67 private LocklessQueue<PendingAck> m_pendingRemoves = new LocklessQueue<PendingAck>();
82 public bool RemoveUnsafe(uint sequenceNumber)
83 {
84 return packets.Remove(sequenceNumber);
85 }
86
87 /// <summary>
88 /// Removes a packet from the collection without attempting to obtain a
89 /// lock first
90 /// </summary>
91 /// <param name="sequenceNumber">Sequence number of the packet to remove</param>
92 /// <param name="packet">Returns the removed packet</param>
93 /// <returns>True if the packet was found and removed, otherwise false</returns>
94 public bool RemoveUnsafe(uint sequenceNumber, out OutgoingPacket packet)
95 {
96 if (packets.TryGetValue(sequenceNumber, out packet))
97 {
98 packets.Remove(sequenceNumber);
99 return true;
100 }
101
102 return false;
103 }
104 68
105 /// <summary> 69 /// <summary>
106 /// Removes all elements from the collection 70 /// Add an unacked packet to the collection
107 /// </summary> 71 /// </summary>
108 public void Clear() 72 /// <param name="packet">Packet that is awaiting acknowledgement</param>
73 /// <returns>True if the packet was successfully added, false if the
74 /// packet already existed in the collection</returns>
75 /// <remarks>This does not immediately add the ACK to the collection,
76 /// it only queues it so it can be added in a thread-safe way later</remarks>
77 public void Add(OutgoingPacket packet)
109 { 78 {
110 lock (SyncRoot) 79 m_pendingAdds.Enqueue(packet);
111 packets.Clear();
112 } 80 }
113 81
114 /// <summary> 82 /// <summary>
115 /// Gets the packet with the lowest sequence number 83 /// Marks a packet as acknowledged
116 /// </summary> 84 /// </summary>
117 /// <returns>The packet with the lowest sequence number, or null if the 85 /// <param name="sequenceNumber">Sequence number of the packet to
118 /// collection is empty</returns> 86 /// acknowledge</param>
119 public OutgoingPacket GetOldest() 87 /// <param name="currentTime">Current value of Environment.TickCount</param>
88 public void Remove(uint sequenceNumber, int currentTime, bool fromResend)
120 { 89 {
121 lock (SyncRoot) 90 m_pendingRemoves.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend));
122 {
123 using (SortedDictionary<uint, OutgoingPacket>.ValueCollection.Enumerator e = packets.Values.GetEnumerator())
124 {
125 if (e.MoveNext())
126 return e.Current;
127 else
128 return null;
129 }
130 }
131 } 91 }
132 92
133 /// <summary> 93 /// <summary>
@@ -138,14 +98,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
138 /// packet is considered expired</param> 98 /// packet is considered expired</param>
139 /// <returns>A list of all expired packets according to the given 99 /// <returns>A list of all expired packets according to the given
140 /// expiration timeout</returns> 100 /// expiration timeout</returns>
101 /// <remarks>This function is not thread safe, and cannot be called
102 /// multiple times concurrently</remarks>
141 public List<OutgoingPacket> GetExpiredPackets(int timeoutMS) 103 public List<OutgoingPacket> GetExpiredPackets(int timeoutMS)
142 { 104 {
105 ProcessQueues();
106
143 List<OutgoingPacket> expiredPackets = null; 107 List<OutgoingPacket> expiredPackets = null;
144 108
145 lock (SyncRoot) 109 if (m_packets.Count > 0)
146 { 110 {
147 int now = Environment.TickCount; 111 int now = Environment.TickCount;
148 foreach (OutgoingPacket packet in packets.Values) 112
113 foreach (OutgoingPacket packet in m_packets.Values)
149 { 114 {
150 // TickCount of zero means a packet is in the resend queue 115 // TickCount of zero means a packet is in the resend queue
151 // but hasn't actually been sent over the wire yet 116 // but hasn't actually been sent over the wire yet
@@ -167,5 +132,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP
167 132
168 return expiredPackets; 133 return expiredPackets;
169 } 134 }
135
136 private void ProcessQueues()
137 {
138 // Process all the pending adds
139 OutgoingPacket pendingAdd;
140 while (m_pendingAdds.Dequeue(out pendingAdd))
141 m_packets[pendingAdd.SequenceNumber] = pendingAdd;
142
143 // Process all the pending removes, including updating statistics and round-trip times
144 PendingAck pendingRemove;
145 OutgoingPacket ackedPacket;
146 while (m_pendingRemoves.Dequeue(out pendingRemove))
147 {
148 if (m_packets.TryGetValue(pendingRemove.SequenceNumber, out ackedPacket))
149 {
150 m_packets.Remove(pendingRemove.SequenceNumber);
151
152 // Update stats
153 System.Threading.Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength);
154
155 if (!pendingRemove.FromResend)
156 {
157 // Calculate the round-trip time for this packet and its ACK
158 int rtt = pendingRemove.RemoveTime - ackedPacket.TickCount;
159 if (rtt > 0)
160 ackedPacket.Client.UpdateRoundTrip(rtt);
161 }
162 }
163 }
164 }
170 } 165 }
171} 166}