diff --git a/OpenSim/Framework/PriorityQueue.cs b/OpenSim/Framework/PriorityQueue.cs
index eec2a9268e..3e6fdaa7f9 100644
--- a/OpenSim/Framework/PriorityQueue.cs
+++ b/OpenSim/Framework/PriorityQueue.cs
@@ -42,22 +42,40 @@ namespace OpenSim.Framework
public delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity);
- // Heap[0] for self updates
- // Heap[1..12] for entity updates
-
+ ///
+ /// Total number of queues (priorities) available
+ ///
public const uint NumberOfQueues = 12;
- public const uint ImmediateQueue = 0;
+
+ ///
+ /// Number of queuest (priorities) that are processed immediately
+ /// [] m_heaps = new MinHeap[NumberOfQueues];
private Dictionary m_lookupTable;
+
+ // internal state used to ensure the deqeues are spread across the priority
+ // queues "fairly". queuecounts is the amount to pull from each queue in
+ // each pass. weighted towards the higher priority queues
private uint m_nextQueue = 0;
+ private uint m_countFromQueue = 0;
+ private uint[] m_queueCounts = { 8, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1 };
+
+ // next request is a counter of the number of updates queued, it provides
+ // a total ordering on the updates coming through the queue and is more
+ // lightweight (and more discriminating) than tick count
private UInt64 m_nextRequest = 0;
+ ///
+ /// Lock for enqueue and dequeue operations on the priority queue
+ ///
private object m_syncRoot = new object();
public object SyncRoot {
get { return this.m_syncRoot; }
}
+#region constructor
public PriorityQueue() : this(MinHeap.DEFAULT_CAPACITY) { }
public PriorityQueue(int capacity)
@@ -66,8 +84,16 @@ namespace OpenSim.Framework
for (int i = 0; i < m_heaps.Length; ++i)
m_heaps[i] = new MinHeap(capacity);
- }
+ m_nextQueue = NumberOfImmediateQueues;
+ m_countFromQueue = m_queueCounts[m_nextQueue];
+ }
+#endregion Constructor
+
+#region PublicMethods
+ ///
+ /// Return the number of items in the queues
+ ///
public int Count
{
get
@@ -75,10 +101,14 @@ namespace OpenSim.Framework
int count = 0;
for (int i = 0; i < m_heaps.Length; ++i)
count += m_heaps[i].Count;
+
return count;
}
}
+ ///
+ /// Enqueue an item into the specified priority queue
+ ///
public bool Enqueue(uint pqueue, IEntityUpdate value)
{
LookupItem lookup;
@@ -100,32 +130,62 @@ namespace OpenSim.Framework
return true;
}
+ ///
+ /// Remove an item from one of the queues. Specifically, it removes the
+ /// oldest item from the next queue in order to provide fair access to
+ /// all of the queues
+ ///
public bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue)
{
// If there is anything in priority queue 0, return it first no
// matter what else. Breaks fairness. But very useful.
- if (m_heaps[ImmediateQueue].Count > 0)
+ for (int iq = 0; iq < NumberOfImmediateQueues; iq++)
{
- MinHeapItem item = m_heaps[ImmediateQueue].RemoveMin();
+ if (m_heaps[iq].Count > 0)
+ {
+ MinHeapItem item = m_heaps[iq].RemoveMin();
+ m_lookupTable.Remove(item.Value.Entity.LocalId);
+ timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
+ value = item.Value;
+
+ return true;
+ }
+ }
+
+ // To get the fair queing, we cycle through each of the
+ // queues when finding an element to dequeue.
+ // We pull (NumberOfQueues - QueueIndex) items from each queue in order
+ // to give lower numbered queues a higher priority and higher percentage
+ // of the bandwidth.
+
+ // Check for more items to be pulled from the current queue
+ if (m_heaps[m_nextQueue].Count > 0 && m_countFromQueue > 0)
+ {
+ m_countFromQueue--;
+
+ MinHeapItem item = m_heaps[m_nextQueue].RemoveMin();
m_lookupTable.Remove(item.Value.Entity.LocalId);
timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
value = item.Value;
-
+
return true;
}
-
+
+ // Find the next non-immediate queue with updates in it
for (int i = 0; i < NumberOfQueues; ++i)
{
- // To get the fair queing, we cycle through each of the
- // queues when finding an element to dequeue, this code
- // assumes that the distribution of updates in the queues
- // is polynomial, probably quadractic (eg distance of PI * R^2)
- uint h = (uint)((m_nextQueue + i) % NumberOfQueues);
- if (m_heaps[h].Count > 0)
- {
- m_nextQueue = (uint)((h + 1) % NumberOfQueues);
+ m_nextQueue = (uint)((m_nextQueue + 1) % NumberOfQueues);
+ m_countFromQueue = m_queueCounts[m_nextQueue];
- MinHeapItem item = m_heaps[h].RemoveMin();
+ // if this is one of the immediate queues, just skip it
+ if (m_nextQueue < NumberOfImmediateQueues)
+ continue;
+
+ if (m_heaps[m_nextQueue].Count > 0)
+ {
+ m_countFromQueue--;
+
+ MinHeapItem item = m_heaps[m_nextQueue].RemoveMin();
m_lookupTable.Remove(item.Value.Entity.LocalId);
timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
value = item.Value;
@@ -139,6 +199,10 @@ namespace OpenSim.Framework
return false;
}
+ ///
+ /// Reapply the prioritization function to each of the updates currently
+ /// stored in the priority queues.
+ ///
+ ///
public override string ToString()
{
string s = "";
for (int i = 0; i < NumberOfQueues; i++)
- {
- if (s != "") s += ",";
- s += m_heaps[i].Count.ToString();
- }
+ s += String.Format("{0,7} ",m_heaps[i].Count);
return s;
}
+#endregion PublicMethods
+
#region MinHeapItem
private struct MinHeapItem : IComparable
{
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index cd438d67d1..51ec3a82e8 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -394,6 +394,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public ulong ActiveGroupPowers { get { return m_activeGroupPowers; } }
public bool IsGroupMember(UUID groupID) { return m_groupPowers.ContainsKey(groupID); }
+ ///
+ /// Entity update queues
+ ///
+ public PriorityQueue EntityUpdateQueue { get { return m_entityUpdates; } }
+
///
/// First name of the agent/avatar represented by the client
///
@@ -3625,7 +3630,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Remove the update packet from the list of packets waiting for acknowledgement
// because we are requeuing the list of updates. They will be resent in new packets
// with the most recent state and priority.
- m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber, 0, true);
+ m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber);
+
+ // Count this as a resent packet since we are going to requeue all of the updates contained in it
+ Interlocked.Increment(ref m_udpClient.PacketsResent);
+
foreach (EntityUpdate update in updates)
ResendPrimUpdate(update);
}
@@ -4092,7 +4101,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Remove the update packet from the list of packets waiting for acknowledgement
// because we are requeuing the list of updates. They will be resent in new packets
// with the most recent state.
- m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber, 0, true);
+ m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber);
+
+ // Count this as a resent packet since we are going to requeue all of the updates contained in it
+ Interlocked.Increment(ref m_udpClient.PacketsResent);
+
foreach (ObjectPropertyUpdate update in updates)
ResendPropertyUpdate(update);
}
@@ -11513,7 +11526,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
///
public byte[] GetThrottlesPacked(float multiplier)
{
- return m_udpClient.GetThrottlesPacked();
+ return m_udpClient.GetThrottlesPacked(multiplier);
}
///
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index e54d32629c..80d4e1b62d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -182,9 +182,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_maxRTO = maxRTO;
// Create a token bucket throttle for this client that has the scene token bucket as a parent
- m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.TotalLimit);
+ m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled);
// Create a token bucket throttle for the total categary with the client bucket as a throttle
- m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit);
+ m_throttleCategory = new TokenBucket(m_throttleClient, rates.Total);
// Create an array of token buckets for this clients different throttle categories
m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
@@ -195,7 +195,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Initialize the packet outboxes, where packets sit while they are waiting for tokens
m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue();
// Initialize the token buckets that control the throttling for each category
- m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetLimit(type));
+ m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetRate(type));
}
// Default the retransmission timeout to three seconds
@@ -229,26 +229,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// Information about the client connection
public ClientInfo GetClientInfo()
{
-///
- TokenBucket tb;
-
- tb = m_throttleClient.Parent;
- m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest,"ROOT");
-
- tb = m_throttleClient;
- m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CLIENT");
-
- tb = m_throttleCategory;
- m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CATEGORY");
-
- for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
- {
- tb = m_throttleCategories[i];
- m_log.WarnFormat("[TOKENS] {4} <{0}:{1}>: Actual={2},Requested={3}",AgentID,i,tb.DripRate,tb.RequestedDripRate," BUCKET");
- }
-
-///
-
// TODO: This data structure is wrong in so many ways. Locking and copying the entire lists
// of pending and needed ACKs for every client every time some method wants information about
// this connection is a recipe for poor performance
@@ -260,12 +240,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
- // info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
- info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle +
- info.taskThrottle + info.assetThrottle + info.textureThrottle;
+ info.totalThrottle = (int)m_throttleCategory.DripRate;
return info;
}
@@ -352,8 +330,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
// State is a subcategory of task that we allocate a percentage to
int state = 0;
- // int state = (int)((float)task * STATE_TASK_PERCENTAGE);
- // task -= state;
// Make sure none of the throttles are set below our packet MTU,
// otherwise a throttle could become permanently clogged
@@ -364,19 +340,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
task = Math.Max(task, LLUDPServer.MTU);
texture = Math.Max(texture, LLUDPServer.MTU);
asset = Math.Max(asset, LLUDPServer.MTU);
- state = Math.Max(state, LLUDPServer.MTU);
- int total = resend + land + wind + cloud + task + texture + asset + state;
-
- //m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}",
- // AgentID, resend, land, wind, cloud, task, texture, asset, state, total);
+ //int total = resend + land + wind + cloud + task + texture + asset;
+ //m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, Total={8}",
+ // AgentID, resend, land, wind, cloud, task, texture, asset, total);
// Update the token buckets with new throttle values
TokenBucket bucket;
- bucket = m_throttleCategory;
- bucket.RequestedDripRate = total;
-
bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
bucket.RequestedDripRate = resend;
@@ -405,22 +376,38 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_packedThrottles = null;
}
- public byte[] GetThrottlesPacked()
+ public byte[] GetThrottlesPacked(float multiplier)
{
byte[] data = m_packedThrottles;
if (data == null)
{
+ float rate;
+
data = new byte[7 * 4];
int i = 0;
- Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate), 0, data, i, 4); i += 4;
- Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate), 0, data, i, 4); i += 4;
- Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate), 0, data, i, 4); i += 4;
- Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate), 0, data, i, 4); i += 4;
- Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate), 0, data, i, 4); i += 4;
- Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate), 0, data, i, 4); i += 4;
- Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate), 0, data, i, 4); i += 4;
+ // multiply by 8 to convert bytes back to bits
+ rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate * 8 * multiplier;
+ Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
+
+ rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate * 8 * multiplier;
+ Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
+
+ rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate * 8 * multiplier;
+ Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
+
+ rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate * 8 * multiplier;
+ Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
+
+ rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate * 8 * multiplier;
+ Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
+
+ rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate * 8 * multiplier;
+ Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
+
+ rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate * 8 * multiplier;
+ Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
m_packedThrottles = data;
}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index a1a58e5a68..ab6674db27 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
@@ -672,7 +672,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (packet.Header.AppendedAcks && packet.Header.AckList != null)
{
for (int i = 0; i < packet.Header.AckList.Length; i++)
- udpClient.NeedAcks.Remove(packet.Header.AckList[i], now, packet.Header.Resent);
+ udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent);
}
// Handle PacketAck packets
@@ -681,7 +681,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
PacketAckPacket ackPacket = (PacketAckPacket)packet;
for (int i = 0; i < ackPacket.Packets.Length; i++)
- udpClient.NeedAcks.Remove(ackPacket.Packets[i].ID, now, packet.Header.Resent);
+ udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent);
// We don't need to do anything else with PacketAck packets
return;
diff --git a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
index aaf6e26d58..c9aac0ba09 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
@@ -52,30 +52,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public int Texture;
/// Drip rate for asset packets
public int Asset;
- /// Drip rate for state packets
- public int State;
+
/// Drip rate for the parent token bucket
public int Total;
- /// Maximum burst rate for resent packets
- public int ResendLimit;
- /// Maximum burst rate for land packets
- public int LandLimit;
- /// Maximum burst rate for wind packets
- public int WindLimit;
- /// Maximum burst rate for cloud packets
- public int CloudLimit;
- /// Maximum burst rate for task (state and transaction) packets
- public int TaskLimit;
- /// Maximum burst rate for texture packets
- public int TextureLimit;
- /// Maximum burst rate for asset packets
- public int AssetLimit;
- /// Maximum burst rate for state packets
- public int StateLimit;
- /// Burst rate for the parent token bucket
- public int TotalLimit;
-
+ /// Flag used to enable adaptive throttles
+ public bool AdaptiveThrottlesEnabled;
+
///
/// Default constructor
///
@@ -86,26 +69,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"];
- Resend = throttleConfig.GetInt("resend_default", 12500);
- Land = throttleConfig.GetInt("land_default", 1000);
- Wind = throttleConfig.GetInt("wind_default", 1000);
- Cloud = throttleConfig.GetInt("cloud_default", 1000);
- Task = throttleConfig.GetInt("task_default", 1000);
- Texture = throttleConfig.GetInt("texture_default", 1000);
- Asset = throttleConfig.GetInt("asset_default", 1000);
- State = throttleConfig.GetInt("state_default", 1000);
-
- ResendLimit = throttleConfig.GetInt("resend_limit", 18750);
- LandLimit = throttleConfig.GetInt("land_limit", 29750);
- WindLimit = throttleConfig.GetInt("wind_limit", 18750);
- CloudLimit = throttleConfig.GetInt("cloud_limit", 18750);
- TaskLimit = throttleConfig.GetInt("task_limit", 18750);
- TextureLimit = throttleConfig.GetInt("texture_limit", 55750);
- AssetLimit = throttleConfig.GetInt("asset_limit", 27500);
- StateLimit = throttleConfig.GetInt("state_limit", 37000);
+ Resend = throttleConfig.GetInt("resend_default", 6625);
+ Land = throttleConfig.GetInt("land_default", 9125);
+ Wind = throttleConfig.GetInt("wind_default", 1750);
+ Cloud = throttleConfig.GetInt("cloud_default", 1750);
+ Task = throttleConfig.GetInt("task_default", 18500);
+ Texture = throttleConfig.GetInt("texture_default", 18500);
+ Asset = throttleConfig.GetInt("asset_default", 10500);
Total = throttleConfig.GetInt("client_throttle_max_bps", 0);
- TotalLimit = Total;
+
+ AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false);
}
catch (Exception) { }
}
@@ -128,34 +102,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return Texture;
case ThrottleOutPacketType.Asset:
return Asset;
- case ThrottleOutPacketType.State:
- return State;
- case ThrottleOutPacketType.Unknown:
- default:
- return 0;
- }
- }
-
- public int GetLimit(ThrottleOutPacketType type)
- {
- switch (type)
- {
- case ThrottleOutPacketType.Resend:
- return ResendLimit;
- case ThrottleOutPacketType.Land:
- return LandLimit;
- case ThrottleOutPacketType.Wind:
- return WindLimit;
- case ThrottleOutPacketType.Cloud:
- return CloudLimit;
- case ThrottleOutPacketType.Task:
- return TaskLimit;
- case ThrottleOutPacketType.Texture:
- return TextureLimit;
- case ThrottleOutPacketType.Asset:
- return AssetLimit;
- case ThrottleOutPacketType.State:
- return StateLimit;
case ThrottleOutPacketType.Unknown:
default:
return 0;
diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
index 4ee6d3a30a..2ec79abd16 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
@@ -29,6 +29,8 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
+using OpenSim.Framework;
+
using log4net;
namespace OpenSim.Region.ClientStack.LindenUDP
@@ -177,7 +179,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
RequestedDripRate = dripRate;
// TotalDripRequest = dripRate; // this will be overwritten when a child node registers
// MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst);
- m_lastDrip = Environment.TickCount & Int32.MaxValue;
+ m_lastDrip = Util.EnvironmentTickCount();
}
#endregion Constructor
@@ -211,12 +213,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
///
public void RegisterRequest(TokenBucket child, Int64 request)
{
- m_children[child] = request;
- // m_totalDripRequest = m_children.Values.Sum();
+ lock (m_children)
+ {
+ m_children[child] = request;
+ // m_totalDripRequest = m_children.Values.Sum();
- m_totalDripRequest = 0;
- foreach (KeyValuePair cref in m_children)
- m_totalDripRequest += cref.Value;
+ m_totalDripRequest = 0;
+ foreach (KeyValuePair cref in m_children)
+ m_totalDripRequest += cref.Value;
+ }
// Pass the new values up to the parent
if (m_parent != null)
@@ -229,12 +234,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
///
public void UnregisterRequest(TokenBucket child)
{
- m_children.Remove(child);
- // m_totalDripRequest = m_children.Values.Sum();
+ lock (m_children)
+ {
+ m_children.Remove(child);
+ // m_totalDripRequest = m_children.Values.Sum();
- m_totalDripRequest = 0;
- foreach (KeyValuePair cref in m_children)
- m_totalDripRequest += cref.Value;
+ m_totalDripRequest = 0;
+ foreach (KeyValuePair cref in m_children)
+ m_totalDripRequest += cref.Value;
+ }
+
// Pass the new values up to the parent
if (m_parent != null)
@@ -297,10 +306,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Determine the interval over which we are adding tokens, never add
// more than a single quantum of tokens
- Int32 now = Environment.TickCount & Int32.MaxValue;
- Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum);
-
- m_lastDrip = now;
+ Int32 deltaMS = Math.Min(Util.EnvironmentTickCountSubtract(m_lastDrip), m_ticksPerQuantum);
+ m_lastDrip = Util.EnvironmentTickCount();
// This can be 0 in the very unusual case that the timer wrapped
// It can be 0 if we try add tokens at a sub-tick rate
@@ -315,10 +322,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
- //
- // The minimum rate for flow control.
- //
- protected const Int64 m_minimumFlow = m_minimumDripRate * 10;
+ ///
+ /// The minimum rate for flow control. Minimum drip rate is one
+ /// packet per second. Open the throttle to 15 packets per second
+ /// or about 160kbps.
+ ///
+ protected const Int64 m_minimumFlow = m_minimumDripRate * 15;
//
// The maximum rate for flow control. Drip rate can never be
@@ -331,6 +340,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); }
}
+ private bool m_enabled = false;
+
//
//
//
@@ -348,9 +359,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
//
//
//
- public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate) : base(parent,m_minimumFlow)
+ public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate, bool enabled) : base(parent,maxDripRate)
{
- MaxDripRate = maxDripRate;
+ m_enabled = enabled;
+
+ if (m_enabled)
+ {
+ m_log.WarnFormat("[TOKENBUCKET] Adaptive throttle enabled");
+ MaxDripRate = maxDripRate;
+ AdjustedDripRate = m_minimumFlow;
+ }
}
//
@@ -359,7 +377,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public void ExpirePackets(Int32 count)
{
// m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count);
- AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count));
+ if (m_enabled)
+ AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count));
}
//
@@ -367,7 +386,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
//
public void AcknowledgePackets(Int32 count)
{
- AdjustedDripRate = AdjustedDripRate + count;
+ if (m_enabled)
+ AdjustedDripRate = AdjustedDripRate + count;
}
}
}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
index b1709649f5..793aefe6e5 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
@@ -65,7 +65,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// Holds packets that need to be added to the unacknowledged list
private LocklessQueue m_pendingAdds = new LocklessQueue();
/// Holds information about pending acknowledgements
- private LocklessQueue m_pendingRemoves = new LocklessQueue();
+ private LocklessQueue m_pendingAcknowledgements = new LocklessQueue();
+ /// Holds information about pending removals
+ private LocklessQueue m_pendingRemoves = new LocklessQueue();
///
/// Add an unacked packet to the collection
@@ -83,15 +85,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
///
/// Marks a packet as acknowledged
+ /// This method is used when an acknowledgement is received from the network for a previously
+ /// sent packet. Effects of removal this way are to update unacked byte count, adjust RTT
+ /// and increase throttle to the coresponding client.
///
/// Sequence number of the packet to
/// acknowledge
/// Current value of Environment.TickCount
/// This does not immediately acknowledge the packet, it only
/// queues the ack so it can be handled in a thread-safe way later
- public void Remove(uint sequenceNumber, int currentTime, bool fromResend)
+ public void Acknowledge(uint sequenceNumber, int currentTime, bool fromResend)
{
- m_pendingRemoves.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend));
+ m_pendingAcknowledgements.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend));
+ }
+
+ ///
+ /// Marks a packet as no longer needing acknowledgement without a received acknowledgement.
+ /// This method is called when a packet expires and we no longer need an acknowledgement.
+ /// When some reliable packet types expire, they are handled in a way other than simply
+ /// resending them. The only effect of removal this way is to update unacked byte count.
+ ///
+ /// Sequence number of the packet to
+ /// acknowledge
+ /// The does not immediately remove the packet, it only queues the removal
+ /// so it can be handled in a thread safe way later
+ public void Remove(uint sequenceNumber)
+ {
+ m_pendingRemoves.Enqueue(sequenceNumber);
}
///
@@ -151,15 +171,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_packets[pendingAdd.SequenceNumber] = pendingAdd;
// Process all the pending removes, including updating statistics and round-trip times
- PendingAck pendingRemove;
- OutgoingPacket ackedPacket;
- while (m_pendingRemoves.TryDequeue(out pendingRemove))
+ PendingAck pendingAcknowledgement;
+ while (m_pendingAcknowledgements.TryDequeue(out pendingAcknowledgement))
{
- if (m_packets.TryGetValue(pendingRemove.SequenceNumber, out ackedPacket))
+ OutgoingPacket ackedPacket;
+ if (m_packets.TryGetValue(pendingAcknowledgement.SequenceNumber, out ackedPacket))
{
if (ackedPacket != null)
{
- m_packets.Remove(pendingRemove.SequenceNumber);
+ m_packets.Remove(pendingAcknowledgement.SequenceNumber);
// As with other network applications, assume that an acknowledged packet is an
// indication that the network can handle a little more load, speed up the transmission
@@ -168,16 +188,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Update stats
Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength);
- if (!pendingRemove.FromResend)
+ if (!pendingAcknowledgement.FromResend)
{
// Calculate the round-trip time for this packet and its ACK
- int rtt = pendingRemove.RemoveTime - ackedPacket.TickCount;
+ int rtt = pendingAcknowledgement.RemoveTime - ackedPacket.TickCount;
if (rtt > 0)
ackedPacket.Client.UpdateRoundTrip(rtt);
}
}
}
}
+
+ uint pendingRemove;
+ while(m_pendingRemoves.TryDequeue(out pendingRemove))
+ {
+ OutgoingPacket removedPacket;
+ if (m_packets.TryGetValue(pendingRemove, out removedPacket))
+ {
+ if (removedPacket != null)
+ {
+ m_packets.Remove(pendingRemove);
+
+ // Update stats
+ Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength);
+ }
+ }
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/OpenSim/Region/Framework/Scenes/Prioritizer.cs b/OpenSim/Region/Framework/Scenes/Prioritizer.cs
index 17b2da13dd..ef78f0fdc9 100644
--- a/OpenSim/Region/Framework/Scenes/Prioritizer.cs
+++ b/OpenSim/Region/Framework/Scenes/Prioritizer.cs
@@ -88,7 +88,7 @@ namespace OpenSim.Region.Framework.Scenes
// If this is an update for our own avatar give it the highest priority
if (client.AgentId == entity.UUID)
- return PriorityQueue.ImmediateQueue;
+ return 0;
uint priority;
@@ -119,16 +119,40 @@ namespace OpenSim.Region.Framework.Scenes
private uint GetPriorityByTime(IClientAPI client, ISceneEntity entity)
{
- return 1;
+ // And anything attached to this avatar gets top priority as well
+ if (entity is SceneObjectPart)
+ {
+ SceneObjectPart sop = (SceneObjectPart)entity;
+ if (sop.ParentGroup.RootPart.IsAttachment && client.AgentId == sop.ParentGroup.RootPart.AttachedAvatar)
+ return 1;
+ }
+
+ return PriorityQueue.NumberOfImmediateQueues; // first queue past the immediate queues
}
private uint GetPriorityByDistance(IClientAPI client, ISceneEntity entity)
{
+ // And anything attached to this avatar gets top priority as well
+ if (entity is SceneObjectPart)
+ {
+ SceneObjectPart sop = (SceneObjectPart)entity;
+ if (sop.ParentGroup.RootPart.IsAttachment && client.AgentId == sop.ParentGroup.RootPart.AttachedAvatar)
+ return 1;
+ }
+
return ComputeDistancePriority(client,entity,false);
}
private uint GetPriorityByFrontBack(IClientAPI client, ISceneEntity entity)
{
+ // And anything attached to this avatar gets top priority as well
+ if (entity is SceneObjectPart)
+ {
+ SceneObjectPart sop = (SceneObjectPart)entity;
+ if (sop.ParentGroup.RootPart.IsAttachment && client.AgentId == sop.ParentGroup.RootPart.AttachedAvatar)
+ return 1;
+ }
+
return ComputeDistancePriority(client,entity,true);
}
@@ -205,8 +229,10 @@ namespace OpenSim.Region.Framework.Scenes
// And convert the distance to a priority queue, this computation gives queues
// at 10, 20, 40, 80, 160, 320, 640, and 1280m
- uint pqueue = 1;
- for (int i = 0; i < 8; i++)
+ uint pqueue = PriorityQueue.NumberOfImmediateQueues;
+ uint queues = PriorityQueue.NumberOfQueues - PriorityQueue.NumberOfImmediateQueues;
+
+ for (int i = 0; i < queues - 1; i++)
{
if (distance < 10 * Math.Pow(2.0,i))
break;
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
index 3a5b05de8e..a6bdc593c8 100644
--- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs
+++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
@@ -3049,18 +3049,17 @@ namespace OpenSim.Region.Framework.Scenes
cadu.GroupAccess = 0;
cadu.Position = AbsolutePosition;
cadu.regionHandle = m_rootRegionHandle;
- float multiplier = 1;
- int innacurateNeighbors = m_scene.GetInaccurateNeighborCount();
- if (innacurateNeighbors != 0)
- {
- multiplier = 1f / (float)innacurateNeighbors;
- }
- if (multiplier <= 0f)
- {
- multiplier = 0.25f;
- }
- //m_log.Info("[NeighborThrottle]: " + m_scene.GetInaccurateNeighborCount().ToString() + " - m: " + multiplier.ToString());
+ // Throttles
+ float multiplier = 1;
+ int childRegions = m_knownChildRegions.Count;
+ if (childRegions != 0)
+ multiplier = 1f / childRegions;
+
+ // Minimum throttle for a child region is 1/4 of the root region throttle
+ if (multiplier <= 0.25f)
+ multiplier = 0.25f;
+
cadu.throttles = ControllingClient.GetThrottlesPacked(multiplier);
cadu.Velocity = Velocity;
@@ -3456,16 +3455,14 @@ namespace OpenSim.Region.Framework.Scenes
// Throttles
float multiplier = 1;
- int innacurateNeighbors = m_scene.GetInaccurateNeighborCount();
- if (innacurateNeighbors != 0)
- {
- multiplier = 1f / innacurateNeighbors;
- }
- if (multiplier <= 0f)
- {
+ int childRegions = m_knownChildRegions.Count;
+ if (childRegions != 0)
+ multiplier = 1f / childRegions;
+
+ // Minimum throttle for a child region is 1/4 of the root region throttle
+ if (multiplier <= 0.25f)
multiplier = 0.25f;
- }
- //m_log.Info("[NeighborThrottle]: " + m_scene.GetInaccurateNeighborCount().ToString() + " - m: " + multiplier.ToString());
+
cAgent.Throttles = ControllingClient.GetThrottlesPacked(multiplier);
cAgent.HeadRotation = m_headrotation;
diff --git a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs
index 6a24cc1092..db17d8faf7 100644
--- a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs
+++ b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs
@@ -81,6 +81,14 @@ namespace OpenSim.Region.CoreModules.UDP.Linden
lock (m_scenes)
m_scenes[scene.RegionInfo.RegionID] = scene;
+ scene.AddCommand(
+ this, "show pqueues",
+ "show pqueues [full]",
+ "Show priority queue data for each client",
+ "Without the 'full' option, only root agents are shown."
+ + " With the 'full' option child agents are also shown.",
+ ShowPQueuesReport);
+
scene.AddCommand(
this, "show queues",
"show queues [full]",
@@ -119,6 +127,11 @@ namespace OpenSim.Region.CoreModules.UDP.Linden
// m_log.DebugFormat("[LINDEN UDP INFO MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName);
}
+ protected void ShowPQueuesReport(string module, string[] cmd)
+ {
+ MainConsole.Instance.Output(GetPQueuesReport(cmd));
+ }
+
protected void ShowQueuesReport(string module, string[] cmd)
{
MainConsole.Instance.Output(GetQueuesReport(cmd));
@@ -155,6 +168,80 @@ namespace OpenSim.Region.CoreModules.UDP.Linden
"");
}
+
+ ///
+ /// Generate UDP Queue data report for each client
+ ///
+ ///
+ ///
+ protected string GetPQueuesReport(string[] showParams)
+ {
+ bool showChildren = false;
+ string pname = "";
+
+ if (showParams.Length > 2 && showParams[2] == "full")
+ showChildren = true;
+ else if (showParams.Length > 3)
+ pname = showParams[2] + " " + showParams[3];
+
+ StringBuilder report = new StringBuilder();
+
+ int columnPadding = 2;
+ int maxNameLength = 18;
+ int maxRegionNameLength = 14;
+ int maxTypeLength = 4;
+ int totalInfoFieldsLength = maxNameLength + columnPadding + maxRegionNameLength + columnPadding + maxTypeLength + columnPadding;
+
+ report.Append(GetColumnEntry("User", maxNameLength, columnPadding));
+ report.Append(GetColumnEntry("Region", maxRegionNameLength, columnPadding));
+ report.Append(GetColumnEntry("Type", maxTypeLength, columnPadding));
+
+ report.AppendFormat(
+ "{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7} {10,7} {11,7}\n",
+ "Pri 0",
+ "Pri 1",
+ "Pri 2",
+ "Pri 3",
+ "Pri 4",
+ "Pri 5",
+ "Pri 6",
+ "Pri 7",
+ "Pri 8",
+ "Pri 9",
+ "Pri 10",
+ "Pri 11");
+
+ lock (m_scenes)
+ {
+ foreach (Scene scene in m_scenes.Values)
+ {
+ scene.ForEachClient(
+ delegate(IClientAPI client)
+ {
+ if (client is LLClientView)
+ {
+ bool isChild = scene.PresenceChildStatus(client.AgentId);
+ if (isChild && !showChildren)
+ return;
+
+ string name = client.Name;
+ if (pname != "" && name != pname)
+ return;
+
+ string regionName = scene.RegionInfo.RegionName;
+
+ report.Append(GetColumnEntry(name, maxNameLength, columnPadding));
+ report.Append(GetColumnEntry(regionName, maxRegionNameLength, columnPadding));
+ report.Append(GetColumnEntry(isChild ? "Cd" : "Rt", maxTypeLength, columnPadding));
+ report.AppendLine(((LLClientView)client).EntityUpdateQueue.ToString());
+ }
+ });
+ }
+ }
+
+ return report.ToString();
+ }
+
///
/// Generate UDP Queue data report for each client
///
@@ -163,10 +250,13 @@ namespace OpenSim.Region.CoreModules.UDP.Linden
protected string GetQueuesReport(string[] showParams)
{
bool showChildren = false;
+ string pname = "";
if (showParams.Length > 2 && showParams[2] == "full")
showChildren = true;
-
+ else if (showParams.Length > 3)
+ pname = showParams[2] + " " + showParams[3];
+
StringBuilder report = new StringBuilder();
int columnPadding = 2;
@@ -224,6 +314,9 @@ namespace OpenSim.Region.CoreModules.UDP.Linden
return;
string name = client.Name;
+ if (pname != "" && name != pname)
+ return;
+
string regionName = scene.RegionInfo.RegionName;
report.Append(GetColumnEntry(name, maxNameLength, columnPadding));
@@ -249,10 +342,13 @@ namespace OpenSim.Region.CoreModules.UDP.Linden
protected string GetThrottlesReport(string[] showParams)
{
bool showChildren = false;
+ string pname = "";
if (showParams.Length > 2 && showParams[2] == "full")
showChildren = true;
-
+ else if (showParams.Length > 3)
+ pname = showParams[2] + " " + showParams[3];
+
StringBuilder report = new StringBuilder();
int columnPadding = 2;
@@ -302,7 +398,7 @@ namespace OpenSim.Region.CoreModules.UDP.Linden
if (client is LLClientView)
{
LLClientView llClient = client as LLClientView;
-
+
if (firstClient)
{
report.AppendLine(GetServerThrottlesReport(llClient.UDPServer));
@@ -314,6 +410,9 @@ namespace OpenSim.Region.CoreModules.UDP.Linden
return;
string name = client.Name;
+ if (pname != "" && name != pname)
+ return;
+
string regionName = scene.RegionInfo.RegionName;
LLUDPClient llUdpClient = llClient.UDPClient;
@@ -352,7 +451,7 @@ namespace OpenSim.Region.CoreModules.UDP.Linden
int maxRegionNameLength = 14;
int maxTypeLength = 4;
- string name = "SERVER AGENT LIMITS";
+ string name = "SERVER AGENT RATES";
report.Append(GetColumnEntry(name, maxNameLength, columnPadding));
report.Append(GetColumnEntry("-", maxRegionNameLength, columnPadding));
@@ -362,13 +461,13 @@ namespace OpenSim.Region.CoreModules.UDP.Linden
report.AppendFormat(
"{0,7} {1,8} {2,7} {3,7} {4,7} {5,7} {6,9} {7,7}",
(throttleRates.Total * 8) / 1000,
- (throttleRates.ResendLimit * 8) / 1000,
- (throttleRates.LandLimit * 8) / 1000,
- (throttleRates.WindLimit * 8) / 1000,
- (throttleRates.CloudLimit * 8) / 1000,
- (throttleRates.TaskLimit * 8) / 1000,
- (throttleRates.TextureLimit * 8) / 1000,
- (throttleRates.AssetLimit * 8) / 1000);
+ (throttleRates.Resend * 8) / 1000,
+ (throttleRates.Land * 8) / 1000,
+ (throttleRates.Wind * 8) / 1000,
+ (throttleRates.Cloud * 8) / 1000,
+ (throttleRates.Task * 8) / 1000,
+ (throttleRates.Texture * 8) / 1000,
+ (throttleRates.Asset * 8) / 1000);
return report.ToString();
}
diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini
index 4c0c79470d..43551bdcb1 100644
--- a/bin/OpenSimDefaults.ini
+++ b/bin/OpenSimDefaults.ini
@@ -362,30 +362,24 @@
;
;client_throttle_max_bps = 196608
- ; Per-client bytes per second rates for the various throttle categories.
- ; These are default values that will be overriden by clients
+ ; Adaptive throttling attempts to limit network overload when multiple
+ ; clients login by starting each connection more slowly. Disabled by
+ ; default
;
- ;resend_default = 12500
- ;land_default = 1000
- ;wind_default = 1000
- ;cloud_default = 1000
- ;task_default = 1000
- ;texture_default = 1000
- ;asset_default = 1000
- ;state_default = 1000
+ ;enable_adaptive_throttles = true
- ; Per-client maximum burst rates in bytes per second for the various
- ; throttle categories. These are default values that will be overriden by
- ; clients
- ;
- ;resend_limit = 18750
- ;land_limit = 29750
- ;wind_limit = 18750
- ;cloud_limit = 18750
- ;task_limit = 18750
- ;texture_limit = 55750
- ;asset_limit = 27500
- ;state_limit = 37000
+ ; Per-client bytes per second rates for the various throttle categories.
+ ; These are default values that will be overriden by clients. These
+ ; defaults are approximately equivalent to the throttles set by the Imprudence
+ ; viewer when maximum bandwidth is set to 350kbps
+
+ ;resend_default = 6625
+ ;land_default = 9125
+ ;wind_default = 1750
+ ;cloud_default = 1750
+ ;task_default = 18500
+ ;texture_default = 18500
+ ;asset_default = 10500
; Configures how ObjectUpdates are aggregated. These numbers
; do not literally mean how many updates will be put in each