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.cs162
1 files changed, 81 insertions, 81 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
index f3242c1..e43f7cf 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 Dictionary<uint, OutgoingPacket> m_packets = new Dictionary<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
@@ -154,16 +123,47 @@ namespace OpenSim.Region.ClientStack.LindenUDP
154 { 123 {
155 if (expiredPackets == null) 124 if (expiredPackets == null)
156 expiredPackets = new List<OutgoingPacket>(); 125 expiredPackets = new List<OutgoingPacket>();
126
127 // The TickCount will be set to the current time when the packet
128 // is actually sent out again
129 packet.TickCount = 0;
130
157 expiredPackets.Add(packet); 131 expiredPackets.Add(packet);
158 } 132 }
159 else
160 {
161 break;
162 }
163 } 133 }
164 } 134 }
165 135
166 return expiredPackets; 136 return expiredPackets;
167 } 137 }
138
139 private void ProcessQueues()
140 {
141 // Process all the pending adds
142 OutgoingPacket pendingAdd;
143 while (m_pendingAdds.Dequeue(out pendingAdd))
144 m_packets[pendingAdd.SequenceNumber] = pendingAdd;
145
146 // Process all the pending removes, including updating statistics and round-trip times
147 PendingAck pendingRemove;
148 OutgoingPacket ackedPacket;
149 while (m_pendingRemoves.Dequeue(out pendingRemove))
150 {
151 if (m_packets.TryGetValue(pendingRemove.SequenceNumber, out ackedPacket))
152 {
153 m_packets.Remove(pendingRemove.SequenceNumber);
154
155 // Update stats
156 System.Threading.Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength);
157
158 if (!pendingRemove.FromResend)
159 {
160 // Calculate the round-trip time for this packet and its ACK
161 int rtt = pendingRemove.RemoveTime - ackedPacket.TickCount;
162 if (rtt > 0)
163 ackedPacket.Client.UpdateRoundTrip(rtt);
164 }
165 }
166 }
167 }
168 } 168 }
169} 169}