Merge branch 'queuetest' into careminster-presence-refactor

avinationmerge
Melanie 2011-04-21 16:51:39 +01:00
commit 204b8b7b7e
10 changed files with 340 additions and 106 deletions

View File

@ -576,34 +576,69 @@ namespace OpenSim.Framework
public class IEntityUpdate public class IEntityUpdate
{ {
public ISceneEntity Entity; private ISceneEntity m_entity;
public uint Flags; private uint m_flags;
private int m_updateTime;
public ISceneEntity Entity
{
get { return m_entity; }
}
public uint Flags
{
get { return m_flags; }
}
public int UpdateTime
{
get { return m_updateTime; }
}
public virtual void Update(IEntityUpdate update) public virtual void Update(IEntityUpdate update)
{ {
this.Flags |= update.Flags; m_flags |= update.Flags;
// Use the older of the updates as the updateTime
if (Util.EnvironmentTickCountCompare(UpdateTime, update.UpdateTime) > 0)
m_updateTime = update.UpdateTime;
} }
public IEntityUpdate(ISceneEntity entity, uint flags) public IEntityUpdate(ISceneEntity entity, uint flags)
{ {
Entity = entity; m_entity = entity;
Flags = flags; m_flags = flags;
m_updateTime = Util.EnvironmentTickCount();
}
public IEntityUpdate(ISceneEntity entity, uint flags, Int32 updateTime)
{
m_entity = entity;
m_flags = flags;
m_updateTime = updateTime;
} }
} }
public class EntityUpdate : IEntityUpdate public class EntityUpdate : IEntityUpdate
{ {
// public ISceneEntity Entity; private float m_timeDilation;
// public PrimUpdateFlags Flags;
public float TimeDilation; public float TimeDilation
{
get { return m_timeDilation; }
}
public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation)
: base(entity,(uint)flags) : base(entity, (uint)flags)
{ {
//Entity = entity;
// Flags = flags; // Flags = flags;
TimeDilation = timedilation; m_timeDilation = timedilation;
}
public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation, Int32 updateTime)
: base(entity,(uint)flags,updateTime)
{
m_timeDilation = timedilation;
} }
} }

View File

