aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
diff options
context:
space:
mode:
authorJohn Hurliman2009-10-21 11:59:48 -0700
committerJohn Hurliman2009-10-21 11:59:48 -0700
commit9178537e9414478f0a9bd84bb5e106b2f15640c3 (patch)
treec21bca46c08cdc9868ca7608856f51d029207aab /OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
parentFixed the way OnQueueEmpty is called to prevent simultaneous calls for the sa... (diff)
downloadopensim-SC_OLD-9178537e9414478f0a9bd84bb5e106b2f15640c3.zip
opensim-SC_OLD-9178537e9414478f0a9bd84bb5e106b2f15640c3.tar.gz
opensim-SC_OLD-9178537e9414478f0a9bd84bb5e106b2f15640c3.tar.bz2
opensim-SC_OLD-9178537e9414478f0a9bd84bb5e106b2f15640c3.tar.xz
* 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
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}