* 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)prioritization
parent
4e04f6b3a5
commit
6492640e72
|
@ -51,4 +51,16 @@ namespace OpenSim.Framework
|
||||||
/// <remarks>This is a sub-category of Task</remarks>
|
/// <remarks>This is a sub-category of Task</remarks>
|
||||||
State = 7,
|
State = 7,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum ThrottleOutPacketTypeFlags
|
||||||
|
{
|
||||||
|
Land = 1 << 0,
|
||||||
|
Wind = 1 << 1,
|
||||||
|
Cloud = 1 << 2,
|
||||||
|
Task = 1 << 3,
|
||||||
|
Texture = 1 << 4,
|
||||||
|
Asset = 1 << 5,
|
||||||
|
State = 1 << 6,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3550,21 +3550,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
OutPacket(attach, ThrottleOutPacketType.Task);
|
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)
|
lock (m_avatarTerseUpdates.SyncRoot)
|
||||||
{
|
{
|
||||||
if (m_avatarTerseUpdates.Count > 0)
|
if (m_avatarTerseUpdates.Count > 0)
|
||||||
ProcessAvatarTerseUpdates();
|
ProcessAvatarTerseUpdates();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
case ThrottleOutPacketType.State:
|
|
||||||
|
if ((categories & ThrottleOutPacketTypeFlags.State) != 0)
|
||||||
|
{
|
||||||
lock (m_primFullUpdates.SyncRoot)
|
lock (m_primFullUpdates.SyncRoot)
|
||||||
{
|
{
|
||||||
if (m_primFullUpdates.Count > 0)
|
if (m_primFullUpdates.Count > 0)
|
||||||
|
@ -3576,7 +3574,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
if (m_primTerseUpdates.Count > 0)
|
if (m_primTerseUpdates.Count > 0)
|
||||||
ProcessPrimTerseUpdates();
|
ProcessPrimTerseUpdates();
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
|
|
||||||
|
if ((categories & ThrottleOutPacketTypeFlags.Texture) != 0)
|
||||||
|
{
|
||||||
|
ProcessTextureRequests();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,11 +50,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// are waiting on ACKs for</param>
|
/// are waiting on ACKs for</param>
|
||||||
public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
|
public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fired when the queue for a packet category is empty. This event can be
|
/// Fired when the queue for one or more packet categories is empty. This
|
||||||
/// hooked to put more data on the empty queue
|
/// event can be hooked to put more data on the empty queues
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="category">Category of the packet queue that is empty</param>
|
/// <param name="category">Categories of the packet queues that are empty</param>
|
||||||
public delegate void QueueEmpty(ThrottleOutPacketType category);
|
public delegate void QueueEmpty(ThrottleOutPacketTypeFlags categories);
|
||||||
|
|
||||||
#endregion Delegates
|
#endregion Delegates
|
||||||
|
|
||||||
|
@ -128,6 +128,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
private int m_packetsReceivedReported;
|
private int m_packetsReceivedReported;
|
||||||
/// <summary>Total number of sent packets that we have reported to the OnPacketStats event(s)</summary>
|
/// <summary>Total number of sent packets that we have reported to the OnPacketStats event(s)</summary>
|
||||||
private int m_packetsSentReported;
|
private int m_packetsSentReported;
|
||||||
|
/// <summary>Holds the Environment.TickCount value of when the next OnQueueEmpty can be fired</summary>
|
||||||
|
private int m_nextOnQueueEmpty = 1;
|
||||||
|
|
||||||
/// <summary>Throttle bucket for this agent's connection</summary>
|
/// <summary>Throttle bucket for this agent's connection</summary>
|
||||||
private readonly TokenBucket m_throttle;
|
private readonly TokenBucket m_throttle;
|
||||||
|
@ -140,9 +142,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// <summary>A container that can hold one packet for each outbox, used to store
|
/// <summary>A container that can hold one packet for each outbox, used to store
|
||||||
/// dequeued packets that are being held for throttling</summary>
|
/// dequeued packets that are being held for throttling</summary>
|
||||||
private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
|
private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
|
||||||
/// <summary>Flags to prevent queue empty callbacks from stacking up on
|
|
||||||
/// top of each other</summary>
|
|
||||||
private readonly bool[] m_onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT];
|
|
||||||
/// <summary>A reference to the LLUDPServer that is managing this client</summary>
|
/// <summary>A reference to the LLUDPServer that is managing this client</summary>
|
||||||
private readonly LLUDPServer m_udpServer;
|
private readonly LLUDPServer m_udpServer;
|
||||||
|
|
||||||
|
@ -405,6 +404,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
OpenSim.Framework.LocklessQueue<OutgoingPacket> queue;
|
OpenSim.Framework.LocklessQueue<OutgoingPacket> queue;
|
||||||
TokenBucket bucket;
|
TokenBucket bucket;
|
||||||
bool packetSent = false;
|
bool packetSent = false;
|
||||||
|
ThrottleOutPacketTypeFlags emptyCategories = 0;
|
||||||
|
|
||||||
//string queueDebugOutput = String.Empty; // Serious debug business
|
//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
|
// empty callback now so it has a chance to fill before we
|
||||||
// get back here
|
// get back here
|
||||||
if (queue.Count == 0)
|
if (queue.Count == 0)
|
||||||
BeginFireQueueEmpty(i);
|
emptyCategories |= CategoryToFlag(i);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// No packets in this queue. Fire the queue empty callback
|
// No packets in this queue. Fire the queue empty callback
|
||||||
// if it has not been called recently
|
// 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
|
//m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business
|
||||||
return packetSent;
|
return packetSent;
|
||||||
}
|
}
|
||||||
|
@ -509,49 +512,90 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="throttleIndex">Throttle category to fire the callback
|
/// <param name="throttleIndex">Throttle category to fire the callback
|
||||||
/// for</param>
|
/// for</param>
|
||||||
private void BeginFireQueueEmpty(int throttleIndex)
|
private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories)
|
||||||
{
|
{
|
||||||
// Unknown is -1 and Resend is 0. Make sure we are only firing the
|
if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty)
|
||||||
// callback for categories other than those
|
|
||||||
if (throttleIndex > 0)
|
|
||||||
{
|
{
|
||||||
if (!m_onQueueEmptyRunning[throttleIndex])
|
// Use a value of 0 to signal that FireQueueEmpty is running
|
||||||
{
|
m_nextOnQueueEmpty = 0;
|
||||||
m_onQueueEmptyRunning[throttleIndex] = true;
|
// Asynchronously run the callback
|
||||||
Util.FireAndForget(FireQueueEmpty, throttleIndex);
|
Util.FireAndForget(FireQueueEmpty, categories);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks to see if this queue empty callback is already running,
|
/// Fires the OnQueueEmpty callback and sets the minimum time that it
|
||||||
/// then firing the event
|
/// can be called again
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="o">Throttle category to fire the callback for, stored
|
/// <param name="o">Throttle categories to fire the callback for,
|
||||||
/// as an object to match the WaitCallback delegate signature</param>
|
/// stored as an object to match the WaitCallback delegate
|
||||||
|
/// signature</param>
|
||||||
private void FireQueueEmpty(object o)
|
private void FireQueueEmpty(object o)
|
||||||
{
|
{
|
||||||
const int MIN_CALLBACK_MS = 30;
|
const int MIN_CALLBACK_MS = 30;
|
||||||
|
|
||||||
int i = (int)o;
|
ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o;
|
||||||
ThrottleOutPacketType type = (ThrottleOutPacketType)i;
|
|
||||||
QueueEmpty callback = OnQueueEmpty;
|
QueueEmpty callback = OnQueueEmpty;
|
||||||
|
|
||||||
int start = Environment.TickCount & Int32.MaxValue;
|
int start = Environment.TickCount & Int32.MaxValue;
|
||||||
|
|
||||||
if (callback != null)
|
if (callback != null)
|
||||||
{
|
{
|
||||||
try { callback(type); }
|
try { callback(categories); }
|
||||||
catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); }
|
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,
|
m_nextOnQueueEmpty = start + MIN_CALLBACK_MS;
|
||||||
// otherwise we'll peg a CPU trying to fire these too fast
|
if (m_nextOnQueueEmpty == 0)
|
||||||
int elapsedMS = (Environment.TickCount & Int32.MaxValue) - start;
|
m_nextOnQueueEmpty = 1;
|
||||||
if (elapsedMS < MIN_CALLBACK_MS)
|
}
|
||||||
System.Threading.Thread.Sleep(MIN_CALLBACK_MS - elapsedMS);
|
|
||||||
|
|
||||||
m_onQueueEmptyRunning[i] = false;
|
/// <summary>
|
||||||
|
/// Converts a <seealso cref="ThrottleOutPacketType"/> integer to a
|
||||||
|
/// flag value
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="i">Throttle category to convert</param>
|
||||||
|
/// <returns>Flag representation of the throttle category</returns>
|
||||||
|
private static ThrottleOutPacketTypeFlags CategoryToFlag(int i)
|
||||||
|
{
|
||||||
|
ThrottleOutPacketType category = (ThrottleOutPacketType)i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Land = 1,
|
||||||
|
/// <summary>Wind data</summary>
|
||||||
|
Wind = 2,
|
||||||
|
/// <summary>Cloud data</summary>
|
||||||
|
Cloud = 3,
|
||||||
|
/// <summary>Any packets that do not fit into the other throttles</summary>
|
||||||
|
Task = 4,
|
||||||
|
/// <summary>Texture assets</summary>
|
||||||
|
Texture = 5,
|
||||||
|
/// <summary>Non-texture assets</summary>
|
||||||
|
Asset = 6,
|
||||||
|
/// <summary>Avatar and primitive data</summary>
|
||||||
|
/// <remarks>This is a sub-category of Task</remarks>
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -424,10 +424,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
|
outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
|
||||||
outgoingPacket.Category = ThrottleOutPacketType.Resend;
|
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
|
// Bump up the resend count on this packet
|
||||||
Interlocked.Increment(ref outgoingPacket.ResendCount);
|
Interlocked.Increment(ref outgoingPacket.ResendCount);
|
||||||
//Interlocked.Increment(ref Stats.ResentPackets);
|
//Interlocked.Increment(ref Stats.ResentPackets);
|
||||||
|
|
|
@ -123,6 +123,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
{
|
{
|
||||||
if (expiredPackets == null)
|
if (expiredPackets == null)
|
||||||
expiredPackets = new List<OutgoingPacket>();
|
expiredPackets = new List<OutgoingPacket>();
|
||||||
|
|
||||||
|
// The TickCount will be set to the current time when the packet
|
||||||
|
// is actually sent out again
|
||||||
|
packet.TickCount = 0;
|
||||||
|
|
||||||
expiredPackets.Add(packet);
|
expiredPackets.Add(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue