diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 65 |
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 @@ | |||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Net; | 30 | using System.Net; |
31 | using log4net; | ||
31 | using OpenSim.Framework; | 32 | using OpenSim.Framework; |
32 | using OpenMetaverse; | 33 | using 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 | } |