OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
LLUDPClient.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 log4net;
33 using OpenSim.Framework;
34 using OpenSim.Framework.Monitoring;
35 using OpenMetaverse;
36 using OpenMetaverse.Packets;
37 
39 
40 namespace OpenSim.Region.ClientStack.LindenUDP
41 {
42  #region Delegates
43 
53  public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
59  public delegate void QueueEmpty(ThrottleOutPacketTypeFlags categories);
60 
61  #endregion Delegates
62 
66  public sealed class LLUDPClient
67  {
68  // TODO: Make this a config setting
71  const float STATE_TASK_PERCENTAGE = 0.8f;
72 
73  private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
74 
77  const int THROTTLE_CATEGORY_COUNT = 8;
78 
83  public int DebugDataOutLevel { get; set; }
84 
89  public int ThrottleDebugLevel
90  {
91  get
92  {
93  return m_throttleDebugLevel;
94  }
95 
96  set
97  {
98  m_throttleDebugLevel = value;
99 /*
100  m_throttleClient.DebugLevel = m_throttleDebugLevel;
101  foreach (TokenBucket tb in m_throttleCategories)
102  tb.DebugLevel = m_throttleDebugLevel;
103  */
104  }
105  }
106  private int m_throttleDebugLevel;
107 
109  public event PacketStats OnPacketStats;
112  public event QueueEmpty OnQueueEmpty;
113 
114  public event Func<ThrottleOutPacketTypeFlags, bool> HasUpdates;
115 
117  public readonly UUID AgentID;
119  public readonly IPEndPoint RemoteEndPoint;
121  public readonly uint CircuitCode;
123  public readonly IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200);
124 
126  public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection();
127 
129  public readonly DoubleLocklessQueue<uint> PendingAcks = new DoubleLocklessQueue<uint>();
130 
132  public int CurrentSequence;
134  public byte CurrentPingSequence;
136  public bool IsConnected = true;
138  public bool IsPaused;
141 
144  public float SRTT;
146  public float RTTVAR;
151  public int RTO;
154  public int BytesSinceLastACK;
156  public int PacketsReceived;
158  public int PacketsSent;
160  public int PacketsResent;
162  public int UnackedBytes;
163 
165  private int m_packetsReceivedReported;
167  private int m_packetsSentReported;
169  private int m_nextOnQueueEmpty = 1;
170 
172  private readonly AdaptiveTokenBucket m_throttleClient;
173  public AdaptiveTokenBucket FlowThrottle
174  {
175  get { return m_throttleClient; }
176  }
177 
179  private readonly TokenBucket[] m_throttleCategories;
181  private readonly DoubleLocklessQueue<OutgoingPacket>[] m_packetOutboxes = new DoubleLocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT];
184  private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
186  private readonly LLUDPServer m_udpServer;
187 
189  private byte[] m_packedThrottles;
190 
191  private int m_defaultRTO = 1000; // 1sec is the recommendation in the RFC
192  private int m_maxRTO = 60000;
193  public bool m_deliverPackets = true;
194 
195  private float m_burstTime;
196 
198  public int m_pingMS;
199 
200  public int PingTimeMS
201  {
202  get
203  {
204  if (m_pingMS < 10)
205  return 10;
206  if(m_pingMS > 2000)
207  return 2000;
208  return m_pingMS;
209  }
210  }
211 
216  private double m_cannibalrate = 0.0;
217 
218  private ClientInfo m_info = new ClientInfo();
219 
237  public LLUDPClient(
238  LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID,
239  IPEndPoint remoteEndPoint, int defaultRTO, int maxRTO)
240  {
241  AgentID = agentID;
242  RemoteEndPoint = remoteEndPoint;
243  CircuitCode = circuitCode;
244  m_udpServer = server;
245  if (defaultRTO != 0)
246  m_defaultRTO = defaultRTO;
247  if (maxRTO != 0)
248  m_maxRTO = maxRTO;
249 
250  m_burstTime = rates.BrustTime;
251  float m_burst = rates.ClientMaxRate * m_burstTime;
252 
253  // Create a token bucket throttle for this client that has the scene token bucket as a parent
254  m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.ClientMaxRate, m_burst, rates.AdaptiveThrottlesEnabled);
255 
256  // Create an array of token buckets for this clients different throttle categories
257  m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
258 
259  m_cannibalrate = rates.CannibalizeTextureRate;
260 
261  m_burst = rates.Total * rates.BrustTime;
262 
263  for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
264  {
266 
267  // Initialize the packet outboxes, where packets sit while they are waiting for tokens
268  m_packetOutboxes[i] = new DoubleLocklessQueue<OutgoingPacket>();
269  // Initialize the token buckets that control the throttling for each category
270  m_throttleCategories[i] = new TokenBucket(m_throttleClient, rates.GetRate(type), m_burst);
271  }
272 
273  // Default the retransmission timeout to one second
274  RTO = m_defaultRTO;
275 
276  // Initialize this to a sane value to prevent early disconnects
277  TickLastPacketReceived = Environment.TickCount & Int32.MaxValue;
278  m_pingMS = (int)(3.0 * server.TickCountResolution); // so filter doesnt start at 0;
279  }
280 
284  public void Shutdown()
285  {
286  IsConnected = false;
287  for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
288  {
289  m_packetOutboxes[i].Clear();
290  m_nextPackets[i] = null;
291  }
292 
293  // pull the throttle out of the scene throttle
294  m_throttleClient.Parent.UnregisterRequest(m_throttleClient);
295  OnPacketStats = null;
296  OnQueueEmpty = null;
297  }
298 
304  {
305  // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists
306  // of pending and needed ACKs for every client every time some method wants information about
307  // this connection is a recipe for poor performance
308 
309  m_info.resendThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
310  m_info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
311  m_info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
312  m_info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
313  m_info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
314  m_info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
315  m_info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
316  m_info.totalThrottle = (int)m_throttleClient.DripRate;
317  return m_info;
318  }
319 
324  public void SetClientInfo(ClientInfo info)
325  {
326  // TODO: Allowing throttles to be manually set from this function seems like a reasonable
327  // idea. On the other hand, letting external code manipulate our ACK accounting is not
328  // going to happen
329  throw new NotImplementedException();
330  }
331 
337  {
338  int total = 0;
339 
340  for (int i = 0; i <= (int)ThrottleOutPacketType.Asset; i++)
341  total += m_packetOutboxes[i].Count;
342 
343  return total;
344  }
345 
352  {
353  int icat = (int)throttleType;
354  if (icat > 0 && icat < THROTTLE_CATEGORY_COUNT)
355  return m_packetOutboxes[icat].Count;
356  else
357  return 0;
358  }
359 
367  public string GetStats()
368  {
369  return string.Format(
370  "{0,7} {1,7} {2,7} {3,9} {4,7} {5,7} {6,7} {7,7} {8,7} {9,8} {10,7} {11,7}",
371  Util.EnvironmentTickCountSubtract(TickLastPacketReceived),
372  PacketsReceived,
373  PacketsSent,
374  PacketsResent,
375  UnackedBytes,
376  m_packetOutboxes[(int)ThrottleOutPacketType.Resend].Count,
377  m_packetOutboxes[(int)ThrottleOutPacketType.Land].Count,
378  m_packetOutboxes[(int)ThrottleOutPacketType.Wind].Count,
379  m_packetOutboxes[(int)ThrottleOutPacketType.Cloud].Count,
380  m_packetOutboxes[(int)ThrottleOutPacketType.Task].Count,
381  m_packetOutboxes[(int)ThrottleOutPacketType.Texture].Count,
382  m_packetOutboxes[(int)ThrottleOutPacketType.Asset].Count);
383  }
384 
385  public void SendPacketStats()
386  {
387  PacketStats callback = OnPacketStats;
388  if (callback != null)
389  {
390  int newPacketsReceived = PacketsReceived - m_packetsReceivedReported;
391  int newPacketsSent = PacketsSent - m_packetsSentReported;
392 
393  callback(newPacketsReceived, newPacketsSent, UnackedBytes);
394 
395  m_packetsReceivedReported += newPacketsReceived;
396  m_packetsSentReported += newPacketsSent;
397  }
398  }
399 
400  public void SetThrottles(byte[] throttleData)
401  {
402  SetThrottles(throttleData, 1.0f);
403  }
404 
405  public void SetThrottles(byte[] throttleData, float factor)
406  {
407  byte[] adjData;
408  int pos = 0;
409 
410  if (!BitConverter.IsLittleEndian)
411  {
412  byte[] newData = new byte[7 * 4];
413  Buffer.BlockCopy(throttleData, 0, newData, 0, 7 * 4);
414 
415  for (int i = 0; i < 7; i++)
416  Array.Reverse(newData, i * 4, 4);
417 
418  adjData = newData;
419  }
420  else
421  {
422  adjData = throttleData;
423  }
424 
425  // 0.125f converts from bits to bytes
426  float scale = 0.125f * factor;
427  int resend = (int)(BitConverter.ToSingle(adjData, pos) * scale); pos += 4;
428  int land = (int)(BitConverter.ToSingle(adjData, pos) * scale); pos += 4;
429  int wind = (int)(BitConverter.ToSingle(adjData, pos) * scale); pos += 4;
430  int cloud = (int)(BitConverter.ToSingle(adjData, pos) * scale); pos += 4;
431  int task = (int)(BitConverter.ToSingle(adjData, pos) * scale); pos += 4;
432  int texture = (int)(BitConverter.ToSingle(adjData, pos) * scale); pos += 4;
433  int asset = (int)(BitConverter.ToSingle(adjData, pos) * scale);
434 
435 
436 
437  // Make sure none of the throttles are set below our packet MTU,
438  // otherwise a throttle could become permanently clogged
439 
440 /* now using floats
441  resend = Math.Max(resend, LLUDPServer.MTU);
442  land = Math.Max(land, LLUDPServer.MTU);
443  wind = Math.Max(wind, LLUDPServer.MTU);
444  cloud = Math.Max(cloud, LLUDPServer.MTU);
445  task = Math.Max(task, LLUDPServer.MTU);
446  texture = Math.Max(texture, LLUDPServer.MTU);
447  asset = Math.Max(asset, LLUDPServer.MTU);
448 */
449 
450  // Since most textures are now delivered through http, make it possible
451  // to cannibalize some of the bw from the texture throttle to use for
452  // the task queue (e.g. object updates)
453  task = task + (int)(m_cannibalrate * texture);
454  texture = (int)((1 - m_cannibalrate) * texture);
455 
456  int total = resend + land + wind + cloud + task + texture + asset;
457 
458  float m_burst = total * m_burstTime;
459 
460  if (ThrottleDebugLevel > 0)
461  {
462  m_log.DebugFormat(
463  "[LLUDPCLIENT]: {0} is setting throttles in {1} to Resend={2}, Land={3}, Wind={4}, Cloud={5}, Task={6}, Texture={7}, Asset={8}, TOTAL = {9}",
464  AgentID, m_udpServer.Scene.Name, resend, land, wind, cloud, task, texture, asset, total);
465  }
466 
467  TokenBucket bucket;
468 
469  bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
470  bucket.RequestedDripRate = resend;
471  bucket.RequestedBurst = m_burst;
472 
473  bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land];
474  bucket.RequestedDripRate = land;
475  bucket.RequestedBurst = m_burst;
476 
477  bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind];
478  bucket.RequestedDripRate = wind;
479  bucket.RequestedBurst = m_burst;
480 
481  bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud];
482  bucket.RequestedDripRate = cloud;
483  bucket.RequestedBurst = m_burst;
484 
485  bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset];
486  bucket.RequestedDripRate = asset;
487  bucket.RequestedBurst = m_burst;
488 
489  bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task];
490  bucket.RequestedDripRate = task;
491  bucket.RequestedBurst = m_burst;
492 
493  bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture];
494  bucket.RequestedDripRate = texture;
495  bucket.RequestedBurst = m_burst;
496 
497  // Reset the packed throttles cached data
498  m_packedThrottles = null;
499  }
500 
501  public byte[] GetThrottlesPacked(float multiplier)
502  {
503  byte[] data = m_packedThrottles;
504 
505  if (data == null)
506  {
507  float rate;
508 
509  data = new byte[7 * 4];
510  int i = 0;
511 
512  // multiply by 8 to convert bytes back to bits
513  multiplier *= 8;
514 
515  rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate * multiplier;
516  Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
517 
518  rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate * multiplier;
519  Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
520 
521  rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate * multiplier;
522  Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
523 
524  rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate * multiplier;
525  Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
526 
527  rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate * multiplier;
528  Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
529 
530  rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate * multiplier;
531  Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
532 
533  rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate * multiplier;
534  Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
535 
536  m_packedThrottles = data;
537  }
538 
539  return data;
540  }
541 
542  public int GetCatBytesCanSend(ThrottleOutPacketType cat, int timeMS)
543  {
544  int icat = (int)cat;
545  if (icat > 0 && icat < THROTTLE_CATEGORY_COUNT)
546  {
547  TokenBucket bucket = m_throttleCategories[icat];
548  return bucket.GetCatBytesCanSend(timeMS);
549  }
550  else
551  return 0;
552  }
553 
563  public bool EnqueueOutgoing(OutgoingPacket packet, bool forceQueue)
564  {
565  return EnqueueOutgoing(packet, forceQueue, false);
566  }
567 
568  public bool EnqueueOutgoing(OutgoingPacket packet, bool forceQueue, bool highPriority)
569  {
570  int category = (int)packet.Category;
571 
572  if (category >= 0 && category < m_packetOutboxes.Length)
573  {
574  DoubleLocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category];
575 
576  if (m_deliverPackets == false)
577  {
578  queue.Enqueue(packet, highPriority);
579  return true;
580  }
581 
582  TokenBucket bucket = m_throttleCategories[category];
583 
584  // Don't send this packet if queue is not empty
585  if (queue.Count > 0 || m_nextPackets[category] != null)
586  {
587  queue.Enqueue(packet, highPriority);
588  return true;
589  }
590 
591  if (!forceQueue && bucket.CheckTokens(packet.Buffer.DataLength))
592  {
593  // enough tokens so it can be sent imediatly by caller
594  bucket.RemoveTokens(packet.Buffer.DataLength);
595  return false;
596  }
597  else
598  {
599  // Force queue specified or not enough tokens in the bucket, queue this packet
600  queue.Enqueue(packet, highPriority);
601  return true;
602  }
603  }
604  else
605  {
606  // We don't have a token bucket for this category, so it will not be queued
607  return false;
608  }
609 
610  }
611 
628  public bool DequeueOutgoing()
629  {
630 // if (m_deliverPackets == false) return false;
631 
632  OutgoingPacket packet = null;
633  DoubleLocklessQueue<OutgoingPacket> queue;
634  TokenBucket bucket;
635  bool packetSent = false;
636  ThrottleOutPacketTypeFlags emptyCategories = 0;
637 
638  //string queueDebugOutput = String.Empty; // Serious debug business
639 
640  for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
641  {
642  bucket = m_throttleCategories[i];
643  //queueDebugOutput += m_packetOutboxes[i].Count + " "; // Serious debug business
644 
645  if (m_nextPackets[i] != null)
646  {
647  // This bucket was empty the last time we tried to send a packet,
648  // leaving a dequeued packet still waiting to be sent out. Try to
649  // send it again
650  OutgoingPacket nextPacket = m_nextPackets[i];
651  if (bucket.RemoveTokens(nextPacket.Buffer.DataLength))
652  {
653  // Send the packet
654  m_udpServer.SendPacketFinal(nextPacket);
655  m_nextPackets[i] = null;
656  packetSent = true;
657 
658  if (m_packetOutboxes[i].Count < 5)
659  emptyCategories |= CategoryToFlag(i);
660  }
661  }
662  else
663  {
664  // No dequeued packet waiting to be sent, try to pull one off
665  // this queue
666  queue = m_packetOutboxes[i];
667  if (queue != null)
668  {
669  bool success = false;
670  try
671  {
672  success = queue.Dequeue(out packet);
673  }
674  catch
675  {
676  m_packetOutboxes[i] = new DoubleLocklessQueue<OutgoingPacket>();
677  }
678  if (success)
679  {
680  // A packet was pulled off the queue. See if we have
681  // enough tokens in the bucket to send it out
682  if (bucket.RemoveTokens(packet.Buffer.DataLength))
683  {
684  // Send the packet
685  m_udpServer.SendPacketFinal(packet);
686  packetSent = true;
687 
688  if (queue.Count < 5)
689  emptyCategories |= CategoryToFlag(i);
690  }
691  else
692  {
693  // Save the dequeued packet for the next iteration
694  m_nextPackets[i] = packet;
695  }
696 
697  }
698  else
699  {
700  // No packets in this queue. Fire the queue empty callback
701  // if it has not been called recently
702  emptyCategories |= CategoryToFlag(i);
703  }
704  }
705  else
706  {
707  m_packetOutboxes[i] = new DoubleLocklessQueue<OutgoingPacket>();
708  emptyCategories |= CategoryToFlag(i);
709  }
710  }
711  }
712 
713  if (emptyCategories != 0)
714  BeginFireQueueEmpty(emptyCategories);
715 
716  //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business
717  return packetSent;
718  }
719 
728  public void UpdateRoundTrip(float r)
729  {
730  const float ALPHA = 0.125f;
731  const float BETA = 0.25f;
732  const float K = 4.0f;
733 
734  if (RTTVAR == 0.0f)
735  {
736  // First RTT measurement
737  SRTT = r;
738  RTTVAR = r * 0.5f;
739  }
740  else
741  {
742  // Subsequence RTT measurement
743  RTTVAR = (1.0f - BETA) * RTTVAR + BETA * Math.Abs(SRTT - r);
744  SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r;
745  }
746 
747  int rto = (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR));
748 
749  // Clamp the retransmission timeout to manageable values
750  rto = Utils.Clamp(rto, m_defaultRTO, m_maxRTO);
751 
752  RTO = rto;
753 
754  //if (RTO != rto)
755  // m_log.Debug("[LLUDPCLIENT]: Setting RTO to " + RTO + "ms from " + rto + "ms with an RTTVAR of " +
756  //RTTVAR + " based on new RTT of " + r + "ms");
757  }
758 
763  public void BackoffRTO()
764  {
765  // Reset SRTT and RTTVAR, we assume they are bogus since things
766  // didn't work out and we're backing off the timeout
767  SRTT = 0.0f;
768  RTTVAR = 0.0f;
769 
770  // Double the retransmission timeout
771  RTO = Math.Min(RTO * 2, m_maxRTO);
772  }
773 
774 
775  const int MIN_CALLBACK_MS = 10;
776 
782  private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories)
783  {
784  if (!m_isQueueEmptyRunning)
785  {
786  int start = Environment.TickCount & Int32.MaxValue;
787 
788  if (start < m_nextOnQueueEmpty)
789  return;
790 
791  m_isQueueEmptyRunning = true;
792 
793  m_nextOnQueueEmpty = start + MIN_CALLBACK_MS;
794  if (m_nextOnQueueEmpty == 0)
795  m_nextOnQueueEmpty = 1;
796 
797  if (HasUpdates(categories))
798  {
799  if (!m_udpServer.OqrEngine.IsRunning)
800  {
801  // Asynchronously run the callback
802  Util.FireAndForget(FireQueueEmpty, categories, "LLUDPClient.BeginFireQueueEmpty");
803  }
804  else
805  {
806  m_udpServer.OqrEngine.QueueJob(AgentID.ToString(), () => FireQueueEmpty(categories));
807  }
808  }
809  else
810  {
811  m_isQueueEmptyRunning = false;
812  }
813  }
814  }
815 
816  private bool m_isQueueEmptyRunning;
817 
818 
826  public void FireQueueEmpty(object o)
827  {
829  QueueEmpty callback = OnQueueEmpty;
830 
831  if (callback != null)
832  {
833  // if (m_udpServer.IsRunningOutbound)
834  // {
835  try { callback(categories); }
836  catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); }
837  // }
838  }
839 
840  m_isQueueEmptyRunning = false;
841  }
842 
843  internal void ForceThrottleSetting(int throttle, int setting)
844  {
845  if (throttle > 0 && throttle < THROTTLE_CATEGORY_COUNT)
846  m_throttleCategories[throttle].RequestedDripRate = Math.Max(setting, LLUDPServer.MTU);
847  }
848 
849  internal int GetThrottleSetting(int throttle)
850  {
851  if (throttle > 0 && throttle < THROTTLE_CATEGORY_COUNT)
852  return (int)m_throttleCategories[throttle].RequestedDripRate;
853  else
854  return 0;
855  }
856 
863  private static ThrottleOutPacketTypeFlags CategoryToFlag(int i)
864  {
866 
867  /*
868  * Land = 1,
870  Wind = 2,
872  Cloud = 3,
874  Task = 4,
876  Texture = 5,
878  Asset = 6,
879  */
880 
881  switch (category)
882  {
883  case ThrottleOutPacketType.Land:
884  return ThrottleOutPacketTypeFlags.Land;
885  case ThrottleOutPacketType.Wind:
886  return ThrottleOutPacketTypeFlags.Wind;
887  case ThrottleOutPacketType.Cloud:
888  return ThrottleOutPacketTypeFlags.Cloud;
889  case ThrottleOutPacketType.Task:
890  return ThrottleOutPacketTypeFlags.Task;
891  case ThrottleOutPacketType.Texture:
892  return ThrottleOutPacketTypeFlags.Texture;
893  case ThrottleOutPacketType.Asset:
894  return ThrottleOutPacketTypeFlags.Asset;
895  default:
896  return 0;
897  }
898  }
899  }
900 
901  public class DoubleLocklessQueue<T> : OpenSim.Framework.LocklessQueue<T>
902  {
903  OpenSim.Framework.LocklessQueue<T> highQueue = new OpenSim.Framework.LocklessQueue<T>();
904 
905  public override int Count
906  {
907  get
908  {
909  return base.Count + highQueue.Count;
910  }
911  }
912 
913  public override bool Dequeue(out T item)
914  {
915  if (highQueue.Dequeue(out item))
916  return true;
917 
918  return base.Dequeue(out item);
919  }
920 
921  public void Enqueue(T item, bool highPriority)
922  {
923  if (highPriority)
924  highQueue.Enqueue(item);
925  else
926  Enqueue(item);
927  }
928  }
929 }
void BackoffRTO()
Exponential backoff of the retransmission timeout, per section 5.5 of RFC 2988
Definition: LLUDPClient.cs:763
A circular buffer and hashset for tracking incoming packet sequence numbers
readonly UUID AgentID
AgentID for this client
Definition: LLUDPClient.cs:117
A hierarchical token bucket for bandwidth throttling. See http://en.wikipedia.org/wiki/Token_bucket f...
Definition: TokenBucket.cs:42
PacketStats OnPacketStats
Fired when updated networking stats are produced for this client
Definition: LLUDPClient.cs:109
LLUDPClient(LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint, int defaultRTO, int maxRTO)
Default constructor
Definition: LLUDPClient.cs:237
int PacketsReceived
Number of packets received from this client
Definition: LLUDPClient.cs:156
int TickLastPacketReceived
Environment.TickCount when the last packet was received for this client
Definition: LLUDPClient.cs:140
void SetThrottles(byte[] throttleData, float factor)
Definition: LLUDPClient.cs:405
bool AdaptiveThrottlesEnabled
Flag used to enable adaptive throttles
float SRTT
Smoothed round-trip time. A smoothed average of the round-trip time for sending a reliable packet to ...
Definition: LLUDPClient.cs:144
void SetClientInfo(ClientInfo info)
Modifies the UDP throttles
Definition: LLUDPClient.cs:324
int GetCatBytesCanSend(ThrottleOutPacketType cat, int timeMS)
Definition: LLUDPClient.cs:542
readonly IPEndPoint RemoteEndPoint
The remote address of the connected client
Definition: LLUDPClient.cs:119
int UnackedBytes
Total byte count of unacked packets sent to this client
Definition: LLUDPClient.cs:162
delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes)
Fired when updated networking stats are produced for this client
string GetStats()
Return statistics information about client packet queues.
Definition: LLUDPClient.cs:367
bool IsPaused
True when this connection is paused, otherwise false
Definition: LLUDPClient.cs:138
OpenSim.Region.ClientStack.LindenUDP.TokenBucket TokenBucket
Definition: LLUDPClient.cs:38
Tracks state for a client UDP connection and provides client-specific methods
Definition: LLUDPClient.cs:66
ClientInfo GetClientInfo()
Gets information about this client connection
Definition: LLUDPClient.cs:303
bool EnqueueOutgoing(OutgoingPacket packet, bool forceQueue)
Queue an outgoing packet if appropriate.
Definition: LLUDPClient.cs:563
int RTO
Retransmission timeout. Packets that have not been acknowledged in this number of milliseconds or lon...
Definition: LLUDPClient.cs:151
byte CurrentPingSequence
Current ping sequence number
Definition: LLUDPClient.cs:134
bool RemoveTokens(int amount)
Remove a given number of tokens from the bucket
Definition: TokenBucket.cs:272
Holds drip rates and maximum burst rates for throttling with hierarchical token buckets. The maximum burst rates set here are hard limits and can not be overridden by client requests
bool DequeueOutgoing()
Loops through all of the packet queues for this client and tries to send an outgoing packet from each...
Definition: LLUDPClient.cs:628
UDPPacketBuffer Buffer
Packet data to send
The LLUDP server for a region. This handles incoming and outgoing packets for all UDP connections to ...
Definition: LLUDPServer.cs:252
float RTTVAR
Round-trip time variance. Measures the consistency of round-trip times
Definition: LLUDPClient.cs:146
Interactive OpenSim region server
Definition: OpenSim.cs:55
void UpdateRoundTrip(float r)
Called when an ACK packet is received and a round-trip time for a packet is calculated. This is used to calculate the smoothed round-trip time, round trip time variance, and finally the retransmission timeout
Definition: LLUDPClient.cs:728
int GetTotalPacketsQueuedCount()
Get the total number of pakcets queued for this client.
Definition: LLUDPClient.cs:336
int PacketsResent
Number of packets resent to this client
Definition: LLUDPClient.cs:160
readonly float TickCountResolution
The measured resolution of Environment.TickCount
Definition: LLUDPServer.cs:274
ThrottleOutPacketType Category
Category this packet belongs to
delegate void QueueEmpty(ThrottleOutPacketTypeFlags categories)
Fired when the queue for one or more packet categories is empty. This event can be hooked to put more...
Special collection that is optimized for tracking unacknowledged packets
readonly uint CircuitCode
Circuit code that this client is connected on
Definition: LLUDPClient.cs:121
void Shutdown()
Shuts down this client connection
Definition: LLUDPClient.cs:284
void FireQueueEmpty(object o)
Fires the OnQueueEmpty callback and sets the minimum time that it can be called again ...
Definition: LLUDPClient.cs:826
int PacketsSent
Number of packets sent to this client
Definition: LLUDPClient.cs:158
int GetPacketsQueuedCount(ThrottleOutPacketType throttleType)
Get the number of packets queued for the given throttle type.
Definition: LLUDPClient.cs:351
QueueEmpty OnQueueEmpty
Fired when the queue for a packet category is empty. This event can be hooked to put more data on the...
Definition: LLUDPClient.cs:112
int BytesSinceLastACK
Number of bytes received since the last acknowledgement was sent out. This is used to loosely follow ...
Definition: LLUDPClient.cs:154
int CurrentSequence
Current packet sequence number
Definition: LLUDPClient.cs:132
Holds a reference to the LLUDPClientthis packet is destined for, along with the serialized packet dat...
bool EnqueueOutgoing(OutgoingPacket packet, bool forceQueue, bool highPriority)
Definition: LLUDPClient.cs:568
Func< ThrottleOutPacketTypeFlags, bool > HasUpdates
Definition: LLUDPClient.cs:114