diff options
author | John Hurliman | 2009-10-21 18:03:41 -0700 |
---|---|---|
committer | John Hurliman | 2009-10-21 18:03:41 -0700 |
commit | 6492640e72776d9f0a015e6a719c8cef28ccb7e3 (patch) | |
tree | af67792558e46e5b5abce59032223b8bae388859 /OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | |
parent | * Clarified what FireQueueEmpty is doing with a MIN_CALLBACK_MS constant and ... (diff) | |
download | opensim-SC-6492640e72776d9f0a015e6a719c8cef28ccb7e3.zip opensim-SC-6492640e72776d9f0a015e6a719c8cef28ccb7e3.tar.gz opensim-SC-6492640e72776d9f0a015e6a719c8cef28ccb7e3.tar.bz2 opensim-SC-6492640e72776d9f0a015e6a719c8cef28ccb7e3.tar.xz |
* Change the OnQueueEmpty signature to send the flags of the queues that are empty instead of firing once per empty queue
* Change the OnQueueEmpty firing to use a minimum time until next fire instead of a sleep
* Set OutgoingPacket.TickCount = 0 earlier to avoid extra resends when things are running slowly (inside a profiler, for example)
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 110 |
1 files changed, 77 insertions, 33 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 2d86a40..a9bc7d2 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | |||
@@ -50,11 +50,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
50 | /// are waiting on ACKs for</param> | 50 | /// are waiting on ACKs for</param> |
51 | public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes); | 51 | public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes); |
52 | /// <summary> | 52 | /// <summary> |
53 | /// Fired when the queue for a packet category is empty. This event can be | 53 | /// Fired when the queue for one or more packet categories is empty. This |
54 | /// hooked to put more data on the empty queue | 54 | /// event can be hooked to put more data on the empty queues |
55 | /// </summary> | 55 | /// </summary> |
56 | /// <param name="category">Category of the packet queue that is empty</param> | 56 | /// <param name="category">Categories of the packet queues that are empty</param> |
57 | public delegate void QueueEmpty(ThrottleOutPacketType category); | 57 | public delegate void QueueEmpty(ThrottleOutPacketTypeFlags categories); |
58 | 58 | ||
59 | #endregion Delegates | 59 | #endregion Delegates |
60 | 60 | ||
@@ -128,6 +128,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
128 | private int m_packetsReceivedReported; | 128 | private int m_packetsReceivedReported; |
129 | /// <summary>Total number of sent packets that we have reported to the OnPacketStats event(s)</summary> | 129 | /// <summary>Total number of sent packets that we have reported to the OnPacketStats event(s)</summary> |
130 | private int m_packetsSentReported; | 130 | private int m_packetsSentReported; |
131 | /// <summary>Holds the Environment.TickCount value of when the next OnQueueEmpty can be fired</summary> | ||
132 | private int m_nextOnQueueEmpty = 1; | ||
131 | 133 | ||
132 | /// <summary>Throttle bucket for this agent's connection</summary> | 134 | /// <summary>Throttle bucket for this agent's connection</summary> |
133 | private readonly TokenBucket m_throttle; | 135 | private readonly TokenBucket m_throttle; |
@@ -140,9 +142,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
140 | /// <summary>A container that can hold one packet for each outbox, used to store | 142 | /// <summary>A container that can hold one packet for each outbox, used to store |
141 | /// dequeued packets that are being held for throttling</summary> | 143 | /// dequeued packets that are being held for throttling</summary> |
142 | private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; | 144 | private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; |
143 | /// <summary>Flags to prevent queue empty callbacks from stacking up on | ||
144 | /// top of each other</summary> | ||
145 | private readonly bool[] m_onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT]; | ||
146 | /// <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> |
147 | private readonly LLUDPServer m_udpServer; | 146 | private readonly LLUDPServer m_udpServer; |
148 | 147 | ||
@@ -405,6 +404,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
405 | OpenSim.Framework.LocklessQueue<OutgoingPacket> queue; | 404 | OpenSim.Framework.LocklessQueue<OutgoingPacket> queue; |
406 | TokenBucket bucket; | 405 | TokenBucket bucket; |
407 | bool packetSent = false; | 406 | bool packetSent = false; |
407 | ThrottleOutPacketTypeFlags emptyCategories = 0; | ||
408 | 408 | ||
409 | //string queueDebugOutput = String.Empty; // Serious debug business | 409 | //string queueDebugOutput = String.Empty; // Serious debug business |
410 | 410 | ||
@@ -452,17 +452,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
452 | // empty callback now so it has a chance to fill before we | 452 | // empty callback now so it has a chance to fill before we |
453 | // get back here | 453 | // get back here |
454 | if (queue.Count == 0) | 454 | if (queue.Count == 0) |
455 | BeginFireQueueEmpty(i); | 455 | emptyCategories |= CategoryToFlag(i); |
456 | } | 456 | } |
457 | else | 457 | else |
458 | { | 458 | { |
459 | // No packets in this queue. Fire the queue empty callback | 459 | // No packets in this queue. Fire the queue empty callback |
460 | // if it has not been called recently | 460 | // if it has not been called recently |
461 | BeginFireQueueEmpty(i); | 461 | emptyCategories |= CategoryToFlag(i); |
462 | } | 462 | } |
463 | } | 463 | } |
464 | } | 464 | } |
465 | 465 | ||
466 | if (emptyCategories != 0) | ||
467 | BeginFireQueueEmpty(emptyCategories); | ||
468 | |||
466 | //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business | 469 | //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business |
467 | return packetSent; | 470 | return packetSent; |
468 | } | 471 | } |
@@ -509,49 +512,90 @@ namespace OpenSim.Region.ClientStack.LindenUDP | |||
509 | /// </summary> | 512 | /// </summary> |
510 | /// <param name="throttleIndex">Throttle category to fire the callback | 513 | /// <param name="throttleIndex">Throttle category to fire the callback |
511 | /// for</param> | 514 | /// for</param> |
512 | private void BeginFireQueueEmpty(int throttleIndex) | 515 | private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories) |
513 | { | 516 | { |
514 | // Unknown is -1 and Resend is 0. Make sure we are only firing the | 517 | if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty) |
515 | // callback for categories other than those | ||
516 | if (throttleIndex > 0) | ||
517 | { | 518 | { |
518 | if (!m_onQueueEmptyRunning[throttleIndex]) | 519 | // Use a value of 0 to signal that FireQueueEmpty is running |
519 | { | 520 | m_nextOnQueueEmpty = 0; |
520 | m_onQueueEmptyRunning[throttleIndex] = true; | 521 | // Asynchronously run the callback |
521 | Util.FireAndForget(FireQueueEmpty, throttleIndex); | 522 | Util.FireAndForget(FireQueueEmpty, categories); |
522 | } | ||
523 | } | 523 | } |
524 | } | 524 | } |
525 | 525 | ||
526 | /// <summary> | 526 | /// <summary> |
527 | /// Checks to see if this queue empty callback is already running, | 527 | /// Fires the OnQueueEmpty callback and sets the minimum time that it |
528 | /// then firing the event | 528 | /// can be called again |
529 | /// </summary> | 529 | /// </summary> |
530 | /// <param name="o">Throttle category to fire the callback for, stored | 530 | /// <param name="o">Throttle categories to fire the callback for, |
531 | /// as an object to match the WaitCallback delegate signature</param> | 531 | /// stored as an object to match the WaitCallback delegate |
532 | /// signature</param> | ||
532 | private void FireQueueEmpty(object o) | 533 | private void FireQueueEmpty(object o) |
533 | { | 534 | { |
534 | const int MIN_CALLBACK_MS = 30; | 535 | const int MIN_CALLBACK_MS = 30; |
535 | 536 | ||
536 | int i = (int)o; | 537 | ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o; |
537 | ThrottleOutPacketType type = (ThrottleOutPacketType)i; | ||
538 | QueueEmpty callback = OnQueueEmpty; | 538 | QueueEmpty callback = OnQueueEmpty; |
539 | 539 | ||
540 | int start = Environment.TickCount & Int32.MaxValue; | 540 | int start = Environment.TickCount & Int32.MaxValue; |
541 | 541 | ||
542 | if (callback != null) | 542 | if (callback != null) |
543 | { | 543 | { |
544 | try { callback(type); } | 544 | try { callback(categories); } |
545 | catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); } | 545 | catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); } |
546 | } | 546 | } |
547 | 547 | ||
548 | // Make sure all queue empty calls take at least some amount of time, | 548 | m_nextOnQueueEmpty = start + MIN_CALLBACK_MS; |
549 | // otherwise we'll peg a CPU trying to fire these too fast | 549 | if (m_nextOnQueueEmpty == 0) |
550 | int elapsedMS = (Environment.TickCount & Int32.MaxValue) - start; | 550 | m_nextOnQueueEmpty = 1; |
551 | if (elapsedMS < MIN_CALLBACK_MS) | 551 | } |
552 | System.Threading.Thread.Sleep(MIN_CALLBACK_MS - elapsedMS); | ||
553 | 552 | ||
554 | m_onQueueEmptyRunning[i] = false; | 553 | /// <summary> |
554 | /// Converts a <seealso cref="ThrottleOutPacketType"/> integer to a | ||
555 | /// flag value | ||
556 | /// </summary> | ||
557 | /// <param name="i">Throttle category to convert</param> | ||
558 | /// <returns>Flag representation of the throttle category</returns> | ||
559 | private static ThrottleOutPacketTypeFlags CategoryToFlag(int i) | ||
560 | { | ||
561 | ThrottleOutPacketType category = (ThrottleOutPacketType)i; | ||
562 | |||
563 | /* | ||
564 | * Land = 1, | ||
565 | /// <summary>Wind data</summary> | ||
566 | Wind = 2, | ||
567 | /// <summary>Cloud data</summary> | ||
568 | Cloud = 3, | ||
569 | /// <summary>Any packets that do not fit into the other throttles</summary> | ||
570 | Task = 4, | ||
571 | /// <summary>Texture assets</summary> | ||
572 | Texture = 5, | ||
573 | /// <summary>Non-texture assets</summary> | ||
574 | Asset = 6, | ||
575 | /// <summary>Avatar and primitive data</summary> | ||
576 | /// <remarks>This is a sub-category of Task</remarks> | ||
577 | State = 7, | ||
578 | */ | ||
579 | |||
580 | switch (category) | ||
581 | { | ||
582 | case ThrottleOutPacketType.Land: | ||
583 | return ThrottleOutPacketTypeFlags.Land; | ||
584 | case ThrottleOutPacketType.Wind: | ||
585 | return ThrottleOutPacketTypeFlags.Wind; | ||
586 | case ThrottleOutPacketType.Cloud: | ||
587 | return ThrottleOutPacketTypeFlags.Cloud; | ||
588 | case ThrottleOutPacketType.Task: | ||
589 | return ThrottleOutPacketTypeFlags.Task; | ||
590 | case ThrottleOutPacketType.Texture: | ||
591 | return ThrottleOutPacketTypeFlags.Texture; | ||
592 | case ThrottleOutPacketType.Asset: | ||
593 | return ThrottleOutPacketTypeFlags.Asset; | ||
594 | case ThrottleOutPacketType.State: | ||
595 | return ThrottleOutPacketTypeFlags.State; | ||
596 | default: | ||
597 | return 0; | ||
598 | } | ||
555 | } | 599 | } |
556 | } | 600 | } |
557 | } | 601 | } |