@ -34,20 +34,21 @@ using OpenSim.Framework;
using OpenSim.Framework.Client; using OpenSim.Framework.Client;
using log4net; using log4net;
namespace OpenSim.Region.ClientStack.LindenUDP namespace OpenSim.Framework
{ {
public class PriorityQueue public class PriorityQueue
{ {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
internal delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity); public delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity);
// Heap[0] for self updates // Heap[0] for self updates
// Heap[1..12] for entity updates // Heap[1..12] for entity updates
internal const uint m_numberOfQueues = 12; public const uint NumberOfQueues = 12;
public const uint ImmediateQueue = 0;
private MinHeap<MinHeapItem>[] m_heaps = new MinHeap<MinHeapItem>[m_numberOfQueues]; private MinHeap<MinHeapItem>[] m_heaps = new MinHeap<MinHeapItem>[NumberOfQueues];
private Dictionary<uint, LookupItem> m_lookupTable; private Dictionary<uint, LookupItem> m_lookupTable;
private uint m_nextQueue = 0; private uint m_nextQueue = 0;
private UInt64 m_nextRequest = 0; private UInt64 m_nextRequest = 0;
@ -57,9 +58,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
get { return this.m_syncRoot; } get { return this.m_syncRoot; }
} }
internal PriorityQueue() : this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY) { } public PriorityQueue() : this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY) { }
internal PriorityQueue(int capacity) public PriorityQueue(int capacity)
{ {
m_lookupTable = new Dictionary<uint, LookupItem>(capacity); m_lookupTable = new Dictionary<uint, LookupItem>(capacity);
@ -67,7 +68,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_heaps[i] = new MinHeap<MinHeapItem>(capacity); m_heaps[i] = new MinHeap<MinHeapItem>(capacity);
} }
internal int Count public int Count
{ {
get get
{ {
@ -91,7 +92,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
lookup.Heap.Remove(lookup.Handle); lookup.Heap.Remove(lookup.Handle);
} }
pqueue = Util.Clamp<uint>(pqueue, 0, m_numberOfQueues - 1); pqueue = Util.Clamp<uint>(pqueue, 0, NumberOfQueues - 1);
lookup.Heap = m_heaps[pqueue]; lookup.Heap = m_heaps[pqueue];
lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle); lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle);
m_lookupTable[localid] = lookup; m_lookupTable[localid] = lookup;
@ -99,18 +100,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return true; return true;
} }
internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue) public bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue)
{ {
for (int i = 0; i < m_numberOfQueues; ++i) // 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)
{
MinHeapItem item = m_heaps[ImmediateQueue].RemoveMin();
m_lookupTable.Remove(item.Value.Entity.LocalId);
timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
value = item.Value;
return true;
}
for (int i = 0; i < NumberOfQueues; ++i)
{ {
// To get the fair queing, we cycle through each of the // To get the fair queing, we cycle through each of the
// queues when finding an element to dequeue, this code // queues when finding an element to dequeue, this code
// assumes that the distribution of updates in the queues // assumes that the distribution of updates in the queues
// is polynomial, probably quadractic (eg distance of PI * R^2) // is polynomial, probably quadractic (eg distance of PI * R^2)
uint h = (uint)((m_nextQueue + i) % m_numberOfQueues); uint h = (uint)((m_nextQueue + i) % NumberOfQueues);
if (m_heaps[h].Count > 0) if (m_heaps[h].Count > 0)
{ {
m_nextQueue = (uint)((h + 1) % m_numberOfQueues); m_nextQueue = (uint)((h + 1) % NumberOfQueues);
MinHeapItem item = m_heaps[h].RemoveMin(); MinHeapItem item = m_heaps[h].RemoveMin();
m_lookupTable.Remove(item.Value.Entity.LocalId); m_lookupTable.Remove(item.Value.Entity.LocalId);
@ -126,7 +139,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
return false; return false;
} }
internal void Reprioritize(UpdatePriorityHandler handler) public void Reprioritize(UpdatePriorityHandler handler)
{ {
MinHeapItem item; MinHeapItem item;
foreach (LookupItem lookup in new List<LookupItem>(this.m_lookupTable.Values)) foreach (LookupItem lookup in new List<LookupItem>(this.m_lookupTable.Values))
@ -140,7 +153,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
// unless the priority queue has changed, there is no need to modify // unless the priority queue has changed, there is no need to modify
// the entry // the entry
pqueue = Util.Clamp<uint>(pqueue, 0, m_numberOfQueues - 1); pqueue = Util.Clamp<uint>(pqueue, 0, NumberOfQueues - 1);
if (pqueue != item.PriorityQueue) if (pqueue != item.PriorityQueue)
{ {
lookup.Heap.Remove(lookup.Handle); lookup.Heap.Remove(lookup.Handle);
@ -164,7 +177,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public override string ToString() public override string ToString()
{ {
string s = ""; string s = "";
for (int i = 0; i < m_numberOfQueues; i++) for (int i = 0; i < NumberOfQueues; i++)
{ {
if (s != "") s += ","; if (s != "") s += ",";
s += m_heaps[i].Count.ToString(); s += m_heaps[i].Count.ToString();

View File

@ -1549,6 +1549,23 @@ namespace OpenSim.Framework
return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1); return (diff >= 0) ? diff : (diff + EnvironmentTickCountMask + 1);
} }
// Returns value of Tick Count A - TickCount B accounting for wrapping of TickCount
// Assumes both tcA and tcB came from previous calls to Util.EnvironmentTickCount().
// A positive return value indicates A occured later than B
public static Int32 EnvironmentTickCountCompare(Int32 tcA, Int32 tcB)
{
// A, B and TC are all between 0 and 0x3fffffff
int tc = EnvironmentTickCount();
if (tc - tcA >= 0)
tcA += EnvironmentTickCountMask + 1;
if (tc - tcB >= 0)
tcB += EnvironmentTickCountMask + 1;
return tcA - tcB;
}
/// <summary> /// <summary>
/// Prints the call stack at any given point. Useful for debugging. /// Prints the call stack at any given point. Useful for debugging.
/// </summary> /// </summary>

View File

@ -3596,6 +3596,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
} }
/// <summary>
/// Requeue an EntityUpdate when it was not acknowledged by the client.
/// We will update the priority and put it in the correct queue, merging update flags
/// with any other updates that may be queued for the same entity.
/// The original update time is used for the merged update.
/// </summary>
private void ResendPrimUpdate(EntityUpdate update)
{
// If the update exists in priority queue, it will be updated.
// If it does not exist then it will be added with the current (rather than its original) priority
uint priority = m_prioritizer.GetUpdatePriority(this, update.Entity);
lock (m_entityUpdates.SyncRoot)
m_entityUpdates.Enqueue(priority, update);
}
/// <summary>
/// Requeue a list of EntityUpdates when they were not acknowledged by the client.
/// We will update the priority and put it in the correct queue, merging update flags
/// with any other updates that may be queued for the same entity.
/// The original update time is used for the merged update.
/// </summary>
private void ResendPrimUpdates(List<EntityUpdate> updates, OutgoingPacket oPacket)
{
// m_log.WarnFormat("[CLIENT] resending prim update {0}",updates[0].UpdateTime);
// 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);
foreach (EntityUpdate update in updates)
ResendPrimUpdate(update);
}
private void ProcessEntityUpdates(int maxUpdates) private void ProcessEntityUpdates(int maxUpdates)
{ {
OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>();
@ -3603,6 +3637,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
OpenSim.Framework.Lazy<List<EntityUpdate>> objectUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
OpenSim.Framework.Lazy<List<EntityUpdate>> compressedUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
OpenSim.Framework.Lazy<List<EntityUpdate>> terseUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
OpenSim.Framework.Lazy<List<EntityUpdate>> terseAgentUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
// Check to see if this is a flush // Check to see if this is a flush
if (maxUpdates <= 0) if (maxUpdates <= 0)
{ {
@ -4027,7 +4066,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
SendFamilyProps = SendFamilyProps || update.SendFamilyProps; SendFamilyProps = SendFamilyProps || update.SendFamilyProps;
SendObjectProps = SendObjectProps || update.SendObjectProps; SendObjectProps = SendObjectProps || update.SendObjectProps;
Flags |= update.Flags; // other properties may need to be updated by base class
base.Update(update);
} }
} }
@ -4038,6 +4078,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false)); m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false));
} }
private void ResendPropertyUpdate(ObjectPropertyUpdate update)
{
uint priority = 0;
lock (m_entityProps.SyncRoot)
m_entityProps.Enqueue(priority, update);
}
private void ResendPropertyUpdates(List<ObjectPropertyUpdate> updates, OutgoingPacket oPacket)
{
// m_log.WarnFormat("[CLIENT] resending object property {0}",updates[0].UpdateTime);
// 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);
foreach (ObjectPropertyUpdate update in updates)
ResendPropertyUpdate(update);
}
public void SendObjectPropertiesReply(ISceneEntity entity) public void SendObjectPropertiesReply(ISceneEntity entity)
{ {
uint priority = 0; // time based ordering only uint priority = 0; // time based ordering only
@ -4053,6 +4112,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>> objectPropertiesBlocks = OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>> objectPropertiesBlocks =
new OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>>(); new OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>>();
OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>> familyUpdates =
new OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>>();
OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>> propertyUpdates =
new OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>>();
IEntityUpdate iupdate; IEntityUpdate iupdate;
Int32 timeinqueue; // this is just debugging code & can be dropped later Int32 timeinqueue; // this is just debugging code & can be dropped later
@ -4071,6 +4136,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
SceneObjectPart sop = (SceneObjectPart)update.Entity; SceneObjectPart sop = (SceneObjectPart)update.Entity;
ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags);
objectFamilyBlocks.Value.Add(objPropDB); objectFamilyBlocks.Value.Add(objPropDB);
familyUpdates.Value.Add(update);
} }
} }
@ -4081,6 +4147,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
SceneObjectPart sop = (SceneObjectPart)update.Entity; SceneObjectPart sop = (SceneObjectPart)update.Entity;
ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop);
objectPropertiesBlocks.Value.Add(objPropDB); objectPropertiesBlocks.Value.Add(objPropDB);
propertyUpdates.Value.Add(update);
} }
} }
@ -4088,12 +4155,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
Int32 ppcnt = 0; // Int32 ppcnt = 0;
Int32 pbcnt = 0; // Int32 pbcnt = 0;
if (objectPropertiesBlocks.IsValueCreated) if (objectPropertiesBlocks.IsValueCreated)
{ {
List<ObjectPropertiesPacket.ObjectDataBlock> blocks = objectPropertiesBlocks.Value; List<ObjectPropertiesPacket.ObjectDataBlock> blocks = objectPropertiesBlocks.Value;
List<ObjectPropertyUpdate> updates = propertyUpdates.Value;
ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count]; packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count];
@ -4101,28 +4169,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
packet.ObjectData[i] = blocks[i]; packet.ObjectData[i] = blocks[i];
packet.Header.Zerocoded = true; packet.Header.Zerocoded = true;
OutPacket(packet, ThrottleOutPacketType.Task, true);
pbcnt += blocks.Count; // Pass in the delegate so that if this packet needs to be resent, we send the current properties
ppcnt++; // of the object rather than the properties when the packet was created
OutPacket(packet, ThrottleOutPacketType.Task, true,
delegate(OutgoingPacket oPacket)
{
ResendPropertyUpdates(updates, oPacket);
});
// pbcnt += blocks.Count;
// ppcnt++;
} }
Int32 fpcnt = 0; // Int32 fpcnt = 0;
Int32 fbcnt = 0; // Int32 fbcnt = 0;
if (objectFamilyBlocks.IsValueCreated) if (objectFamilyBlocks.IsValueCreated)
{ {
List<ObjectPropertiesFamilyPacket.ObjectDataBlock> blocks = objectFamilyBlocks.Value; List<ObjectPropertiesFamilyPacket.ObjectDataBlock> blocks = objectFamilyBlocks.Value;
// ObjectPropertiesFamilyPacket objPropFamilyPack =
// (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily);
//
// objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count];
// for (int i = 0; i < blocks.Count; i++)
// objPropFamilyPack.ObjectData[i] = blocks[i];
//
// OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true);
// one packet per object block... uggh... // one packet per object block... uggh...
for (int i = 0; i < blocks.Count; i++) for (int i = 0; i < blocks.Count; i++)
{ {
@ -4131,10 +4197,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
packet.ObjectData = blocks[i]; packet.ObjectData = blocks[i];
packet.Header.Zerocoded = true; packet.Header.Zerocoded = true;
OutPacket(packet, ThrottleOutPacketType.Task);
fpcnt++; // Pass in the delegate so that if this packet needs to be resent, we send the current properties
fbcnt++; // of the object rather than the properties when the packet was created
List<ObjectPropertyUpdate> updates = new List<ObjectPropertyUpdate>();
updates.Add(familyUpdates.Value[i]);
OutPacket(packet, ThrottleOutPacketType.Task, true,
delegate(OutgoingPacket oPacket)
{
ResendPropertyUpdates(updates, oPacket);
});
// fpcnt++;
// fbcnt++;
} }
} }
@ -11472,6 +11547,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// packets (the default), or false to disable splitting if the calling code /// packets (the default), or false to disable splitting if the calling code
/// handles splitting manually</param> /// handles splitting manually</param>
protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting) protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting)
{
OutPacket(packet, throttlePacketType, doAutomaticSplitting, null);
}
/// <summary>
/// This is the starting point for sending a simulator packet out to the client
/// </summary>
/// <param name="packet">Packet to send</param>
/// <param name="throttlePacketType">Throttling category for the packet</param>
/// <param name="doAutomaticSplitting">True to automatically split oversized
/// packets (the default), or false to disable splitting if the calling code
/// handles splitting manually</param>
/// <param name="method">The method to be called in the event this packet is reliable
/// and unacknowledged. The server will provide normal resend capability if you do not
/// provide your own method.</param>
protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting, UnackedPacketMethod method)
{ {
if (m_debugPacketLevel > 0) if (m_debugPacketLevel > 0)
{ {
@ -11498,7 +11589,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type);
} }
m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting, method);
} }
public bool AddMoney(int debit) public bool AddMoney(int debit)

