clockwerk-opensim-stable – Blame information for rev 1

Subversion Repositories:
Rev:
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 log4net;
33 using OpenSim.Framework;
34 using OpenSim.Framework.Monitoring;
35 using OpenMetaverse;
36 using OpenMetaverse.Packets;
37  
38 using TokenBucket = OpenSim.Region.ClientStack.LindenUDP.TokenBucket;
39  
40 namespace OpenSim.Region.ClientStack.LindenUDP
41 {
42 #region Delegates
43  
44 /// <summary>
45 /// Fired when updated networking stats are produced for this client
46 /// </summary>
47 /// <param name="inPackets">Number of incoming packets received since this
48 /// event was last fired</param>
49 /// <param name="outPackets">Number of outgoing packets sent since this
50 /// event was last fired</param>
51 /// <param name="unAckedBytes">Current total number of bytes in packets we
52 /// are waiting on ACKs for</param>
53 public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
54 /// <summary>
55 /// Fired when the queue for one or more packet categories is empty. This
56 /// event can be hooked to put more data on the empty queues
57 /// </summary>
58 /// <param name="category">Categories of the packet queues that are empty</param>
59 public delegate void QueueEmpty(ThrottleOutPacketTypeFlags categories);
60  
61 #endregion Delegates
62  
63 /// <summary>
64 /// Tracks state for a client UDP connection and provides client-specific methods
65 /// </summary>
66 public sealed class LLUDPClient
67 {
68 // TODO: Make this a config setting
69 /// <summary>Percentage of the task throttle category that is allocated to avatar and prim
70 /// state updates</summary>
71 const float STATE_TASK_PERCENTAGE = 0.8f;
72  
73 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
74  
75 /// <summary>The number of packet categories to throttle on. If a throttle category is added
76 /// or removed, this number must also change</summary>
77 const int THROTTLE_CATEGORY_COUNT = 8;
78  
79 /// <summary>Fired when updated networking stats are produced for this client</summary>
80 public event PacketStats OnPacketStats;
81 /// <summary>Fired when the queue for a packet category is empty. This event can be
82 /// hooked to put more data on the empty queue</summary>
83 public event QueueEmpty OnQueueEmpty;
84  
85 public event Func<ThrottleOutPacketTypeFlags, bool> HasUpdates;
86  
87 /// <summary>AgentID for this client</summary>
88 public readonly UUID AgentID;
89 /// <summary>The remote address of the connected client</summary>
90 public readonly IPEndPoint RemoteEndPoint;
91 /// <summary>Circuit code that this client is connected on</summary>
92 public readonly uint CircuitCode;
93 /// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary>
94 public readonly IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200);
95 /// <summary>Packets we have sent that need to be ACKed by the client</summary>
96 public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection();
97 /// <summary>ACKs that are queued up, waiting to be sent to the client</summary>
98 public readonly OpenSim.Framework.LocklessQueue<uint> PendingAcks = new OpenSim.Framework.LocklessQueue<uint>();
99  
100 /// <summary>Current packet sequence number</summary>
101 public int CurrentSequence;
102 /// <summary>Current ping sequence number</summary>
103 public byte CurrentPingSequence;
104 /// <summary>True when this connection is alive, otherwise false</summary>
105 public bool IsConnected = true;
106 /// <summary>True when this connection is paused, otherwise false</summary>
107 public bool IsPaused;
108 /// <summary>Environment.TickCount when the last packet was received for this client</summary>
109 public int TickLastPacketReceived;
110  
111 /// <summary>Smoothed round-trip time. A smoothed average of the round-trip time for sending a
112 /// reliable packet to the client and receiving an ACK</summary>
113 public float SRTT;
114 /// <summary>Round-trip time variance. Measures the consistency of round-trip times</summary>
115 public float RTTVAR;
116 /// <summary>Retransmission timeout. Packets that have not been acknowledged in this number of
117 /// milliseconds or longer will be resent</summary>
118 /// <remarks>Calculated from <seealso cref="SRTT"/> and <seealso cref="RTTVAR"/> using the
119 /// guidelines in RFC 2988</remarks>
120 public int RTO;
121 /// <summary>Number of bytes received since the last acknowledgement was sent out. This is used
122 /// to loosely follow the TCP delayed ACK algorithm in RFC 1122 (4.2.3.2)</summary>
123 public int BytesSinceLastACK;
124 /// <summary>Number of packets received from this client</summary>
125 public int PacketsReceived;
126 /// <summary>Number of packets sent to this client</summary>
127 public int PacketsSent;
128 /// <summary>Number of packets resent to this client</summary>
129 public int PacketsResent;
130 /// <summary>Total byte count of unacked packets sent to this client</summary>
131 public int UnackedBytes;
132  
133 /// <summary>Total number of received packets that we have reported to the OnPacketStats event(s)</summary>
134 private int m_packetsReceivedReported;
135 /// <summary>Total number of sent packets that we have reported to the OnPacketStats event(s)</summary>
136 private int m_packetsSentReported;
137 /// <summary>Holds the Environment.TickCount value of when the next OnQueueEmpty can be fired</summary>
138 private int m_nextOnQueueEmpty = 1;
139  
140 /// <summary>Throttle bucket for this agent's connection</summary>
141 private readonly AdaptiveTokenBucket m_throttleClient;
142 public AdaptiveTokenBucket FlowThrottle
143 {
144 get { return m_throttleClient; }
145 }
146  
147 /// <summary>Throttle bucket for this agent's connection</summary>
148 private readonly TokenBucket m_throttleCategory;
149 /// <summary>Throttle buckets for each packet category</summary>
150 private readonly TokenBucket[] m_throttleCategories;
151 /// <summary>Outgoing queues for throttled packets</summary>
152 private readonly OpenSim.Framework.LocklessQueue<OutgoingPacket>[] m_packetOutboxes = new OpenSim.Framework.LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT];
153 /// <summary>A container that can hold one packet for each outbox, used to store
154 /// dequeued packets that are being held for throttling</summary>
155 private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
156 /// <summary>A reference to the LLUDPServer that is managing this client</summary>
157 private readonly LLUDPServer m_udpServer;
158  
159 /// <summary>Caches packed throttle information</summary>
160 private byte[] m_packedThrottles;
161  
162 private int m_defaultRTO = 1000; // 1sec is the recommendation in the RFC
163 private int m_maxRTO = 60000;
164  
165 private ClientInfo m_info = new ClientInfo();
166  
167 /// <summary>
168 /// Default constructor
169 /// </summary>
170 /// <param name="server">Reference to the UDP server this client is connected to</param>
171 /// <param name="rates">Default throttling rates and maximum throttle limits</param>
172 /// <param name="parentThrottle">Parent HTB (hierarchical token bucket)
173 /// that the child throttles will be governed by</param>
174 /// <param name="circuitCode">Circuit code for this connection</param>
175 /// <param name="agentID">AgentID for the connected agent</param>
176 /// <param name="remoteEndPoint">Remote endpoint for this connection</param>
177 /// <param name="defaultRTO">
178 /// Default retransmission timeout for unacked packets. The RTO will never drop
179 /// beyond this number.
180 /// </param>
181 /// <param name="maxRTO">
182 /// The maximum retransmission timeout for unacked packets. The RTO will never exceed this number.
183 /// </param>
184 public LLUDPClient(
185 LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID,
186 IPEndPoint remoteEndPoint, int defaultRTO, int maxRTO)
187 {
188 AgentID = agentID;
189 RemoteEndPoint = remoteEndPoint;
190 CircuitCode = circuitCode;
191 m_udpServer = server;
192 if (defaultRTO != 0)
193 m_defaultRTO = defaultRTO;
194 if (maxRTO != 0)
195 m_maxRTO = maxRTO;
196  
197 // Create a token bucket throttle for this client that has the scene token bucket as a parent
198 m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled);
199 // Create a token bucket throttle for the total categary with the client bucket as a throttle
200 m_throttleCategory = new TokenBucket(m_throttleClient, 0);
201 // Create an array of token buckets for this clients different throttle categories
202 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
203  
204 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
205 {
206 ThrottleOutPacketType type = (ThrottleOutPacketType)i;
207  
208 // Initialize the packet outboxes, where packets sit while they are waiting for tokens
209 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
210 // Initialize the token buckets that control the throttling for each category
211 m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetRate(type));
212 }
213  
214 // Default the retransmission timeout to one second
215 RTO = m_defaultRTO;
216  
217 // Initialize this to a sane value to prevent early disconnects
218 TickLastPacketReceived = Environment.TickCount & Int32.MaxValue;
219 }
220  
221 /// <summary>
222 /// Shuts down this client connection
223 /// </summary>
224 public void Shutdown()
225 {
226 IsConnected = false;
227 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
228 {
229 m_packetOutboxes[i].Clear();
230 m_nextPackets[i] = null;
231 }
232  
233 // pull the throttle out of the scene throttle
234 m_throttleClient.Parent.UnregisterRequest(m_throttleClient);
235 OnPacketStats = null;
236 OnQueueEmpty = null;
237 }
238  
239 /// <summary>
240 /// Gets information about this client connection
241 /// </summary>
242 /// <returns>Information about the client connection</returns>
243 public ClientInfo GetClientInfo()
244 {
245 // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists
246 // of pending and needed ACKs for every client every time some method wants information about
247 // this connection is a recipe for poor performance
248  
249 m_info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
250 m_info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
251 m_info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
252 m_info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
253 m_info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
254 m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
255 m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
256 m_info.totalThrottle = (int)m_throttleCategory.DripRate;
257  
258 return m_info;
259 }
260  
261 /// <summary>
262 /// Modifies the UDP throttles
263 /// </summary>
264 /// <param name="info">New throttling values</param>
265 public void SetClientInfo(ClientInfo info)
266 {
267 // TODO: Allowing throttles to be manually set from this function seems like a reasonable
268 // idea. On the other hand, letting external code manipulate our ACK accounting is not
269 // going to happen
270 throw new NotImplementedException();
271 }
272  
273 /// <summary>
274 /// Return statistics information about client packet queues.
275 /// </summary>
276 /// <remarks>
277 /// FIXME: This should really be done in a more sensible manner rather than sending back a formatted string.
278 /// </remarks>
279 /// <returns></returns>
280 public string GetStats()
281 {
282 return string.Format(
283 "{0,7} {1,7} {2,7} {3,9} {4,7} {5,7} {6,7} {7,7} {8,7} {9,8} {10,7} {11,7}",
284 Util.EnvironmentTickCountSubtract(TickLastPacketReceived),
285 PacketsReceived,
286 PacketsSent,
287 PacketsResent,
288 UnackedBytes,
289 m_packetOutboxes[(int)ThrottleOutPacketType.Resend].Count,
290 m_packetOutboxes[(int)ThrottleOutPacketType.Land].Count,
291 m_packetOutboxes[(int)ThrottleOutPacketType.Wind].Count,
292 m_packetOutboxes[(int)ThrottleOutPacketType.Cloud].Count,
293 m_packetOutboxes[(int)ThrottleOutPacketType.Task].Count,
294 m_packetOutboxes[(int)ThrottleOutPacketType.Texture].Count,
295 m_packetOutboxes[(int)ThrottleOutPacketType.Asset].Count);
296 }
297  
298 public void SendPacketStats()
299 {
300 PacketStats callback = OnPacketStats;
301 if (callback != null)
302 {
303 int newPacketsReceived = PacketsReceived - m_packetsReceivedReported;
304 int newPacketsSent = PacketsSent - m_packetsSentReported;
305  
306 callback(newPacketsReceived, newPacketsSent, UnackedBytes);
307  
308 m_packetsReceivedReported += newPacketsReceived;
309 m_packetsSentReported += newPacketsSent;
310 }
311 }
312  
313 public void SetThrottles(byte[] throttleData)
314 {
315 byte[] adjData;
316 int pos = 0;
317  
318 if (!BitConverter.IsLittleEndian)
319 {
320 byte[] newData = new byte[7 * 4];
321 Buffer.BlockCopy(throttleData, 0, newData, 0, 7 * 4);
322  
323 for (int i = 0; i < 7; i++)
324 Array.Reverse(newData, i * 4, 4);
325  
326 adjData = newData;
327 }
328 else
329 {
330 adjData = throttleData;
331 }
332  
333 // 0.125f converts from bits to bytes
334 int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
335 int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
336 int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
337 int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
338 int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
339 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
340 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
341  
342 // Make sure none of the throttles are set below our packet MTU,
343 // otherwise a throttle could become permanently clogged
344 resend = Math.Max(resend, LLUDPServer.MTU);
345 land = Math.Max(land, LLUDPServer.MTU);
346 wind = Math.Max(wind, LLUDPServer.MTU);
347 cloud = Math.Max(cloud, LLUDPServer.MTU);
348 task = Math.Max(task, LLUDPServer.MTU);
349 texture = Math.Max(texture, LLUDPServer.MTU);
350 asset = Math.Max(asset, LLUDPServer.MTU);
351  
352 //int total = resend + land + wind + cloud + task + texture + asset;
353 //m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, Total={8}",
354 // AgentID, resend, land, wind, cloud, task, texture, asset, total);
355  
356 // Update the token buckets with new throttle values
357 TokenBucket bucket;
358  
359 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
360 bucket.RequestedDripRate = resend;
361  
362 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land];
363 bucket.RequestedDripRate = land;
364  
365 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind];
366 bucket.RequestedDripRate = wind;
367  
368 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud];
369 bucket.RequestedDripRate = cloud;
370  
371 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset];
372 bucket.RequestedDripRate = asset;
373  
374 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task];
375 bucket.RequestedDripRate = task;
376  
377 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture];
378 bucket.RequestedDripRate = texture;
379  
380 // Reset the packed throttles cached data
381 m_packedThrottles = null;
382 }
383  
384 public byte[] GetThrottlesPacked(float multiplier)
385 {
386 byte[] data = m_packedThrottles;
387  
388 if (data == null)
389 {
390 float rate;
391  
392 data = new byte[7 * 4];
393 int i = 0;
394  
395 // multiply by 8 to convert bytes back to bits
396 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate * 8 * multiplier;
397 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
398  
399 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate * 8 * multiplier;
400 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
401  
402 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate * 8 * multiplier;
403 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
404  
405 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate * 8 * multiplier;
406 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
407  
408 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate * 8 * multiplier;
409 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
410  
411 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate * 8 * multiplier;
412 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
413  
414 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate * 8 * multiplier;
415 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
416  
417 m_packedThrottles = data;
418 }
419  
420 return data;
421 }
422  
423 /// <summary>
424 /// Queue an outgoing packet if appropriate.
425 /// </summary>
426 /// <param name="packet"></param>
427 /// <param name="forceQueue">Always queue the packet if at all possible.</param>
428 /// <returns>
429 /// true if the packet has been queued,
430 /// false if the packet has not been queued and should be sent immediately.
431 /// </returns>
432 public bool EnqueueOutgoing(OutgoingPacket packet, bool forceQueue)
433 {
434 int category = (int)packet.Category;
435  
436 if (category >= 0 && category < m_packetOutboxes.Length)
437 {
438 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category];
439 TokenBucket bucket = m_throttleCategories[category];
440  
441 // Don't send this packet if there is already a packet waiting in the queue
442 // even if we have the tokens to send it, tokens should go to the already
443 // queued packets
444 if (queue.Count > 0)
445 {
446 queue.Enqueue(packet);
447 return true;
448 }
449  
450  
451 if (!forceQueue && bucket.RemoveTokens(packet.Buffer.DataLength))
452 {
453 // Enough tokens were removed from the bucket, the packet will not be queued
454 return false;
455 }
456 else
457 {
458 // Force queue specified or not enough tokens in the bucket, queue this packet
459 queue.Enqueue(packet);
460 return true;
461 }
462 }
463 else
464 {
465 // We don't have a token bucket for this category, so it will not be queued
466 return false;
467 }
468 }
469  
470 /// <summary>
471 /// Loops through all of the packet queues for this client and tries to send
472 /// an outgoing packet from each, obeying the throttling bucket limits
473 /// </summary>
474 ///
475 /// <remarks>
476 /// Packet queues are inspected in ascending numerical order starting from 0. Therefore, queues with a lower
477 /// ThrottleOutPacketType number will see their packet get sent first (e.g. if both Land and Wind queues have
478 /// packets, then the packet at the front of the Land queue will be sent before the packet at the front of the
479 /// wind queue).
480 ///
481 /// This function is only called from a synchronous loop in the
482 /// UDPServer so we don't need to bother making this thread safe
483 /// </remarks>
484 ///
485 /// <returns>True if any packets were sent, otherwise false</returns>
486 public bool DequeueOutgoing()
487 {
488 OutgoingPacket packet;
489 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue;
490 TokenBucket bucket;
491 bool packetSent = false;
492 ThrottleOutPacketTypeFlags emptyCategories = 0;
493  
494 //string queueDebugOutput = String.Empty; // Serious debug business
495  
496 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
497 {
498 bucket = m_throttleCategories[i];
499 //queueDebugOutput += m_packetOutboxes[i].Count + " "; // Serious debug business
500  
501 if (m_nextPackets[i] != null)
502 {
503 // This bucket was empty the last time we tried to send a packet,
504 // leaving a dequeued packet still waiting to be sent out. Try to
505 // send it again
506 OutgoingPacket nextPacket = m_nextPackets[i];
507 if (bucket.RemoveTokens(nextPacket.Buffer.DataLength))
508 {
509 // Send the packet
510 m_udpServer.SendPacketFinal(nextPacket);
511 m_nextPackets[i] = null;
512 packetSent = true;
513 }
514 }
515 else
516 {
517 // No dequeued packet waiting to be sent, try to pull one off
518 // this queue
519 queue = m_packetOutboxes[i];
520 if (queue.Dequeue(out packet))
521 {
522 // A packet was pulled off the queue. See if we have
523 // enough tokens in the bucket to send it out
524 if (bucket.RemoveTokens(packet.Buffer.DataLength))
525 {
526 // Send the packet
527 m_udpServer.SendPacketFinal(packet);
528 packetSent = true;
529 }
530 else
531 {
532 // Save the dequeued packet for the next iteration
533 m_nextPackets[i] = packet;
534 }
535  
536 // If the queue is empty after this dequeue, fire the queue
537 // empty callback now so it has a chance to fill before we
538 // get back here
539 if (queue.Count == 0)
540 emptyCategories |= CategoryToFlag(i);
541 }
542 else
543 {
544 // No packets in this queue. Fire the queue empty callback
545 // if it has not been called recently
546 emptyCategories |= CategoryToFlag(i);
547 }
548 }
549 }
550  
551 if (emptyCategories != 0)
552 BeginFireQueueEmpty(emptyCategories);
553  
554 //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business
555 return packetSent;
556 }
557  
558 /// <summary>
559 /// Called when an ACK packet is received and a round-trip time for a
560 /// packet is calculated. This is used to calculate the smoothed
561 /// round-trip time, round trip time variance, and finally the
562 /// retransmission timeout
563 /// </summary>
564 /// <param name="r">Round-trip time of a single packet and its
565 /// acknowledgement</param>
566 public void UpdateRoundTrip(float r)
567 {
568 const float ALPHA = 0.125f;
569 const float BETA = 0.25f;
570 const float K = 4.0f;
571  
572 if (RTTVAR == 0.0f)
573 {
574 // First RTT measurement
575 SRTT = r;
576 RTTVAR = r * 0.5f;
577 }
578 else
579 {
580 // Subsequence RTT measurement
581 RTTVAR = (1.0f - BETA) * RTTVAR + BETA * Math.Abs(SRTT - r);
582 SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r;
583 }
584  
585 int rto = (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR));
586  
587 // Clamp the retransmission timeout to manageable values
588 rto = Utils.Clamp(rto, m_defaultRTO, m_maxRTO);
589  
590 RTO = rto;
591  
592 //if (RTO != rto)
593 // m_log.Debug("[LLUDPCLIENT]: Setting RTO to " + RTO + "ms from " + rto + "ms with an RTTVAR of " +
594 //RTTVAR + " based on new RTT of " + r + "ms");
595 }
596  
597 /// <summary>
598 /// Exponential backoff of the retransmission timeout, per section 5.5
599 /// of RFC 2988
600 /// </summary>
601 public void BackoffRTO()
602 {
603 // Reset SRTT and RTTVAR, we assume they are bogus since things
604 // didn't work out and we're backing off the timeout
605 SRTT = 0.0f;
606 RTTVAR = 0.0f;
607  
608 // Double the retransmission timeout
609 RTO = Math.Min(RTO * 2, m_maxRTO);
610 }
611  
612 /// <summary>
613 /// Does an early check to see if this queue empty callback is already
614 /// running, then asynchronously firing the event
615 /// </summary>
616 /// <param name="categories">Throttle categories to fire the callback for</param>
617 private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories)
618 {
619 // if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty)
620 if (!m_isQueueEmptyRunning && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty)
621 {
622 m_isQueueEmptyRunning = true;
623  
624 int start = Environment.TickCount & Int32.MaxValue;
625 const int MIN_CALLBACK_MS = 30;
626  
627 m_nextOnQueueEmpty = start + MIN_CALLBACK_MS;
628 if (m_nextOnQueueEmpty == 0)
629 m_nextOnQueueEmpty = 1;
630  
631 // Use a value of 0 to signal that FireQueueEmpty is running
632 // m_nextOnQueueEmpty = 0;
633  
634 m_categories = categories;
635  
636 if (HasUpdates(m_categories))
637 {
638 // Asynchronously run the callback
639 Util.FireAndForget(FireQueueEmpty, categories);
640 }
641 else
642 {
643 m_isQueueEmptyRunning = false;
644 }
645 }
646 }
647  
648 private bool m_isQueueEmptyRunning;
649 private ThrottleOutPacketTypeFlags m_categories = 0;
650  
651 /// <summary>
652 /// Fires the OnQueueEmpty callback and sets the minimum time that it
653 /// can be called again
654 /// </summary>
655 /// <param name="o">Throttle categories to fire the callback for,
656 /// stored as an object to match the WaitCallback delegate
657 /// signature</param>
658 private void FireQueueEmpty(object o)
659 {
660 // int start = Environment.TickCount & Int32.MaxValue;
661 // const int MIN_CALLBACK_MS = 30;
662  
663 // if (m_udpServer.IsRunningOutbound)
664 // {
665 ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o;
666 QueueEmpty callback = OnQueueEmpty;
667  
668 if (callback != null)
669 {
670 // if (m_udpServer.IsRunningOutbound)
671 // {
672 try { callback(categories); }
673 catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); }
674 // }
675 }
676 // }
677  
678 // m_nextOnQueueEmpty = start + MIN_CALLBACK_MS;
679 // if (m_nextOnQueueEmpty == 0)
680 // m_nextOnQueueEmpty = 1;
681  
682 // }
683  
684 m_isQueueEmptyRunning = false;
685 }
686  
687 /// <summary>
688 /// Converts a <seealso cref="ThrottleOutPacketType"/> integer to a
689 /// flag value
690 /// </summary>
691 /// <param name="i">Throttle category to convert</param>
692 /// <returns>Flag representation of the throttle category</returns>
693 private static ThrottleOutPacketTypeFlags CategoryToFlag(int i)
694 {
695 ThrottleOutPacketType category = (ThrottleOutPacketType)i;
696  
697 /*
698 * Land = 1,
699 /// <summary>Wind data</summary>
700 Wind = 2,
701 /// <summary>Cloud data</summary>
702 Cloud = 3,
703 /// <summary>Any packets that do not fit into the other throttles</summary>
704 Task = 4,
705 /// <summary>Texture assets</summary>
706 Texture = 5,
707 /// <summary>Non-texture assets</summary>
708 Asset = 6,
709 */
710  
711 switch (category)
712 {
713 case ThrottleOutPacketType.Land:
714 return ThrottleOutPacketTypeFlags.Land;
715 case ThrottleOutPacketType.Wind:
716 return ThrottleOutPacketTypeFlags.Wind;
717 case ThrottleOutPacketType.Cloud:
718 return ThrottleOutPacketTypeFlags.Cloud;
719 case ThrottleOutPacketType.Task:
720 return ThrottleOutPacketTypeFlags.Task;
721 case ThrottleOutPacketType.Texture:
722 return ThrottleOutPacketTypeFlags.Texture;
723 case ThrottleOutPacketType.Asset:
724 return ThrottleOutPacketTypeFlags.Asset;
725 default:
726 return 0;
727 }
728 }
729 }
730 }