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.cs157
1 files changed, 76 insertions, 81 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
index f3242c1..016712f 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
@@ -37,97 +37,59 @@ 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 /// <remarks>This does not immediately acknowledge the packet, it only
89 /// queues the ack so it can be handled in a thread-safe way later</remarks>
90 public void Remove(uint sequenceNumber, int currentTime, bool fromResend)
120 { 91 {
121 lock (SyncRoot) 92 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 } 93 }
132 94
133 /// <summary> 95 /// <summary>
@@ -138,15 +100,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
138 /// packet is considered expired</param> 100 /// packet is considered expired</param>
139 /// <returns>A list of all expired packets according to the given 101 /// <returns>A list of all expired packets according to the given
140 /// expiration timeout</returns> 102 /// expiration timeout</returns>
103 /// <remarks>This function is not thread safe, and cannot be called
104 /// multiple times concurrently</remarks>
141 public List<OutgoingPacket> GetExpiredPackets(int timeoutMS) 105 public List<OutgoingPacket> GetExpiredPackets(int timeoutMS)
142 { 106 {
107 ProcessQueues();
108
143 List<OutgoingPacket> expiredPackets = null; 109 List<OutgoingPacket> expiredPackets = null;
144 110
145 lock (SyncRoot) 111 if (m_packets.Count > 0)
146 { 112 {
147 int now = Environment.TickCount; 113 int now = Environment.TickCount & Int32.MaxValue;
148 foreach (OutgoingPacket packet in packets.Values) 114
115 foreach (OutgoingPacket packet in m_packets.Values)
149 { 116 {
117 // TickCount of zero means a packet is in the resend queue
118 // but hasn't actually been sent over the wire yet
150 if (packet.TickCount == 0) 119 if (packet.TickCount == 0)
151 continue; 120 continue;
152 121
@@ -156,14 +125,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP
156 expiredPackets = new List<OutgoingPacket>(); 125 expiredPackets = new List<OutgoingPacket>();
157 expiredPackets.Add(packet); 126 expiredPackets.Add(packet);
158 } 127 }
159 else
160 {
161 break;
162 }
163 } 128 }
164 } 129 }
165 130
166 return expiredPackets; 131 return expiredPackets;
167 } 132 }
133
134 private void ProcessQueues()
135 {
136 // Process all the pending adds
137 OutgoingPacket pendingAdd;
138 while (m_pendingAdds.Dequeue(out pendingAdd))
139 m_packets[pendingAdd.SequenceNumber] = pendingAdd;
140
141 // Process all the pending removes, including updating statistics and round-trip times
142 PendingAck pendingRemove;
143 OutgoingPacket ackedPacket;
144 while (m_pendingRemoves.Dequeue(out pendingRemove))
145 {
146 if (m_packets.TryGetValue(pendingRemove.SequenceNumber, out ackedPacket))
147 {
148 m_packets.Remove(pendingRemove.SequenceNumber);
149
150 // Update stats
151 System.Threading.Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength);
152
153 if (!pendingRemove.FromResend)
154 {
155 // Calculate the round-trip time for this packet and its ACK
156 int rtt = pendingRemove.RemoveTime - ackedPacket.TickCount;
157 if (rtt > 0)
158 ackedPacket.Client.UpdateRoundTrip(rtt);
159 }
160 }
161 }
162 }
168 } 163 }
169} 164}