View File

@ -135,7 +135,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private int m_nextOnQueueEmpty = 1; 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_throttleClient; private readonly AdaptiveTokenBucket m_throttleClient;
public AdaptiveTokenBucket FlowThrottle
{
get { return m_throttleClient; }
}
/// <summary>Throttle bucket for this agent's connection</summary> /// <summary>Throttle bucket for this agent's connection</summary>
private readonly TokenBucket m_throttleCategory; private readonly TokenBucket m_throttleCategory;
/// <summary>Throttle buckets for each packet category</summary> /// <summary>Throttle buckets for each packet category</summary>
@ -177,7 +182,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_maxRTO = maxRTO; m_maxRTO = maxRTO;
// Create a token bucket throttle for this client that has the scene token bucket as a parent // Create a token bucket throttle for this client that has the scene token bucket as a parent
m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit); m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.TotalLimit);
// Create a token bucket throttle for the total categary with the client bucket as a throttle // 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.TotalLimit);
// Create an array of token buckets for this clients different throttle categories // Create an array of token buckets for this clients different throttle categories

View File

@ -297,7 +297,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
delegate(IClientAPI client) delegate(IClientAPI client)
{ {
if (client is LLClientView) if (client is LLClientView)
SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null);
} }
); );
} }
@ -309,7 +309,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
delegate(IClientAPI client) delegate(IClientAPI client)
{ {
if (client is LLClientView) if (client is LLClientView)
SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null);
} }
); );
} }
@ -322,7 +322,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="packet"></param> /// <param name="packet"></param>
/// <param name="category"></param> /// <param name="category"></param>
/// <param name="allowSplitting"></param> /// <param name="allowSplitting"></param>
public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting) public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting, UnackedPacketMethod method)
{ {
// CoarseLocationUpdate packets cannot be split in an automated way // CoarseLocationUpdate packets cannot be split in an automated way
if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
@ -339,13 +339,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
for (int i = 0; i < packetCount; i++) for (int i = 0; i < packetCount; i++)
{ {
byte[] data = datas[i]; byte[] data = datas[i];
SendPacketData(udpClient, data, packet.Type, category); SendPacketData(udpClient, data, packet.Type, category, method);
} }
} }
else else
{ {
byte[] data = packet.ToBytes(); byte[] data = packet.ToBytes();
SendPacketData(udpClient, data, packet.Type, category); SendPacketData(udpClient, data, packet.Type, category, method);
} }
} }
@ -356,7 +356,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="data"></param> /// <param name="data"></param>
/// <param name="type"></param> /// <param name="type"></param>
/// <param name="category"></param> /// <param name="category"></param>
public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category) public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category, UnackedPacketMethod method)
{ {
int dataLength = data.Length; int dataLength = data.Length;
bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0;
@ -411,7 +411,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
#region Queue or Send #region Queue or Send
OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, null);
// If we were not provided a method for handling unacked, use the UDPServer default method
outgoingPacket.UnackedMethod = ((method == null) ? delegate(OutgoingPacket oPacket) { ResendUnacked(oPacket); } : method);
// If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will // If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will
// continue to display the deleted object until relog. Therefore, we need to always queue a kill object // continue to display the deleted object until relog. Therefore, we need to always queue a kill object
@ -445,7 +447,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
packet.Header.Reliable = false; packet.Header.Reliable = false;
packet.Packets = blocks.ToArray(); packet.Packets = blocks.ToArray();
SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true); SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true, null);
} }
} }
@ -458,17 +460,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// We *could* get OldestUnacked, but it would hurt performance and not provide any benefit // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit
pc.PingID.OldestUnacked = 0; pc.PingID.OldestUnacked = 0;
SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false); SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false, null);
} }
public void CompletePing(LLUDPClient udpClient, byte pingID) public void CompletePing(LLUDPClient udpClient, byte pingID)
{ {
CompletePingCheckPacket completePing = new CompletePingCheckPacket(); CompletePingCheckPacket completePing = new CompletePingCheckPacket();
completePing.PingID.PingID = pingID; completePing.PingID.PingID = pingID;
SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false); SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false, null);
} }
public void ResendUnacked(LLUDPClient udpClient) public void HandleUnacked(LLUDPClient udpClient)
{ {
if (!udpClient.IsConnected) if (!udpClient.IsConnected)
return; return;
@ -488,33 +490,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (expiredPackets != null) if (expiredPackets != null)
{ {
//m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO); //m_log.Debug("[LLUDPSERVER]: Handling " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO);
// Exponential backoff of the retransmission timeout // Exponential backoff of the retransmission timeout
udpClient.BackoffRTO(); udpClient.BackoffRTO();
for (int i = 0; i < expiredPackets.Count; ++i)
// Resend packets expiredPackets[i].UnackedMethod(expiredPackets[i]);
for (int i = 0; i < expiredPackets.Count; i++)
{
OutgoingPacket outgoingPacket = expiredPackets[i];
//m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
// outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
// Set the resent flag
outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
outgoingPacket.Category = ThrottleOutPacketType.Resend;
// Bump up the resend count on this packet
Interlocked.Increment(ref outgoingPacket.ResendCount);
// Requeue or resend the packet
if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, false))
SendPacketFinal(outgoingPacket);
}
} }
} }
public void ResendUnacked(OutgoingPacket outgoingPacket)
{
//m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
// outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
// Set the resent flag
outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
outgoingPacket.Category = ThrottleOutPacketType.Resend;
// Bump up the resend count on this packet
Interlocked.Increment(ref outgoingPacket.ResendCount);
// Requeue or resend the packet
if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, false))
SendPacketFinal(outgoingPacket);
}
public void Flush(LLUDPClient udpClient) public void Flush(LLUDPClient udpClient)
{ {
// FIXME: Implement? // FIXME: Implement?
@ -1098,7 +1098,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (udpClient.IsConnected) if (udpClient.IsConnected)
{ {
if (m_resendUnacked) if (m_resendUnacked)
ResendUnacked(udpClient); HandleUnacked(udpClient);
if (m_sendAcks) if (m_sendAcks)
SendAcks(udpClient); SendAcks(udpClient);
@ -1154,7 +1154,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
nticksUnack++; nticksUnack++;
watch2.Start(); watch2.Start();
ResendUnacked(udpClient); HandleUnacked(udpClient);
watch2.Stop(); watch2.Stop();
avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack); avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack);

View File

@ -31,6 +31,8 @@ using OpenMetaverse;
namespace OpenSim.Region.ClientStack.LindenUDP namespace OpenSim.Region.ClientStack.LindenUDP
{ {
public delegate void UnackedPacketMethod(OutgoingPacket oPacket);
/// <summary> /// <summary>
/// Holds a reference to the <seealso cref="LLUDPClient"/> this packet is /// Holds a reference to the <seealso cref="LLUDPClient"/> this packet is
/// destined for, along with the serialized packet data, sequence number /// destined for, along with the serialized packet data, sequence number
@ -52,6 +54,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public int TickCount; public int TickCount;
/// <summary>Category this packet belongs to</summary> /// <summary>Category this packet belongs to</summary>
public ThrottleOutPacketType Category; public ThrottleOutPacketType Category;
/// <summary>The delegate to be called if this packet is determined to be unacknowledged</summary>
public UnackedPacketMethod UnackedMethod;
/// <summary> /// <summary>
/// Default constructor /// Default constructor
@ -60,11 +64,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <param name="buffer">Serialized packet data. If the flags or sequence number /// <param name="buffer">Serialized packet data. If the flags or sequence number
/// need to be updated, they will be injected directly into this binary buffer</param> /// need to be updated, they will be injected directly into this binary buffer</param>
/// <param name="category">Throttling category for this packet</param> /// <param name="category">Throttling category for this packet</param>
public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category) public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category, UnackedPacketMethod method)
{ {
Client = client; Client = client;
Buffer = buffer; Buffer = buffer;
Category = category; Category = category;
UnackedMethod = method;
} }
} }
} }

