aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs65
1 files changed, 52 insertions, 13 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index e7707a9..39472cb 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -28,6 +28,7 @@
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Net; 30using System.Net;
31using log4net;
31using OpenSim.Framework; 32using OpenSim.Framework;
32using OpenMetaverse; 33using OpenMetaverse;
33 34
@@ -59,6 +60,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
59 /// </summary> 60 /// </summary>
60 public sealed class LLUDPClient 61 public sealed class LLUDPClient
61 { 62 {
63 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
64
62 // FIXME: Make this a config setting 65 // FIXME: Make this a config setting
63 /// <summary>Percentage of the task throttle category that is allocated to avatar and prim 66 /// <summary>Percentage of the task throttle category that is allocated to avatar and prim
64 /// state updates</summary> 67 /// state updates</summary>
@@ -136,9 +139,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
136 /// <summary>A container that can hold one packet for each outbox, used to store 139 /// <summary>A container that can hold one packet for each outbox, used to store
137 /// dequeued packets that are being held for throttling</summary> 140 /// dequeued packets that are being held for throttling</summary>
138 private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; 141 private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
139 /// <summary>An optimization to store the length of dequeued packets being held 142 /// <summary>Flags to prevent queue empty callbacks from stacking up on
140 /// for throttling. This avoids expensive calls to Packet.Length</summary> 143 /// top of each other</summary>
141 private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT]; 144 private readonly bool[] onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT];
142 /// <summary>A reference to the LLUDPServer that is managing this client</summary> 145 /// <summary>A reference to the LLUDPServer that is managing this client</summary>
143 private readonly LLUDPServer udpServer; 146 private readonly LLUDPServer udpServer;
144 147
@@ -163,7 +166,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
163 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) 166 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
164 packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); 167 packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
165 168
166 throttle = new TokenBucket(parentThrottle, 0, 0); 169 throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total);
167 throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; 170 throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
168 throttleCategories[(int)ThrottleOutPacketType.Resend] = new TokenBucket(throttle, rates.ResendLimit, rates.Resend); 171 throttleCategories[(int)ThrottleOutPacketType.Resend] = new TokenBucket(throttle, rates.ResendLimit, rates.Resend);
169 throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land); 172 throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land);
@@ -401,10 +404,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
401 // This bucket was empty the last time we tried to send a packet, 404 // This bucket was empty the last time we tried to send a packet,
402 // leaving a dequeued packet still waiting to be sent out. Try to 405 // leaving a dequeued packet still waiting to be sent out. Try to
403 // send it again 406 // send it again
404 if (bucket.RemoveTokens(nextPacketLengths[i])) 407 OutgoingPacket nextPacket = nextPackets[i];
408 if (bucket.RemoveTokens(nextPacket.Buffer.DataLength))
405 { 409 {
406 // Send the packet 410 // Send the packet
407 udpServer.SendPacketFinal(nextPackets[i]); 411 udpServer.SendPacketFinal(nextPacket);
408 nextPackets[i] = null; 412 nextPackets[i] = null;
409 packetSent = true; 413 packetSent = true;
410 } 414 }
@@ -426,23 +430,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
426 } 430 }
427 else 431 else
428 { 432 {
429 // Save the dequeued packet and the length calculation for 433 // Save the dequeued packet for the next iteration
430 // the next iteration
431 nextPackets[i] = packet; 434 nextPackets[i] = packet;
432 nextPacketLengths[i] = packet.Buffer.DataLength;
433 } 435 }
434 436
435 // If the queue is empty after this dequeue, fire the queue 437 // If the queue is empty after this dequeue, fire the queue
436 // empty callback now so it has a chance to fill before we 438 // empty callback now so it has a chance to fill before we
437 // get back here 439 // get back here
438 if (queue.Count == 0) 440 if (queue.Count == 0)
439 FireQueueEmpty(i); 441 BeginFireQueueEmpty(i);
440 } 442 }
441 else 443 else
442 { 444 {
443 // No packets in this queue. Fire the queue empty callback 445 // No packets in this queue. Fire the queue empty callback
444 // if it has not been called recently 446 // if it has not been called recently
445 FireQueueEmpty(i); 447 BeginFireQueueEmpty(i);
446 } 448 }
447 } 449 }
448 } 450 }
@@ -450,6 +452,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
450 return packetSent; 452 return packetSent;
451 } 453 }
452 454
455 /// <summary>
456 /// Called when an ACK packet is received and a round-trip time for a
457 /// packet is calculated. This is used to calculate the smoothed
458 /// round-trip time, round trip time variance, and finally the
459 /// retransmission timeout
460 /// </summary>
461 /// <param name="r">Round-trip time of a single packet and its
462 /// acknowledgement</param>
453 public void UpdateRoundTrip(float r) 463 public void UpdateRoundTrip(float r)
454 { 464 {
455 const float ALPHA = 0.125f; 465 const float ALPHA = 0.125f;
@@ -475,11 +485,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP
475 // RTTVAR + " based on new RTT of " + r + "ms"); 485 // RTTVAR + " based on new RTT of " + r + "ms");
476 } 486 }
477 487
478 private void FireQueueEmpty(int queueIndex) 488 /// <summary>
489 /// Does an early check to see if this queue empty callback is already
490 /// running, then asynchronously firing the event
491 /// </summary>
492 /// <param name="throttleIndex">Throttle category to fire the callback
493 /// for</param>
494 private void BeginFireQueueEmpty(int throttleIndex)
479 { 495 {
496 if (!onQueueEmptyRunning[throttleIndex])
497 Util.FireAndForget(FireQueueEmpty, throttleIndex);
498 }
499
500 /// <summary>
501 /// Checks to see if this queue empty callback is already running,
502 /// then firing the event
503 /// </summary>
504 /// <param name="o">Throttle category to fire the callback for, stored
505 /// as an object to match the WaitCallback delegate signature</param>
506 private void FireQueueEmpty(object o)
507 {
508 int i = (int)o;
509 ThrottleOutPacketType type = (ThrottleOutPacketType)i;
480 QueueEmpty callback = OnQueueEmpty; 510 QueueEmpty callback = OnQueueEmpty;
511
481 if (callback != null) 512 if (callback != null)
482 Util.FireAndForget(delegate(object o) { callback((ThrottleOutPacketType)(int)o); }, queueIndex); 513 {
514 if (!onQueueEmptyRunning[i])
515 {
516 onQueueEmptyRunning[i] = true;
517 try { callback(type); }
518 catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); }
519 onQueueEmptyRunning[i] = false;
520 }
521 }
483 } 522 }
484 } 523 }
485} 524}