Merge branch 'queuetest' into careminster-presence-refactor
commit
204b8b7b7e
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
|
@ -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>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue