OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
UnackedPacketCollection.cs
Go to the documentation of this file.
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 //using System.Reflection;
35 //using log4net;
36 
37 namespace OpenSim.Region.ClientStack.LindenUDP
38 {
42  public sealed class UnackedPacketCollection
43  {
47  private struct PendingAck
48  {
50  public uint SequenceNumber;
53  public int RemoveTime;
56  public bool FromResend;
57 
58  public PendingAck(uint sequenceNumber, int currentTime, bool fromResend)
59  {
60  SequenceNumber = sequenceNumber;
61  RemoveTime = currentTime;
62  FromResend = fromResend;
63  }
64  }
65 
66  //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
67 
69  private Dictionary<uint, OutgoingPacket> m_packets = new Dictionary<uint, OutgoingPacket>();
71  private LocklessQueue<OutgoingPacket> m_pendingAdds = new LocklessQueue<OutgoingPacket>();
73  private LocklessQueue<PendingAck> m_pendingAcknowledgements = new LocklessQueue<PendingAck>();
75  private LocklessQueue<uint> m_pendingRemoves = new LocklessQueue<uint>();
76 
85  public void Add(OutgoingPacket packet)
86  {
87  m_pendingAdds.Enqueue(packet);
88  Interlocked.Add(ref packet.Client.UnackedBytes, packet.Buffer.DataLength);
89  }
90 
102  public void Acknowledge(uint sequenceNumber, int currentTime, bool fromResend)
103  {
104  m_pendingAcknowledgements.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend));
105  }
106 
117  public void Remove(uint sequenceNumber)
118  {
119  m_pendingRemoves.Enqueue(sequenceNumber);
120  }
121 
137  public List<OutgoingPacket> GetExpiredPackets(int timeoutMS)
138  {
139  ProcessQueues();
140 
141  List<OutgoingPacket> expiredPackets = null;
142 
143  if (m_packets.Count > 0)
144  {
145  int now = Environment.TickCount & Int32.MaxValue;
146 
147  foreach (OutgoingPacket packet in m_packets.Values)
148  {
149  // TickCount of zero means a packet is in the resend queue
150  // but hasn't actually been sent over the wire yet
151  if (packet.TickCount == 0)
152  continue;
153 
154  if (now - packet.TickCount >= timeoutMS)
155  {
156  if (expiredPackets == null)
157  expiredPackets = new List<OutgoingPacket>();
158 
159  // The TickCount will be set to the current time when the packet
160  // is actually sent out again
161  packet.TickCount = 0;
162 
163  // As with other network applications, assume that an expired packet is
164  // an indication of some network problem, slow transmission
165  packet.Client.FlowThrottle.ExpirePackets(1);
166 
167  expiredPackets.Add(packet);
168  }
169  }
170  }
171 
172  // if (expiredPackets != null)
173  // m_log.DebugFormat("[UNACKED PACKET COLLECTION]: Found {0} expired packets on timeout of {1}", expiredPackets.Count, timeoutMS);
174 
175  return expiredPackets;
176  }
177 
178  private void ProcessQueues()
179  {
180  // Process all the pending adds
181  OutgoingPacket pendingAdd;
182  while (m_pendingAdds.TryDequeue(out pendingAdd))
183  if (pendingAdd != null)
184  m_packets[pendingAdd.SequenceNumber] = pendingAdd;
185 
186  // Process all the pending removes, including updating statistics and round-trip times
187  PendingAck pendingAcknowledgement;
188  while (m_pendingAcknowledgements.TryDequeue(out pendingAcknowledgement))
189  {
190  //m_log.DebugFormat("[UNACKED PACKET COLLECTION]: Processing ack {0}", pendingAcknowledgement.SequenceNumber);
191  OutgoingPacket ackedPacket;
192  if (m_packets.TryGetValue(pendingAcknowledgement.SequenceNumber, out ackedPacket))
193  {
194  if (ackedPacket != null)
195  {
196  m_packets.Remove(pendingAcknowledgement.SequenceNumber);
197 
198  // As with other network applications, assume that an acknowledged packet is an
199  // indication that the network can handle a little more load, speed up the transmission
200  ackedPacket.Client.FlowThrottle.AcknowledgePackets(1);
201 
202  // Update stats
203  Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength);
204 
205  if (!pendingAcknowledgement.FromResend)
206  {
207  // Calculate the round-trip time for this packet and its ACK
208  int rtt = pendingAcknowledgement.RemoveTime - ackedPacket.TickCount;
209  if (rtt > 0)
210  ackedPacket.Client.UpdateRoundTrip(rtt);
211  }
212  }
213  else
214  {
215  // m_log.WarnFormat("[UNACKED PACKET COLLECTION]: found null packet for sequence number {0} to ack",
216  // pendingAcknowledgement.SequenceNumber);
217  }
218  }
219  else
220  {
221  // m_log.WarnFormat("[UNACKED PACKET COLLECTION]: Could not find packet with sequence number {0} to ack",
222  // pendingAcknowledgement.SequenceNumber);
223  }
224  }
225 
226  uint pendingRemove;
227  while(m_pendingRemoves.TryDequeue(out pendingRemove))
228  {
229  OutgoingPacket removedPacket;
230  if (m_packets.TryGetValue(pendingRemove, out removedPacket))
231  {
232  if (removedPacket != null)
233  {
234  m_packets.Remove(pendingRemove);
235 
236  // Update stats
237  Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength);
238  }
239  }
240  }
241  }
242  }
243 }
void Add(OutgoingPacket packet)
Add an unacked packet to the collection
int TickCount
Environment.TickCount when this packet was last sent over the wire
void Acknowledge(uint sequenceNumber, int currentTime, bool fromResend)
Marks a packet as acknowledged This method is used when an acknowledgement is received from the netwo...
List< OutgoingPacket > GetExpiredPackets(int timeoutMS)
Returns a list of all of the packets with a TickCount older than the specified timeout ...
Special collection that is optimized for tracking unacknowledged packets
void Remove(uint sequenceNumber)
Marks a packet as no longer needing acknowledgement without a received acknowledgement. This method is called when a packet expires and we no longer need an acknowledgement. When some reliable packet types expire, they are handled in a way other than simply resending them. The only effect of removal this way is to update unacked byte count.
Holds a reference to the LLUDPClientthis packet is destined for, along with the serialized packet dat...