diff options
author | John Hurliman | 2009-10-21 11:59:48 -0700 |
---|---|---|
committer | John Hurliman | 2009-10-21 11:59:48 -0700 |
commit | 9178537e9414478f0a9bd84bb5e106b2f15640c3 (patch) | |
tree | c21bca46c08cdc9868ca7608856f51d029207aab /OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs | |
parent | Fixed the way OnQueueEmpty is called to prevent simultaneous calls for the sa... (diff) | |
download | opensim-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.cs | 147 |
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 | } |