From 6492640e72776d9f0a015e6a719c8cef28ccb7e3 Mon Sep 17 00:00:00 2001
From: John Hurliman
Date: Wed, 21 Oct 2009 18:03:41 -0700
Subject: * 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)
---
.../Region/ClientStack/LindenUDP/LLClientView.cs | 50 +++++-----
.../Region/ClientStack/LindenUDP/LLUDPClient.cs | 110 ++++++++++++++-------
.../Region/ClientStack/LindenUDP/LLUDPServer.cs | 4 -
.../LindenUDP/UnackedPacketCollection.cs | 5 +
4 files changed, 108 insertions(+), 61 deletions(-)
(limited to 'OpenSim/Region/ClientStack/LindenUDP')
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 0bb7a71..432fee7 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -3550,33 +3550,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OutPacket(attach, ThrottleOutPacketType.Task);
}
- void HandleQueueEmpty(ThrottleOutPacketType queue)
+ void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories)
{
- switch (queue)
+ if ((categories & ThrottleOutPacketTypeFlags.Task) != 0)
{
- case ThrottleOutPacketType.Texture:
- ProcessTextureRequests();
- break;
- case ThrottleOutPacketType.Task:
- lock (m_avatarTerseUpdates.SyncRoot)
- {
- if (m_avatarTerseUpdates.Count > 0)
- ProcessAvatarTerseUpdates();
- }
- break;
- case ThrottleOutPacketType.State:
- lock (m_primFullUpdates.SyncRoot)
- {
- if (m_primFullUpdates.Count > 0)
- ProcessPrimFullUpdates();
- }
+ lock (m_avatarTerseUpdates.SyncRoot)
+ {
+ if (m_avatarTerseUpdates.Count > 0)
+ ProcessAvatarTerseUpdates();
+ }
+ }
- lock (m_primTerseUpdates.SyncRoot)
- {
- if (m_primTerseUpdates.Count > 0)
- ProcessPrimTerseUpdates();
- }
- break;
+ if ((categories & ThrottleOutPacketTypeFlags.State) != 0)
+ {
+ lock (m_primFullUpdates.SyncRoot)
+ {
+ if (m_primFullUpdates.Count > 0)
+ ProcessPrimFullUpdates();
+ }
+
+ lock (m_primTerseUpdates.SyncRoot)
+ {
+ if (m_primTerseUpdates.Count > 0)
+ ProcessPrimTerseUpdates();
+ }
+ }
+
+ if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0)
+ {
+ ProcessTextureRequests();
}
}
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
/// are waiting on ACKs for
public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
///
- /// Fired when the queue for a packet category is empty. This event can be
- /// hooked to put more data on the empty queue
+ /// Fired when the queue for one or more packet categories is empty. This
+ /// event can be hooked to put more data on the empty queues
///
- /// Category of the packet queue that is empty
- public delegate void QueueEmpty(ThrottleOutPacketType category);
+ /// Categories of the packet queues that are empty
+ public delegate void QueueEmpty(ThrottleOutPacketTypeFlags categories);
#endregion Delegates
@@ -128,6 +128,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private int m_packetsReceivedReported;
/// Total number of sent packets that we have reported to the OnPacketStats event(s)
private int m_packetsSentReported;
+ /// Holds the Environment.TickCount value of when the next OnQueueEmpty can be fired
+ private int m_nextOnQueueEmpty = 1;
/// Throttle bucket for this agent's connection
private readonly TokenBucket m_throttle;
@@ -140,9 +142,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// A container that can hold one packet for each outbox, used to store
/// dequeued packets that are being held for throttling
private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
- /// Flags to prevent queue empty callbacks from stacking up on
- /// top of each other
- private readonly bool[] m_onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT];
/// A reference to the LLUDPServer that is managing this client
private readonly LLUDPServer m_udpServer;
@@ -405,6 +404,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OpenSim.Framework.LocklessQueue queue;
TokenBucket bucket;
bool packetSent = false;
+ ThrottleOutPacketTypeFlags emptyCategories = 0;
//string queueDebugOutput = String.Empty; // Serious debug business
@@ -452,17 +452,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// empty callback now so it has a chance to fill before we
// get back here
if (queue.Count == 0)
- BeginFireQueueEmpty(i);
+ emptyCategories |= CategoryToFlag(i);
}
else
{
// No packets in this queue. Fire the queue empty callback
// if it has not been called recently
- BeginFireQueueEmpty(i);
+ emptyCategories |= CategoryToFlag(i);
}
}
}
+ if (emptyCategories != 0)
+ BeginFireQueueEmpty(emptyCategories);
+
//m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business
return packetSent;
}
@@ -509,49 +512,90 @@ namespace OpenSim.Region.ClientStack.LindenUDP
///
/// Throttle category to fire the callback
/// for
- private void BeginFireQueueEmpty(int throttleIndex)
+ private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories)
{
- // Unknown is -1 and Resend is 0. Make sure we are only firing the
- // callback for categories other than those
- if (throttleIndex > 0)
+ if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty)
{
- if (!m_onQueueEmptyRunning[throttleIndex])
- {
- m_onQueueEmptyRunning[throttleIndex] = true;
- Util.FireAndForget(FireQueueEmpty, throttleIndex);
- }
+ // Use a value of 0 to signal that FireQueueEmpty is running
+ m_nextOnQueueEmpty = 0;
+ // Asynchronously run the callback
+ Util.FireAndForget(FireQueueEmpty, categories);
}
}
///
- /// Checks to see if this queue empty callback is already running,
- /// then firing the event
+ /// Fires the OnQueueEmpty callback and sets the minimum time that it
+ /// can be called again
///
- /// Throttle category to fire the callback for, stored
- /// as an object to match the WaitCallback delegate signature
+ /// Throttle categories to fire the callback for,
+ /// stored as an object to match the WaitCallback delegate
+ /// signature
private void FireQueueEmpty(object o)
{
const int MIN_CALLBACK_MS = 30;
- int i = (int)o;
- ThrottleOutPacketType type = (ThrottleOutPacketType)i;
+ ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o;
QueueEmpty callback = OnQueueEmpty;
-
+
int start = Environment.TickCount & Int32.MaxValue;
if (callback != null)
{
- try { callback(type); }
- catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); }
+ try { callback(categories); }
+ catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); }
}
- // Make sure all queue empty calls take at least some amount of time,
- // otherwise we'll peg a CPU trying to fire these too fast
- int elapsedMS = (Environment.TickCount & Int32.MaxValue) - start;
- if (elapsedMS < MIN_CALLBACK_MS)
- System.Threading.Thread.Sleep(MIN_CALLBACK_MS - elapsedMS);
+ m_nextOnQueueEmpty = start + MIN_CALLBACK_MS;
+ if (m_nextOnQueueEmpty == 0)
+ m_nextOnQueueEmpty = 1;
+ }
- m_onQueueEmptyRunning[i] = false;
+ ///
+ /// Converts a integer to a
+ /// flag value
+ ///
+ /// Throttle category to convert
+ /// Flag representation of the throttle category
+ private static ThrottleOutPacketTypeFlags CategoryToFlag(int i)
+ {
+ ThrottleOutPacketType category = (ThrottleOutPacketType)i;
+
+ /*
+ * Land = 1,
+ /// Wind data
+ Wind = 2,
+ /// Cloud data
+ Cloud = 3,
+ /// Any packets that do not fit into the other throttles
+ Task = 4,
+ /// Texture assets
+ Texture = 5,
+ /// Non-texture assets
+ Asset = 6,
+ /// Avatar and primitive data
+ /// This is a sub-category of Task
+ State = 7,
+ */
+
+ switch (category)
+ {
+ case ThrottleOutPacketType.Land:
+ return ThrottleOutPacketTypeFlags.Land;
+ case ThrottleOutPacketType.Wind:
+ return ThrottleOutPacketTypeFlags.Wind;
+ case ThrottleOutPacketType.Cloud:
+ return ThrottleOutPacketTypeFlags.Cloud;
+ case ThrottleOutPacketType.Task:
+ return ThrottleOutPacketTypeFlags.Task;
+ case ThrottleOutPacketType.Texture:
+ return ThrottleOutPacketTypeFlags.Texture;
+ case ThrottleOutPacketType.Asset:
+ return ThrottleOutPacketTypeFlags.Asset;
+ case ThrottleOutPacketType.State:
+ return ThrottleOutPacketTypeFlags.State;
+ default:
+ return 0;
+ }
}
}
}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index 40d3771..a9f4b2c 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
@@ -424,10 +424,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
outgoingPacket.Category = ThrottleOutPacketType.Resend;
- // The TickCount will be set to the current time when the packet
- // is actually sent out again
- outgoingPacket.TickCount = 0;
-
// Bump up the resend count on this packet
Interlocked.Increment(ref outgoingPacket.ResendCount);
//Interlocked.Increment(ref Stats.ResentPackets);
diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
index 3e2e81c..e43f7cf 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
@@ -123,6 +123,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
if (expiredPackets == null)
expiredPackets = new List();
+
+ // The TickCount will be set to the current time when the packet
+ // is actually sent out again
+ packet.TickCount = 0;
+
expiredPackets.Add(packet);
}
}
--
cgit v1.1