View File

@ -48,31 +48,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// Number of ticks (ms) per quantum, drip rate and max burst /// Number of ticks (ms) per quantum, drip rate and max burst
/// are defined over this interval. /// are defined over this interval.
/// </summary> /// </summary>
private const Int32 m_ticksPerQuantum = 1000; protected const Int32 m_ticksPerQuantum = 1000;
/// <summary> /// <summary>
/// This is the number of quantums worth of packets that can /// This is the number of quantums worth of packets that can
/// be accommodated during a burst /// be accommodated during a burst
/// </summary> /// </summary>
private const Double m_quantumsPerBurst = 1.5; protected const Double m_quantumsPerBurst = 1.5;
/// <summary> /// <summary>
/// </summary> /// </summary>
private const Int32 m_minimumDripRate = 1400; protected const Int32 m_minimumDripRate = 1400;
/// <summary>Time of the last drip, in system ticks</summary> /// <summary>Time of the last drip, in system ticks</summary>
private Int32 m_lastDrip; protected Int32 m_lastDrip;
/// <summary> /// <summary>
/// The number of bytes that can be sent at this moment. This is the /// The number of bytes that can be sent at this moment. This is the
/// current number of tokens in the bucket /// current number of tokens in the bucket
/// </summary> /// </summary>
private Int64 m_tokenCount; protected Int64 m_tokenCount;
/// <summary> /// <summary>
/// Map of children buckets and their requested maximum burst rate /// Map of children buckets and their requested maximum burst rate
/// </summary> /// </summary>
private Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>(); protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>();
#region Properties #region Properties
@ -81,7 +81,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// parent. The parent bucket will limit the aggregate bandwidth of all /// parent. The parent bucket will limit the aggregate bandwidth of all
/// of its children buckets /// of its children buckets
/// </summary> /// </summary>
private TokenBucket m_parent; protected TokenBucket m_parent;
public TokenBucket Parent public TokenBucket Parent
{ {
get { return m_parent; } get { return m_parent; }
@ -93,7 +93,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// of tokens that can accumulate in the bucket at any one time. This /// of tokens that can accumulate in the bucket at any one time. This
/// also sets the total request for leaf nodes /// also sets the total request for leaf nodes
/// </summary> /// </summary>
private Int64 m_burstRate; protected Int64 m_burstRate;
public Int64 RequestedBurstRate public Int64 RequestedBurstRate
{ {
get { return m_burstRate; } get { return m_burstRate; }
@ -118,8 +118,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <remarks>Tokens are added to the bucket any time /// <remarks>Tokens are added to the bucket any time
/// <seealso cref="RemoveTokens"/> is called, at the granularity of /// <seealso cref="RemoveTokens"/> is called, at the granularity of
/// the system tick interval (typically around 15-22ms)</remarks> /// the system tick interval (typically around 15-22ms)</remarks>
private Int64 m_dripRate; protected Int64 m_dripRate;
public Int64 RequestedDripRate public virtual Int64 RequestedDripRate
{ {
get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); }
set { set {
@ -131,7 +131,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
} }
public Int64 DripRate public virtual Int64 DripRate
{ {
get { get {
if (m_parent == null) if (m_parent == null)
@ -149,7 +149,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// The current total of the requested maximum burst rates of /// The current total of the requested maximum burst rates of
/// this bucket's children buckets. /// this bucket's children buckets.
/// </summary> /// </summary>
private Int64 m_totalDripRequest; protected Int64 m_totalDripRequest;
public Int64 TotalDripRequest public Int64 TotalDripRequest
{ {
get { return m_totalDripRequest; } get { return m_totalDripRequest; }
@ -189,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// hierarchy. However, if any of the parents is over-booked, then /// hierarchy. However, if any of the parents is over-booked, then
/// the modifier will be less than 1. /// the modifier will be less than 1.
/// </summary> /// </summary>
private double DripRateModifier() protected double DripRateModifier()
{ {
Int64 driprate = DripRate; Int64 driprate = DripRate;
return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
@ -197,7 +197,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// <summary> /// <summary>
/// </summary> /// </summary>
private double BurstRateModifier() protected double BurstRateModifier()
{ {
// for now... burst rate is always m_quantumsPerBurst (constant) // for now... burst rate is always m_quantumsPerBurst (constant)
// larger than drip rate so the ratio of burst requests is the // larger than drip rate so the ratio of burst requests is the
@ -268,7 +268,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// Deposit tokens into the bucket from a child bucket that did /// Deposit tokens into the bucket from a child bucket that did
/// not use all of its available tokens /// not use all of its available tokens
/// </summary> /// </summary>
private void Deposit(Int64 count) protected void Deposit(Int64 count)
{ {
m_tokenCount += count; m_tokenCount += count;
@ -285,7 +285,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// call to Drip /// call to Drip
/// </summary> /// </summary>
/// <returns>True if tokens were added to the bucket, otherwise false</returns> /// <returns>True if tokens were added to the bucket, otherwise false</returns>
private void Drip() protected void Drip()
{ {
// This should never happen... means we are a leaf node and were created // This should never happen... means we are a leaf node and were created
// with no drip rate... // with no drip rate...
@ -310,4 +310,64 @@ namespace OpenSim.Region.ClientStack.LindenUDP
Deposit(deltaMS * DripRate / m_ticksPerQuantum); Deposit(deltaMS * DripRate / m_ticksPerQuantum);
} }
} }
public class AdaptiveTokenBucket : TokenBucket
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
// <summary>
// The minimum rate for flow control.
// </summary>
protected const Int64 m_minimumFlow = m_minimumDripRate * 10;
// <summary>
// The maximum rate for flow control. Drip rate can never be
// greater than this.
// </summary>
protected Int64 m_maxDripRate = 0;
protected Int64 MaxDripRate
{
get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); }
set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); }
}
// <summary>
//
// </summary>
public virtual Int64 AdjustedDripRate
{
get { return m_dripRate; }
set {
m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value,m_minimumFlow,MaxDripRate);
m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
if (m_parent != null)
m_parent.RegisterRequest(this,m_dripRate);
}
}
// <summary>
//
// </summary>
public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate) : base(parent,m_minimumFlow)
{
MaxDripRate = maxDripRate;
}
// <summary>
//
// </summary>
public void ExpirePackets(Int32 count)
{
// m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count);
AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count));
}
// <summary>
//
// </summary>
public void AcknowledgePackets(Int32 count)
{
AdjustedDripRate = AdjustedDripRate + count;
}
}
} }

View File

@ -130,6 +130,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// is actually sent out again // is actually sent out again
packet.TickCount = 0; packet.TickCount = 0;
// As with other network applications, assume that an expired packet is
// an indication of some network problem, slow transmission
packet.Client.FlowThrottle.ExpirePackets(1);
expiredPackets.Add(packet); expiredPackets.Add(packet);
} }
} }
@ -157,6 +161,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
m_packets.Remove(pendingRemove.SequenceNumber); m_packets.Remove(pendingRemove.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
ackedPacket.Client.FlowThrottle.AcknowledgePackets(ackedPacket.Buffer.DataLength);
// Update stats // Update stats
Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength);

View File

@ -88,7 +88,7 @@ namespace OpenSim.Region.Framework.Scenes
// If this is an update for our own avatar give it the highest priority // If this is an update for our own avatar give it the highest priority
if (client.AgentId == entity.UUID) if (client.AgentId == entity.UUID)
return 0; return PriorityQueue.ImmediateQueue;
uint priority; uint priority;
@ -180,7 +180,7 @@ namespace OpenSim.Region.Framework.Scenes
// m_log.WarnFormat("[PRIORITIZER] attempt to use agent {0} not in the scene",client.AgentId); // m_log.WarnFormat("[PRIORITIZER] attempt to use agent {0} not in the scene",client.AgentId);
// throw new InvalidOperationException("Prioritization agent not defined"); // throw new InvalidOperationException("Prioritization agent not defined");
return Int32.MaxValue; return PriorityQueue.NumberOfQueues - 1;
} }
// Use group position for child prims, since we are putting child prims in // Use group position for child prims, since we are putting child prims in