clockwerk-opensim-stable – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 1 | /* |
2 | * Copyright (c) Contributors, http://opensimulator.org/ |
||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. |
||
4 | * |
||
5 | * Redistribution and use in source and binary forms, with or without |
||
6 | * modification, are permitted provided that the following conditions are met: |
||
7 | * * Redistributions of source code must retain the above copyright |
||
8 | * notice, this list of conditions and the following disclaimer. |
||
9 | * * Redistributions in binary form must reproduce the above copyright |
||
10 | * notice, this list of conditions and the following disclaimer in the |
||
11 | * documentation and/or other materials provided with the distribution. |
||
12 | * * Neither the name of the OpenSimulator Project nor the |
||
13 | * names of its contributors may be used to endorse or promote products |
||
14 | * derived from this software without specific prior written permission. |
||
15 | * |
||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY |
||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY |
||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
26 | */ |
||
27 | |||
28 | using System; |
||
29 | using System.Collections.Generic; |
||
30 | using System.Net; |
||
31 | using System.Threading; |
||
32 | using OpenMetaverse; |
||
33 | |||
34 | namespace OpenSim.Region.ClientStack.LindenUDP |
||
35 | { |
||
36 | /// <summary> |
||
37 | /// Special collection that is optimized for tracking unacknowledged packets |
||
38 | /// </summary> |
||
39 | public sealed class UnackedPacketCollection |
||
40 | { |
||
41 | /// <summary> |
||
42 | /// Holds information about a pending acknowledgement |
||
43 | /// </summary> |
||
44 | private struct PendingAck |
||
45 | { |
||
46 | /// <summary>Sequence number of the packet to remove</summary> |
||
47 | public uint SequenceNumber; |
||
48 | /// <summary>Environment.TickCount value when the remove was queued. |
||
49 | /// This is used to update round-trip times for packets</summary> |
||
50 | public int RemoveTime; |
||
51 | /// <summary>Whether or not this acknowledgement was attached to a |
||
52 | /// resent packet. If so, round-trip time will not be calculated</summary> |
||
53 | public bool FromResend; |
||
54 | |||
55 | public PendingAck(uint sequenceNumber, int currentTime, bool fromResend) |
||
56 | { |
||
57 | SequenceNumber = sequenceNumber; |
||
58 | RemoveTime = currentTime; |
||
59 | FromResend = fromResend; |
||
60 | } |
||
61 | } |
||
62 | |||
63 | /// <summary>Holds the actual unacked packet data, sorted by sequence number</summary> |
||
64 | private Dictionary<uint, OutgoingPacket> m_packets = new Dictionary<uint, OutgoingPacket>(); |
||
65 | /// <summary>Holds packets that need to be added to the unacknowledged list</summary> |
||
66 | private LocklessQueue<OutgoingPacket> m_pendingAdds = new LocklessQueue<OutgoingPacket>(); |
||
67 | /// <summary>Holds information about pending acknowledgements</summary> |
||
68 | private LocklessQueue<PendingAck> m_pendingAcknowledgements = new LocklessQueue<PendingAck>(); |
||
69 | /// <summary>Holds information about pending removals</summary> |
||
70 | private LocklessQueue<uint> m_pendingRemoves = new LocklessQueue<uint>(); |
||
71 | |||
72 | /// <summary> |
||
73 | /// Add an unacked packet to the collection |
||
74 | /// </summary> |
||
75 | /// <param name="packet">Packet that is awaiting acknowledgement</param> |
||
76 | /// <returns>True if the packet was successfully added, false if the |
||
77 | /// packet already existed in the collection</returns> |
||
78 | /// <remarks>This does not immediately add the ACK to the collection, |
||
79 | /// it only queues it so it can be added in a thread-safe way later</remarks> |
||
80 | public void Add(OutgoingPacket packet) |
||
81 | { |
||
82 | m_pendingAdds.Enqueue(packet); |
||
83 | Interlocked.Add(ref packet.Client.UnackedBytes, packet.Buffer.DataLength); |
||
84 | } |
||
85 | |||
86 | /// <summary> |
||
87 | /// Marks a packet as acknowledged |
||
88 | /// This method is used when an acknowledgement is received from the network for a previously |
||
89 | /// sent packet. Effects of removal this way are to update unacked byte count, adjust RTT |
||
90 | /// and increase throttle to the coresponding client. |
||
91 | /// </summary> |
||
92 | /// <param name="sequenceNumber">Sequence number of the packet to |
||
93 | /// acknowledge</param> |
||
94 | /// <param name="currentTime">Current value of Environment.TickCount</param> |
||
95 | /// <remarks>This does not immediately acknowledge the packet, it only |
||
96 | /// queues the ack so it can be handled in a thread-safe way later</remarks> |
||
97 | public void Acknowledge(uint sequenceNumber, int currentTime, bool fromResend) |
||
98 | { |
||
99 | m_pendingAcknowledgements.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend)); |
||
100 | } |
||
101 | |||
102 | /// <summary> |
||
103 | /// Marks a packet as no longer needing acknowledgement without a received acknowledgement. |
||
104 | /// This method is called when a packet expires and we no longer need an acknowledgement. |
||
105 | /// When some reliable packet types expire, they are handled in a way other than simply |
||
106 | /// resending them. The only effect of removal this way is to update unacked byte count. |
||
107 | /// </summary> |
||
108 | /// <param name="sequenceNumber">Sequence number of the packet to |
||
109 | /// acknowledge</param> |
||
110 | /// <remarks>The does not immediately remove the packet, it only queues the removal |
||
111 | /// so it can be handled in a thread safe way later</remarks> |
||
112 | public void Remove(uint sequenceNumber) |
||
113 | { |
||
114 | m_pendingRemoves.Enqueue(sequenceNumber); |
||
115 | } |
||
116 | |||
117 | /// <summary> |
||
118 | /// Returns a list of all of the packets with a TickCount older than |
||
119 | /// the specified timeout |
||
120 | /// </summary> |
||
121 | /// <remarks> |
||
122 | /// This function is not thread safe, and cannot be called |
||
123 | /// multiple times concurrently |
||
124 | /// </remarks> |
||
125 | /// <param name="timeoutMS">Number of ticks (milliseconds) before a |
||
126 | /// packet is considered expired |
||
127 | /// </param> |
||
128 | /// <returns> |
||
129 | /// A list of all expired packets according to the given |
||
130 | /// expiration timeout |
||
131 | /// </returns> |
||
132 | public List<OutgoingPacket> GetExpiredPackets(int timeoutMS) |
||
133 | { |
||
134 | ProcessQueues(); |
||
135 | |||
136 | List<OutgoingPacket> expiredPackets = null; |
||
137 | |||
138 | if (m_packets.Count > 0) |
||
139 | { |
||
140 | int now = Environment.TickCount & Int32.MaxValue; |
||
141 | |||
142 | foreach (OutgoingPacket packet in m_packets.Values) |
||
143 | { |
||
144 | // TickCount of zero means a packet is in the resend queue |
||
145 | // but hasn't actually been sent over the wire yet |
||
146 | if (packet.TickCount == 0) |
||
147 | continue; |
||
148 | |||
149 | if (now - packet.TickCount >= timeoutMS) |
||
150 | { |
||
151 | if (expiredPackets == null) |
||
152 | expiredPackets = new List<OutgoingPacket>(); |
||
153 | |||
154 | // The TickCount will be set to the current time when the packet |
||
155 | // is actually sent out again |
||
156 | packet.TickCount = 0; |
||
157 | |||
158 | // As with other network applications, assume that an expired packet is |
||
159 | // an indication of some network problem, slow transmission |
||
160 | packet.Client.FlowThrottle.ExpirePackets(1); |
||
161 | |||
162 | expiredPackets.Add(packet); |
||
163 | } |
||
164 | } |
||
165 | } |
||
166 | |||
167 | //if (expiredPackets != null) |
||
168 | // m_log.DebugFormat("[UNACKED PACKET COLLECTION]: Found {0} expired packets on timeout of {1}", expiredPackets.Count, timeoutMS); |
||
169 | |||
170 | return expiredPackets; |
||
171 | } |
||
172 | |||
173 | private void ProcessQueues() |
||
174 | { |
||
175 | // Process all the pending adds |
||
176 | OutgoingPacket pendingAdd; |
||
177 | while (m_pendingAdds.TryDequeue(out pendingAdd)) |
||
178 | if (pendingAdd != null) |
||
179 | m_packets[pendingAdd.SequenceNumber] = pendingAdd; |
||
180 | |||
181 | // Process all the pending removes, including updating statistics and round-trip times |
||
182 | PendingAck pendingAcknowledgement; |
||
183 | while (m_pendingAcknowledgements.TryDequeue(out pendingAcknowledgement)) |
||
184 | { |
||
185 | //m_log.DebugFormat("[UNACKED PACKET COLLECTION]: Processing ack {0}", pendingAcknowledgement.SequenceNumber); |
||
186 | OutgoingPacket ackedPacket; |
||
187 | if (m_packets.TryGetValue(pendingAcknowledgement.SequenceNumber, out ackedPacket)) |
||
188 | { |
||
189 | if (ackedPacket != null) |
||
190 | { |
||
191 | m_packets.Remove(pendingAcknowledgement.SequenceNumber); |
||
192 | |||
193 | // As with other network applications, assume that an acknowledged packet is an |
||
194 | // indication that the network can handle a little more load, speed up the transmission |
||
195 | ackedPacket.Client.FlowThrottle.AcknowledgePackets(ackedPacket.Buffer.DataLength); |
||
196 | |||
197 | // Update stats |
||
198 | Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); |
||
199 | |||
200 | if (!pendingAcknowledgement.FromResend) |
||
201 | { |
||
202 | // Calculate the round-trip time for this packet and its ACK |
||
203 | int rtt = pendingAcknowledgement.RemoveTime - ackedPacket.TickCount; |
||
204 | if (rtt > 0) |
||
205 | ackedPacket.Client.UpdateRoundTrip(rtt); |
||
206 | } |
||
207 | } |
||
208 | else |
||
209 | { |
||
210 | //m_log.WarnFormat("[UNACKED PACKET COLLECTION]: Could not find packet with sequence number {0} to ack", pendingAcknowledgement.SequenceNumber); |
||
211 | } |
||
212 | } |
||
213 | } |
||
214 | |||
215 | uint pendingRemove; |
||
216 | while(m_pendingRemoves.TryDequeue(out pendingRemove)) |
||
217 | { |
||
218 | OutgoingPacket removedPacket; |
||
219 | if (m_packets.TryGetValue(pendingRemove, out removedPacket)) |
||
220 | { |
||
221 | if (removedPacket != null) |
||
222 | { |
||
223 | m_packets.Remove(pendingRemove); |
||
224 | |||
225 | // Update stats |
||
226 | Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength); |
||
227 | } |
||
228 | } |
||
229 | } |
||
230 | } |
||
231 | } |
||
232 | } |