aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Framework/PriorityQueue.cs (renamed from OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs)132
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs194
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs82
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs68
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs7
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs80
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs142
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs66
8 files changed, 512 insertions, 259 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs b/OpenSim/Framework/PriorityQueue.cs
index b62ec07..3e6fdaa 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/PriorityQueue.cs
+++ b/OpenSim/Framework/PriorityQueue.cs
@@ -34,50 +34,81 @@ using OpenSim.Framework;
34using OpenSim.Framework.Client; 34using OpenSim.Framework.Client;
35using log4net; 35using log4net;
36 36
37namespace OpenSim.Region.ClientStack.LindenUDP 37namespace OpenSim.Framework
38{ 38{
39 public class PriorityQueue 39 public class PriorityQueue
40 { 40 {
41 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 41 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
42 42
43 internal delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity); 43 public delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity);
44 44
45 // Heap[0] for self updates 45 /// <summary>
46 // Heap[1..12] for entity updates 46 /// Total number of queues (priorities) available
47 /// </summary>
48 public const uint NumberOfQueues = 12;
47 49
48 internal const uint m_numberOfQueues = 12; 50 /// <summary>
51 /// Number of queuest (priorities) that are processed immediately
52 /// </summary.
53 public const uint NumberOfImmediateQueues = 2;
49 54
50 private MinHeap<MinHeapItem>[] m_heaps = new MinHeap<MinHeapItem>[m_numberOfQueues]; 55 private MinHeap<MinHeapItem>[] m_heaps = new MinHeap<MinHeapItem>[NumberOfQueues];
51 private Dictionary<uint, LookupItem> m_lookupTable; 56 private Dictionary<uint, LookupItem> m_lookupTable;
57
58 // internal state used to ensure the deqeues are spread across the priority
59 // queues "fairly". queuecounts is the amount to pull from each queue in
60 // each pass. weighted towards the higher priority queues
52 private uint m_nextQueue = 0; 61 private uint m_nextQueue = 0;
62 private uint m_countFromQueue = 0;
63 private uint[] m_queueCounts = { 8, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1 };
64
65 // next request is a counter of the number of updates queued, it provides
66 // a total ordering on the updates coming through the queue and is more
67 // lightweight (and more discriminating) than tick count
53 private UInt64 m_nextRequest = 0; 68 private UInt64 m_nextRequest = 0;
54 69
70 /// <summary>
71 /// Lock for enqueue and dequeue operations on the priority queue
72 /// </summary>
55 private object m_syncRoot = new object(); 73 private object m_syncRoot = new object();
56 public object SyncRoot { 74 public object SyncRoot {
57 get { return this.m_syncRoot; } 75 get { return this.m_syncRoot; }
58 } 76 }
59 77
60 internal PriorityQueue() : this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY) { } 78#region constructor
79 public PriorityQueue() : this(MinHeap<MinHeapItem>.DEFAULT_CAPACITY) { }
61 80
62 internal PriorityQueue(int capacity) 81 public PriorityQueue(int capacity)
63 { 82 {
64 m_lookupTable = new Dictionary<uint, LookupItem>(capacity); 83 m_lookupTable = new Dictionary<uint, LookupItem>(capacity);
65 84
66 for (int i = 0; i < m_heaps.Length; ++i) 85 for (int i = 0; i < m_heaps.Length; ++i)
67 m_heaps[i] = new MinHeap<MinHeapItem>(capacity); 86 m_heaps[i] = new MinHeap<MinHeapItem>(capacity);
87
88 m_nextQueue = NumberOfImmediateQueues;
89 m_countFromQueue = m_queueCounts[m_nextQueue];
68 } 90 }
91#endregion Constructor
69 92
70 internal int Count 93#region PublicMethods
94 /// <summary>
95 /// Return the number of items in the queues
96 /// </summary>
97 public int Count
71 { 98 {
72 get 99 get
73 { 100 {
74 int count = 0; 101 int count = 0;
75 for (int i = 0; i < m_heaps.Length; ++i) 102 for (int i = 0; i < m_heaps.Length; ++i)
76 count += m_heaps[i].Count; 103 count += m_heaps[i].Count;
104
77 return count; 105 return count;
78 } 106 }
79 } 107 }
80 108
109 /// <summary>
110 /// Enqueue an item into the specified priority queue
111 /// </summary>
81 public bool Enqueue(uint pqueue, IEntityUpdate value) 112 public bool Enqueue(uint pqueue, IEntityUpdate value)
82 { 113 {
83 LookupItem lookup; 114 LookupItem lookup;
@@ -91,7 +122,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
91 lookup.Heap.Remove(lookup.Handle); 122 lookup.Heap.Remove(lookup.Handle);
92 } 123 }
93 124
94 pqueue = Util.Clamp<uint>(pqueue, 0, m_numberOfQueues - 1); 125 pqueue = Util.Clamp<uint>(pqueue, 0, NumberOfQueues - 1);
95 lookup.Heap = m_heaps[pqueue]; 126 lookup.Heap = m_heaps[pqueue];
96 lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle); 127 lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle);
97 m_lookupTable[localid] = lookup; 128 m_lookupTable[localid] = lookup;
@@ -99,20 +130,62 @@ namespace OpenSim.Region.ClientStack.LindenUDP
99 return true; 130 return true;
100 } 131 }
101 132
102 internal bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue) 133 /// <summary>
134 /// Remove an item from one of the queues. Specifically, it removes the
135 /// oldest item from the next queue in order to provide fair access to
136 /// all of the queues
137 /// </summary>
138 public bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue)
103 { 139 {
104 for (int i = 0; i < m_numberOfQueues; ++i) 140 // If there is anything in priority queue 0, return it first no
141 // matter what else. Breaks fairness. But very useful.
142 for (int iq = 0; iq < NumberOfImmediateQueues; iq++)
105 { 143 {
106 // To get the fair queing, we cycle through each of the 144 if (m_heaps[iq].Count > 0)
107 // queues when finding an element to dequeue, this code
108 // assumes that the distribution of updates in the queues
109 // is polynomial, probably quadractic (eg distance of PI * R^2)
110 uint h = (uint)((m_nextQueue + i) % m_numberOfQueues);
111 if (m_heaps[h].Count > 0)
112 { 145 {
113 m_nextQueue = (uint)((h + 1) % m_numberOfQueues); 146 MinHeapItem item = m_heaps[iq].RemoveMin();
147 m_lookupTable.Remove(item.Value.Entity.LocalId);
148 timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
149 value = item.Value;
114 150
115 MinHeapItem item = m_heaps[h].RemoveMin(); 151 return true;
152 }
153 }
154
155 // To get the fair queing, we cycle through each of the
156 // queues when finding an element to dequeue.
157 // We pull (NumberOfQueues - QueueIndex) items from each queue in order
158 // to give lower numbered queues a higher priority and higher percentage
159 // of the bandwidth.
160
161 // Check for more items to be pulled from the current queue
162 if (m_heaps[m_nextQueue].Count > 0 && m_countFromQueue > 0)
163 {
164 m_countFromQueue--;
165
166 MinHeapItem item = m_heaps[m_nextQueue].RemoveMin();
167 m_lookupTable.Remove(item.Value.Entity.LocalId);
168 timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
169 value = item.Value;
170
171 return true;
172 }
173
174 // Find the next non-immediate queue with updates in it
175 for (int i = 0; i < NumberOfQueues; ++i)
176 {
177 m_nextQueue = (uint)((m_nextQueue + 1) % NumberOfQueues);
178 m_countFromQueue = m_queueCounts[m_nextQueue];
179
180 // if this is one of the immediate queues, just skip it
181 if (m_nextQueue < NumberOfImmediateQueues)
182 continue;
183
184 if (m_heaps[m_nextQueue].Count > 0)
185 {
186 m_countFromQueue--;
187
188 MinHeapItem item = m_heaps[m_nextQueue].RemoveMin();
116 m_lookupTable.Remove(item.Value.Entity.LocalId); 189 m_lookupTable.Remove(item.Value.Entity.LocalId);
117 timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime); 190 timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime);
118 value = item.Value; 191 value = item.Value;
@@ -126,7 +199,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
126 return false; 199 return false;
127 } 200 }
128 201
129 internal void Reprioritize(UpdatePriorityHandler handler) 202 /// <summary>
203 /// Reapply the prioritization function to each of the updates currently
204 /// stored in the priority queues.
205 /// </summary
206 public void Reprioritize(UpdatePriorityHandler handler)
130 { 207 {
131 MinHeapItem item; 208 MinHeapItem item;
132 foreach (LookupItem lookup in new List<LookupItem>(this.m_lookupTable.Values)) 209 foreach (LookupItem lookup in new List<LookupItem>(this.m_lookupTable.Values))
@@ -140,7 +217,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
140 { 217 {
141 // unless the priority queue has changed, there is no need to modify 218 // unless the priority queue has changed, there is no need to modify
142 // the entry 219 // the entry
143 pqueue = Util.Clamp<uint>(pqueue, 0, m_numberOfQueues - 1); 220 pqueue = Util.Clamp<uint>(pqueue, 0, NumberOfQueues - 1);
144 if (pqueue != item.PriorityQueue) 221 if (pqueue != item.PriorityQueue)
145 { 222 {
146 lookup.Heap.Remove(lookup.Handle); 223 lookup.Heap.Remove(lookup.Handle);
@@ -161,17 +238,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
161 } 238 }
162 } 239 }
163 240
241 /// <summary>
242 /// </summary>
164 public override string ToString() 243 public override string ToString()
165 { 244 {
166 string s = ""; 245 string s = "";
167 for (int i = 0; i < m_numberOfQueues; i++) 246 for (int i = 0; i < NumberOfQueues; i++)
168 { 247 s += String.Format("{0,7} ",m_heaps[i].Count);
169 if (s != "") s += ",";
170 s += m_heaps[i].Count.ToString();
171 }
172 return s; 248 return s;
173 } 249 }
174 250
251#endregion PublicMethods
252
175#region MinHeapItem 253#region MinHeapItem
176 private struct MinHeapItem : IComparable<MinHeapItem> 254 private struct MinHeapItem : IComparable<MinHeapItem>
177 { 255 {
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 1f7e66d..cdd4224 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -385,6 +385,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
385 public bool IsGroupMember(UUID groupID) { return m_groupPowers.ContainsKey(groupID); } 385 public bool IsGroupMember(UUID groupID) { return m_groupPowers.ContainsKey(groupID); }
386 386
387 /// <summary> 387 /// <summary>
388 /// Entity update queues
389 /// </summary>
390 public PriorityQueue EntityUpdateQueue { get { return m_entityUpdates; } }
391
392 /// <summary>
388 /// First name of the agent/avatar represented by the client 393 /// First name of the agent/avatar represented by the client
389 /// </summary> 394 /// </summary>
390 public string FirstName { get { return m_firstName; } } 395 public string FirstName { get { return m_firstName; } }
@@ -3561,6 +3566,44 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3561 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation)); 3566 m_entityUpdates.Enqueue(priority, new EntityUpdate(entity, updateFlags, m_scene.TimeDilation));
3562 } 3567 }
3563 3568
3569 /// <summary>
3570 /// Requeue an EntityUpdate when it was not acknowledged by the client.
3571 /// We will update the priority and put it in the correct queue, merging update flags
3572 /// with any other updates that may be queued for the same entity.
3573 /// The original update time is used for the merged update.
3574 /// </summary>
3575 private void ResendPrimUpdate(EntityUpdate update)
3576 {
3577 // If the update exists in priority queue, it will be updated.
3578 // If it does not exist then it will be added with the current (rather than its original) priority
3579 uint priority = m_prioritizer.GetUpdatePriority(this, update.Entity);
3580
3581 lock (m_entityUpdates.SyncRoot)
3582 m_entityUpdates.Enqueue(priority, update);
3583 }
3584
3585 /// <summary>
3586 /// Requeue a list of EntityUpdates when they were not acknowledged by the client.
3587 /// We will update the priority and put it in the correct queue, merging update flags
3588 /// with any other updates that may be queued for the same entity.
3589 /// The original update time is used for the merged update.
3590 /// </summary>
3591 private void ResendPrimUpdates(List<EntityUpdate> updates, OutgoingPacket oPacket)
3592 {
3593 // m_log.WarnFormat("[CLIENT] resending prim update {0}",updates[0].UpdateTime);
3594
3595 // Remove the update packet from the list of packets waiting for acknowledgement
3596 // because we are requeuing the list of updates. They will be resent in new packets
3597 // with the most recent state and priority.
3598 m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber);
3599
3600 // Count this as a resent packet since we are going to requeue all of the updates contained in it
3601 Interlocked.Increment(ref m_udpClient.PacketsResent);
3602
3603 foreach (EntityUpdate update in updates)
3604 ResendPrimUpdate(update);
3605 }
3606
3564 private void ProcessEntityUpdates(int maxUpdates) 3607 private void ProcessEntityUpdates(int maxUpdates)
3565 { 3608 {
3566 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>(); 3609 OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new OpenSim.Framework.Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>();
@@ -3568,6 +3611,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3568 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3611 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3569 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>(); 3612 OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
3570 3613
3614 OpenSim.Framework.Lazy<List<EntityUpdate>> objectUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
3615 OpenSim.Framework.Lazy<List<EntityUpdate>> compressedUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
3616 OpenSim.Framework.Lazy<List<EntityUpdate>> terseUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
3617 OpenSim.Framework.Lazy<List<EntityUpdate>> terseAgentUpdates = new OpenSim.Framework.Lazy<List<EntityUpdate>>();
3618
3571 // Check to see if this is a flush 3619 // Check to see if this is a flush
3572 if (maxUpdates <= 0) 3620 if (maxUpdates <= 0)
3573 { 3621 {
@@ -3583,7 +3631,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3583 float avgTimeDilation = 1.0f; 3631 float avgTimeDilation = 1.0f;
3584 IEntityUpdate iupdate; 3632 IEntityUpdate iupdate;
3585 Int32 timeinqueue; // this is just debugging code & can be dropped later 3633 Int32 timeinqueue; // this is just debugging code & can be dropped later
3586 3634
3587 while (updatesThisCall < maxUpdates) 3635 while (updatesThisCall < maxUpdates)
3588 { 3636 {
3589 lock (m_entityUpdates.SyncRoot) 3637 lock (m_entityUpdates.SyncRoot)
@@ -3688,24 +3736,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3688 if (update.Entity is ScenePresence) 3736 if (update.Entity is ScenePresence)
3689 { 3737 {
3690 objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity)); 3738 objectUpdateBlocks.Value.Add(CreateAvatarUpdateBlock((ScenePresence)update.Entity));
3739 objectUpdates.Value.Add(update);
3691 } 3740 }
3692 else 3741 else
3693 { 3742 {
3694 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId)); 3743 objectUpdateBlocks.Value.Add(CreatePrimUpdateBlock((SceneObjectPart)update.Entity, this.m_agentId));
3744 objectUpdates.Value.Add(update);
3695 } 3745 }
3696 } 3746 }
3697 else if (!canUseImproved) 3747 else if (!canUseImproved)
3698 { 3748 {
3699 compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags)); 3749 compressedUpdateBlocks.Value.Add(CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags));
3750 compressedUpdates.Value.Add(update);
3700 } 3751 }
3701 else 3752 else
3702 { 3753 {
3703 if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId) 3754 if (update.Entity is ScenePresence && ((ScenePresence)update.Entity).UUID == AgentId)
3755 {
3704 // Self updates go into a special list 3756 // Self updates go into a special list
3705 terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); 3757 terseAgentUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3758 terseAgentUpdates.Value.Add(update);
3759 }
3706 else 3760 else
3761 {
3707 // Everything else goes here 3762 // Everything else goes here
3708 terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures))); 3763 terseUpdateBlocks.Value.Add(CreateImprovedTerseBlock(update.Entity, updateFlags.HasFlag(PrimUpdateFlags.Textures)));
3764 terseUpdates.Value.Add(update);
3765 }
3709 } 3766 }
3710 3767
3711 #endregion Block Construction 3768 #endregion Block Construction
@@ -3713,28 +3770,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3713 3770
3714 3771
3715 #region Packet Sending 3772 #region Packet Sending
3716
3717 //const float TIME_DILATION = 1.0f;
3718
3719
3720 ushort timeDilation = Utils.FloatToUInt16(avgTimeDilation, 0.0f, 1.0f); 3773 ushort timeDilation = Utils.FloatToUInt16(avgTimeDilation, 0.0f, 1.0f);
3721 3774
3722 if (terseAgentUpdateBlocks.IsValueCreated) 3775 if (terseAgentUpdateBlocks.IsValueCreated)
3723 { 3776 {
3724 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value; 3777 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseAgentUpdateBlocks.Value;
3725 3778
3726 ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); 3779 ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
3727 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; 3780 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle;
3728 packet.RegionData.TimeDilation = timeDilation; 3781 packet.RegionData.TimeDilation = timeDilation;
3729 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; 3782 packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];
3730 3783
3731 for (int i = 0; i < blocks.Count; i++) 3784 for (int i = 0; i < blocks.Count; i++)
3732 packet.ObjectData[i] = blocks[i]; 3785 packet.ObjectData[i] = blocks[i];
3733 3786 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3734 3787 OutPacket(packet, ThrottleOutPacketType.Unknown, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseAgentUpdates.Value, oPacket); });
3735 OutPacket(packet, ThrottleOutPacketType.Unknown, true);
3736 } 3788 }
3737 3789
3738 if (objectUpdateBlocks.IsValueCreated) 3790 if (objectUpdateBlocks.IsValueCreated)
3739 { 3791 {
3740 List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value; 3792 List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value;
@@ -3746,8 +3798,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3746 3798
3747 for (int i = 0; i < blocks.Count; i++) 3799 for (int i = 0; i < blocks.Count; i++)
3748 packet.ObjectData[i] = blocks[i]; 3800 packet.ObjectData[i] = blocks[i];
3749 3801 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3750 OutPacket(packet, ThrottleOutPacketType.Task, true); 3802 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(objectUpdates.Value, oPacket); });
3751 } 3803 }
3752 3804
3753 if (compressedUpdateBlocks.IsValueCreated) 3805 if (compressedUpdateBlocks.IsValueCreated)
@@ -3761,10 +3813,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3761 3813
3762 for (int i = 0; i < blocks.Count; i++) 3814 for (int i = 0; i < blocks.Count; i++)
3763 packet.ObjectData[i] = blocks[i]; 3815 packet.ObjectData[i] = blocks[i];
3764 3816 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3765 OutPacket(packet, ThrottleOutPacketType.Task, true); 3817 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(compressedUpdates.Value, oPacket); });
3766 } 3818 }
3767 3819
3768 if (terseUpdateBlocks.IsValueCreated) 3820 if (terseUpdateBlocks.IsValueCreated)
3769 { 3821 {
3770 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value; 3822 List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value;
@@ -3776,8 +3828,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3776 3828
3777 for (int i = 0; i < blocks.Count; i++) 3829 for (int i = 0; i < blocks.Count; i++)
3778 packet.ObjectData[i] = blocks[i]; 3830 packet.ObjectData[i] = blocks[i];
3779 3831 // If any of the packets created from this call go unacknowledged, all of the updates will be resent
3780 OutPacket(packet, ThrottleOutPacketType.Task, true); 3832 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseUpdates.Value, oPacket); });
3781 } 3833 }
3782 } 3834 }
3783 3835
@@ -3969,7 +4021,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3969 { 4021 {
3970 SendFamilyProps = SendFamilyProps || update.SendFamilyProps; 4022 SendFamilyProps = SendFamilyProps || update.SendFamilyProps;
3971 SendObjectProps = SendObjectProps || update.SendObjectProps; 4023 SendObjectProps = SendObjectProps || update.SendObjectProps;
3972 Flags |= update.Flags; 4024 // other properties may need to be updated by base class
4025 base.Update(update);
3973 } 4026 }
3974 } 4027 }
3975 4028
@@ -3980,6 +4033,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3980 m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false)); 4033 m_entityProps.Enqueue(priority, new ObjectPropertyUpdate(entity,requestFlags,true,false));
3981 } 4034 }
3982 4035
4036 private void ResendPropertyUpdate(ObjectPropertyUpdate update)
4037 {
4038 uint priority = 0;
4039 lock (m_entityProps.SyncRoot)
4040 m_entityProps.Enqueue(priority, update);
4041 }
4042
4043 private void ResendPropertyUpdates(List<ObjectPropertyUpdate> updates, OutgoingPacket oPacket)
4044 {
4045 // m_log.WarnFormat("[CLIENT] resending object property {0}",updates[0].UpdateTime);
4046
4047 // Remove the update packet from the list of packets waiting for acknowledgement
4048 // because we are requeuing the list of updates. They will be resent in new packets
4049 // with the most recent state.
4050 m_udpClient.NeedAcks.Remove(oPacket.SequenceNumber);
4051
4052 // Count this as a resent packet since we are going to requeue all of the updates contained in it
4053 Interlocked.Increment(ref m_udpClient.PacketsResent);
4054
4055 foreach (ObjectPropertyUpdate update in updates)
4056 ResendPropertyUpdate(update);
4057 }
4058
3983 public void SendObjectPropertiesReply(ISceneEntity entity) 4059 public void SendObjectPropertiesReply(ISceneEntity entity)
3984 { 4060 {
3985 uint priority = 0; // time based ordering only 4061 uint priority = 0; // time based ordering only
@@ -3995,6 +4071,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3995 OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>> objectPropertiesBlocks = 4071 OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>> objectPropertiesBlocks =
3996 new OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>>(); 4072 new OpenSim.Framework.Lazy<List<ObjectPropertiesPacket.ObjectDataBlock>>();
3997 4073
4074 OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>> familyUpdates =
4075 new OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>>();
4076
4077 OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>> propertyUpdates =
4078 new OpenSim.Framework.Lazy<List<ObjectPropertyUpdate>>();
4079
3998 IEntityUpdate iupdate; 4080 IEntityUpdate iupdate;
3999 Int32 timeinqueue; // this is just debugging code & can be dropped later 4081 Int32 timeinqueue; // this is just debugging code & can be dropped later
4000 4082
@@ -4013,6 +4095,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4013 SceneObjectPart sop = (SceneObjectPart)update.Entity; 4095 SceneObjectPart sop = (SceneObjectPart)update.Entity;
4014 ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags); 4096 ObjectPropertiesFamilyPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesFamilyBlock(sop,update.Flags);
4015 objectFamilyBlocks.Value.Add(objPropDB); 4097 objectFamilyBlocks.Value.Add(objPropDB);
4098 familyUpdates.Value.Add(update);
4016 } 4099 }
4017 } 4100 }
4018 4101
@@ -4023,6 +4106,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4023 SceneObjectPart sop = (SceneObjectPart)update.Entity; 4106 SceneObjectPart sop = (SceneObjectPart)update.Entity;
4024 ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop); 4107 ObjectPropertiesPacket.ObjectDataBlock objPropDB = CreateObjectPropertiesBlock(sop);
4025 objectPropertiesBlocks.Value.Add(objPropDB); 4108 objectPropertiesBlocks.Value.Add(objPropDB);
4109 propertyUpdates.Value.Add(update);
4026 } 4110 }
4027 } 4111 }
4028 4112
@@ -4030,12 +4114,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4030 } 4114 }
4031 4115
4032 4116
4033 Int32 ppcnt = 0; 4117 // Int32 ppcnt = 0;
4034 Int32 pbcnt = 0; 4118 // Int32 pbcnt = 0;
4035 4119
4036 if (objectPropertiesBlocks.IsValueCreated) 4120 if (objectPropertiesBlocks.IsValueCreated)
4037 { 4121 {
4038 List<ObjectPropertiesPacket.ObjectDataBlock> blocks = objectPropertiesBlocks.Value; 4122 List<ObjectPropertiesPacket.ObjectDataBlock> blocks = objectPropertiesBlocks.Value;
4123 List<ObjectPropertyUpdate> updates = propertyUpdates.Value;
4039 4124
4040 ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); 4125 ObjectPropertiesPacket packet = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
4041 packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count]; 4126 packet.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[blocks.Count];
@@ -4043,28 +4128,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4043 packet.ObjectData[i] = blocks[i]; 4128 packet.ObjectData[i] = blocks[i];
4044 4129
4045 packet.Header.Zerocoded = true; 4130 packet.Header.Zerocoded = true;
4046 OutPacket(packet, ThrottleOutPacketType.Task, true);
4047 4131
4048 pbcnt += blocks.Count; 4132 // Pass in the delegate so that if this packet needs to be resent, we send the current properties
4049 ppcnt++; 4133 // of the object rather than the properties when the packet was created
4134 OutPacket(packet, ThrottleOutPacketType.Task, true,
4135 delegate(OutgoingPacket oPacket)
4136 {
4137 ResendPropertyUpdates(updates, oPacket);
4138 });
4139
4140 // pbcnt += blocks.Count;
4141 // ppcnt++;
4050 } 4142 }
4051 4143
4052 Int32 fpcnt = 0; 4144 // Int32 fpcnt = 0;
4053 Int32 fbcnt = 0; 4145 // Int32 fbcnt = 0;
4054 4146
4055 if (objectFamilyBlocks.IsValueCreated) 4147 if (objectFamilyBlocks.IsValueCreated)
4056 { 4148 {
4057 List<ObjectPropertiesFamilyPacket.ObjectDataBlock> blocks = objectFamilyBlocks.Value; 4149 List<ObjectPropertiesFamilyPacket.ObjectDataBlock> blocks = objectFamilyBlocks.Value;
4058 4150
4059 // ObjectPropertiesFamilyPacket objPropFamilyPack =
4060 // (ObjectPropertiesFamilyPacket)PacketPool.Instance.GetPacket(PacketType.ObjectPropertiesFamily);
4061 //
4062 // objPropFamilyPack.ObjectData = new ObjectPropertiesFamilyPacket.ObjectDataBlock[blocks.Count];
4063 // for (int i = 0; i < blocks.Count; i++)
4064 // objPropFamilyPack.ObjectData[i] = blocks[i];
4065 //
4066 // OutPacket(objPropFamilyPack, ThrottleOutPacketType.Task, true);
4067
4068 // one packet per object block... uggh... 4151 // one packet per object block... uggh...
4069 for (int i = 0; i < blocks.Count; i++) 4152 for (int i = 0; i < blocks.Count; i++)
4070 { 4153 {
@@ -4073,10 +4156,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4073 4156
4074 packet.ObjectData = blocks[i]; 4157 packet.ObjectData = blocks[i];
4075 packet.Header.Zerocoded = true; 4158 packet.Header.Zerocoded = true;
4076 OutPacket(packet, ThrottleOutPacketType.Task);
4077 4159
4078 fpcnt++; 4160 // Pass in the delegate so that if this packet needs to be resent, we send the current properties
4079 fbcnt++; 4161 // of the object rather than the properties when the packet was created
4162 List<ObjectPropertyUpdate> updates = new List<ObjectPropertyUpdate>();
4163 updates.Add(familyUpdates.Value[i]);
4164 OutPacket(packet, ThrottleOutPacketType.Task, true,
4165 delegate(OutgoingPacket oPacket)
4166 {
4167 ResendPropertyUpdates(updates, oPacket);
4168 });
4169
4170 // fpcnt++;
4171 // fbcnt++;
4080 } 4172 }
4081 4173
4082 } 4174 }
@@ -4113,7 +4205,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4113 4205
4114 return block; 4206 return block;
4115 } 4207 }
4116 4208
4117 private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop) 4209 private ObjectPropertiesPacket.ObjectDataBlock CreateObjectPropertiesBlock(SceneObjectPart sop)
4118 { 4210 {
4119 //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties); 4211 //ObjectPropertiesPacket proper = (ObjectPropertiesPacket)PacketPool.Instance.GetPacket(PacketType.ObjectProperties);
@@ -11328,7 +11420,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11328 /// <returns></returns> 11420 /// <returns></returns>
11329 public byte[] GetThrottlesPacked(float multiplier) 11421 public byte[] GetThrottlesPacked(float multiplier)
11330 { 11422 {
11331 return m_udpClient.GetThrottlesPacked(); 11423 return m_udpClient.GetThrottlesPacked(multiplier);
11332 } 11424 }
11333 11425
11334 /// <summary> 11426 /// <summary>
@@ -11363,6 +11455,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11363 /// handles splitting manually</param> 11455 /// handles splitting manually</param>
11364 protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting) 11456 protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting)
11365 { 11457 {
11458 OutPacket(packet, throttlePacketType, doAutomaticSplitting, null);
11459 }
11460
11461 /// <summary>
11462 /// This is the starting point for sending a simulator packet out to the client
11463 /// </summary>
11464 /// <param name="packet">Packet to send</param>
11465 /// <param name="throttlePacketType">Throttling category for the packet</param>
11466 /// <param name="doAutomaticSplitting">True to automatically split oversized
11467 /// packets (the default), or false to disable splitting if the calling code
11468 /// handles splitting manually</param>
11469 /// <param name="method">The method to be called in the event this packet is reliable
11470 /// and unacknowledged. The server will provide normal resend capability if you do not
11471 /// provide your own method.</param>
11472 protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting, UnackedPacketMethod method)
11473 {
11366 if (m_debugPacketLevel > 0) 11474 if (m_debugPacketLevel > 0)
11367 { 11475 {
11368 bool logPacket = true; 11476 bool logPacket = true;
@@ -11388,7 +11496,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11388 m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type); 11496 m_log.DebugFormat("[CLIENT]: Packet OUT {0}", packet.Type);
11389 } 11497 }
11390 11498
11391 m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting); 11499 m_udpServer.SendPacket(m_udpClient, packet, throttlePacketType, doAutomaticSplitting, method);
11392 } 11500 }
11393 11501
11394 public bool AddMoney(int debit) 11502 public bool AddMoney(int debit)
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index 7be8a0a..50f1e2c 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -135,7 +135,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
135 private int m_nextOnQueueEmpty = 1; 135 private int m_nextOnQueueEmpty = 1;
136 136
137 /// <summary>Throttle bucket for this agent's connection</summary> 137 /// <summary>Throttle bucket for this agent's connection</summary>
138 private readonly TokenBucket m_throttleClient; 138 private readonly AdaptiveTokenBucket m_throttleClient;
139 public AdaptiveTokenBucket FlowThrottle
140 {
141 get { return m_throttleClient; }
142 }
143
139 /// <summary>Throttle bucket for this agent's connection</summary> 144 /// <summary>Throttle bucket for this agent's connection</summary>
140 private readonly TokenBucket m_throttleCategory; 145 private readonly TokenBucket m_throttleCategory;
141 /// <summary>Throttle buckets for each packet category</summary> 146 /// <summary>Throttle buckets for each packet category</summary>
@@ -176,9 +181,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
176 m_maxRTO = maxRTO; 181 m_maxRTO = maxRTO;
177 182
178 // Create a token bucket throttle for this client that has the scene token bucket as a parent 183 // Create a token bucket throttle for this client that has the scene token bucket as a parent
179 m_throttleClient = new TokenBucket(parentThrottle, rates.TotalLimit); 184 m_throttleClient = new AdaptiveTokenBucket(parentThrottle, rates.Total, rates.AdaptiveThrottlesEnabled);
180 // Create a token bucket throttle for the total categary with the client bucket as a throttle 185 // Create a token bucket throttle for the total categary with the client bucket as a throttle
181 m_throttleCategory = new TokenBucket(m_throttleClient, rates.TotalLimit); 186 m_throttleCategory = new TokenBucket(m_throttleClient, rates.Total);
182 // Create an array of token buckets for this clients different throttle categories 187 // Create an array of token buckets for this clients different throttle categories
183 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; 188 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
184 189
@@ -189,7 +194,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
189 // Initialize the packet outboxes, where packets sit while they are waiting for tokens 194 // Initialize the packet outboxes, where packets sit while they are waiting for tokens
190 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); 195 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
191 // Initialize the token buckets that control the throttling for each category 196 // Initialize the token buckets that control the throttling for each category
192 m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetLimit(type)); 197 m_throttleCategories[i] = new TokenBucket(m_throttleCategory, rates.GetRate(type));
193 } 198 }
194 199
195 // Default the retransmission timeout to three seconds 200 // Default the retransmission timeout to three seconds
@@ -223,26 +228,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
223 /// <returns>Information about the client connection</returns> 228 /// <returns>Information about the client connection</returns>
224 public ClientInfo GetClientInfo() 229 public ClientInfo GetClientInfo()
225 { 230 {
226///<mic>
227 TokenBucket tb;
228
229 tb = m_throttleClient.Parent;
230 m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest,"ROOT");
231
232 tb = m_throttleClient;
233 m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CLIENT");
234
235 tb = m_throttleCategory;
236 m_log.WarnFormat("[TOKENS] {3}: Actual={0},Request={1},TotalRequest={2}",tb.DripRate,tb.RequestedDripRate,tb.TotalDripRequest," CATEGORY");
237
238 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
239 {
240 tb = m_throttleCategories[i];
241 m_log.WarnFormat("[TOKENS] {4} <{0}:{1}>: Actual={2},Requested={3}",AgentID,i,tb.DripRate,tb.RequestedDripRate," BUCKET");
242 }
243
244///</mic>
245
246 // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists 231 // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists
247 // of pending and needed ACKs for every client every time some method wants information about 232 // of pending and needed ACKs for every client every time some method wants information about
248 // this connection is a recipe for poor performance 233 // this connection is a recipe for poor performance
@@ -254,12 +239,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
254 info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; 239 info.landThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
255 info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; 240 info.windThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
256 info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; 241 info.cloudThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
257 // info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
258 info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; 242 info.taskThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
259 info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; 243 info.assetThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
260 info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; 244 info.textureThrottle = (int)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
261 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + 245 info.totalThrottle = (int)m_throttleCategory.DripRate;
262 info.taskThrottle + info.assetThrottle + info.textureThrottle;
263 246
264 return info; 247 return info;
265 } 248 }
@@ -346,8 +329,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
346 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); 329 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
347 // State is a subcategory of task that we allocate a percentage to 330 // State is a subcategory of task that we allocate a percentage to
348 int state = 0; 331 int state = 0;
349 // int state = (int)((float)task * STATE_TASK_PERCENTAGE);
350 // task -= state;
351 332
352 // Make sure none of the throttles are set below our packet MTU, 333 // Make sure none of the throttles are set below our packet MTU,
353 // otherwise a throttle could become permanently clogged 334 // otherwise a throttle could become permanently clogged
@@ -358,19 +339,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
358 task = Math.Max(task, LLUDPServer.MTU); 339 task = Math.Max(task, LLUDPServer.MTU);
359 texture = Math.Max(texture, LLUDPServer.MTU); 340 texture = Math.Max(texture, LLUDPServer.MTU);
360 asset = Math.Max(asset, LLUDPServer.MTU); 341 asset = Math.Max(asset, LLUDPServer.MTU);
361 state = Math.Max(state, LLUDPServer.MTU);
362
363 int total = resend + land + wind + cloud + task + texture + asset + state;
364 342
365 //m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}", 343 //int total = resend + land + wind + cloud + task + texture + asset;
366 // AgentID, resend, land, wind, cloud, task, texture, asset, state, total); 344 //m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, Total={8}",
345 // AgentID, resend, land, wind, cloud, task, texture, asset, total);
367 346
368 // Update the token buckets with new throttle values 347 // Update the token buckets with new throttle values
369 TokenBucket bucket; 348 TokenBucket bucket;
370 349
371 bucket = m_throttleCategory;
372 bucket.RequestedDripRate = total;
373
374 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend]; 350 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
375 bucket.RequestedDripRate = resend; 351 bucket.RequestedDripRate = resend;
376 352
@@ -399,22 +375,38 @@ namespace OpenSim.Region.ClientStack.LindenUDP
399 m_packedThrottles = null; 375 m_packedThrottles = null;
400 } 376 }
401 377
402 public byte[] GetThrottlesPacked() 378 public byte[] GetThrottlesPacked(float multiplier)
403 { 379 {
404 byte[] data = m_packedThrottles; 380 byte[] data = m_packedThrottles;
405 381
406 if (data == null) 382 if (data == null)
407 { 383 {
384 float rate;
385
408 data = new byte[7 * 4]; 386 data = new byte[7 * 4];
409 int i = 0; 387 int i = 0;
410 388
411 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate), 0, data, i, 4); i += 4; 389 // multiply by 8 to convert bytes back to bits
412 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate), 0, data, i, 4); i += 4; 390 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].RequestedDripRate * 8 * multiplier;
413 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate), 0, data, i, 4); i += 4; 391 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
414 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate), 0, data, i, 4); i += 4; 392
415 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate), 0, data, i, 4); i += 4; 393 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Land].RequestedDripRate * 8 * multiplier;
416 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate), 0, data, i, 4); i += 4; 394 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
417 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate), 0, data, i, 4); i += 4; 395
396 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].RequestedDripRate * 8 * multiplier;
397 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
398
399 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].RequestedDripRate * 8 * multiplier;
400 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
401
402 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Task].RequestedDripRate * 8 * multiplier;
403 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
404
405 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].RequestedDripRate * 8 * multiplier;
406 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
407
408 rate = (float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].RequestedDripRate * 8 * multiplier;
409 Buffer.BlockCopy(Utils.FloatToBytes(rate), 0, data, i, 4); i += 4;
418 410
419 m_packedThrottles = data; 411 m_packedThrottles = data;
420 } 412 }
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index d08b25f..aff90c5 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
@@ -297,7 +297,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
297 delegate(IClientAPI client) 297 delegate(IClientAPI client)
298 { 298 {
299 if (client is LLClientView) 299 if (client is LLClientView)
300 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); 300 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null);
301 } 301 }
302 ); 302 );
303 } 303 }
@@ -309,7 +309,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
309 delegate(IClientAPI client) 309 delegate(IClientAPI client)
310 { 310 {
311 if (client is LLClientView) 311 if (client is LLClientView)
312 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); 312 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null);
313 } 313 }
314 ); 314 );
315 } 315 }
@@ -322,7 +322,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
322 /// <param name="packet"></param> 322 /// <param name="packet"></param>
323 /// <param name="category"></param> 323 /// <param name="category"></param>
324 /// <param name="allowSplitting"></param> 324 /// <param name="allowSplitting"></param>
325 public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting) 325 public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting, UnackedPacketMethod method)
326 { 326 {
327 // CoarseLocationUpdate packets cannot be split in an automated way 327 // CoarseLocationUpdate packets cannot be split in an automated way
328 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) 328 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
@@ -339,13 +339,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
339 for (int i = 0; i < packetCount; i++) 339 for (int i = 0; i < packetCount; i++)
340 { 340 {
341 byte[] data = datas[i]; 341 byte[] data = datas[i];
342 SendPacketData(udpClient, data, packet.Type, category); 342 SendPacketData(udpClient, data, packet.Type, category, method);
343 } 343 }
344 } 344 }
345 else 345 else
346 { 346 {
347 byte[] data = packet.ToBytes(); 347 byte[] data = packet.ToBytes();
348 SendPacketData(udpClient, data, packet.Type, category); 348 SendPacketData(udpClient, data, packet.Type, category, method);
349 } 349 }
350 } 350 }
351 351
@@ -356,7 +356,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
356 /// <param name="data"></param> 356 /// <param name="data"></param>
357 /// <param name="type"></param> 357 /// <param name="type"></param>
358 /// <param name="category"></param> 358 /// <param name="category"></param>
359 public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category) 359 public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category, UnackedPacketMethod method)
360 { 360 {
361 int dataLength = data.Length; 361 int dataLength = data.Length;
362 bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; 362 bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0;
@@ -411,7 +411,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
411 411
412 #region Queue or Send 412 #region Queue or Send
413 413
414 OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); 414 OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, null);
415 // If we were not provided a method for handling unacked, use the UDPServer default method
416 outgoingPacket.UnackedMethod = ((method == null) ? delegate(OutgoingPacket oPacket) { ResendUnacked(oPacket); } : method);
415 417
416 // If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will 418 // If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will
417 // continue to display the deleted object until relog. Therefore, we need to always queue a kill object 419 // 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
445 packet.Header.Reliable = false; 447 packet.Header.Reliable = false;
446 packet.Packets = blocks.ToArray(); 448 packet.Packets = blocks.ToArray();
447 449
448 SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true); 450 SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true, null);
449 } 451 }
450 } 452 }
451 453
@@ -458,17 +460,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
458 // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit 460 // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit
459 pc.PingID.OldestUnacked = 0; 461 pc.PingID.OldestUnacked = 0;
460 462
461 SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false); 463 SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false, null);
462 } 464 }
463 465
464 public void CompletePing(LLUDPClient udpClient, byte pingID) 466 public void CompletePing(LLUDPClient udpClient, byte pingID)
465 { 467 {
466 CompletePingCheckPacket completePing = new CompletePingCheckPacket(); 468 CompletePingCheckPacket completePing = new CompletePingCheckPacket();
467 completePing.PingID.PingID = pingID; 469 completePing.PingID.PingID = pingID;
468 SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false); 470 SendPacket(udpClient, completePing, ThrottleOutPacketType.Unknown, false, null);
469 } 471 }
470 472
471 public void ResendUnacked(LLUDPClient udpClient) 473 public void HandleUnacked(LLUDPClient udpClient)
472 { 474 {
473 if (!udpClient.IsConnected) 475 if (!udpClient.IsConnected)
474 return; 476 return;
@@ -488,31 +490,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
488 490
489 if (expiredPackets != null) 491 if (expiredPackets != null)
490 { 492 {
491 //m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO); 493 //m_log.Debug("[LLUDPSERVER]: Handling " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO);
492
493 // Exponential backoff of the retransmission timeout 494 // Exponential backoff of the retransmission timeout
494 udpClient.BackoffRTO(); 495 udpClient.BackoffRTO();
496 for (int i = 0; i < expiredPackets.Count; ++i)
497 expiredPackets[i].UnackedMethod(expiredPackets[i]);
498 }
499 }
495 500
496 // Resend packets 501 public void ResendUnacked(OutgoingPacket outgoingPacket)
497 for (int i = 0; i < expiredPackets.Count; i++) 502 {
498 { 503 //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
499 OutgoingPacket outgoingPacket = expiredPackets[i]; 504 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
500
501 //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
502 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
503 505
504 // Set the resent flag 506 // Set the resent flag
505 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); 507 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
506 outgoingPacket.Category = ThrottleOutPacketType.Resend; 508 outgoingPacket.Category = ThrottleOutPacketType.Resend;
507 509
508 // Bump up the resend count on this packet 510 // Bump up the resend count on this packet
509 Interlocked.Increment(ref outgoingPacket.ResendCount); 511 Interlocked.Increment(ref outgoingPacket.ResendCount);
510 512
511 // Requeue or resend the packet 513 // Requeue or resend the packet
512 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, false)) 514 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, false))
513 SendPacketFinal(outgoingPacket); 515 SendPacketFinal(outgoingPacket);
514 }
515 }
516 } 516 }
517 517
518 public void Flush(LLUDPClient udpClient) 518 public void Flush(LLUDPClient udpClient)
@@ -672,7 +672,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
672 if (packet.Header.AppendedAcks && packet.Header.AckList != null) 672 if (packet.Header.AppendedAcks && packet.Header.AckList != null)
673 { 673 {
674 for (int i = 0; i < packet.Header.AckList.Length; i++) 674 for (int i = 0; i < packet.Header.AckList.Length; i++)
675 udpClient.NeedAcks.Remove(packet.Header.AckList[i], now, packet.Header.Resent); 675 udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent);
676 } 676 }
677 677
678 // Handle PacketAck packets 678 // Handle PacketAck packets
@@ -681,7 +681,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
681 PacketAckPacket ackPacket = (PacketAckPacket)packet; 681 PacketAckPacket ackPacket = (PacketAckPacket)packet;
682 682
683 for (int i = 0; i < ackPacket.Packets.Length; i++) 683 for (int i = 0; i < ackPacket.Packets.Length; i++)
684 udpClient.NeedAcks.Remove(ackPacket.Packets[i].ID, now, packet.Header.Resent); 684 udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent);
685 685
686 // We don't need to do anything else with PacketAck packets 686 // We don't need to do anything else with PacketAck packets
687 return; 687 return;
@@ -1096,7 +1096,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1096 if (udpClient.IsConnected) 1096 if (udpClient.IsConnected)
1097 { 1097 {
1098 if (m_resendUnacked) 1098 if (m_resendUnacked)
1099 ResendUnacked(udpClient); 1099 HandleUnacked(udpClient);
1100 1100
1101 if (m_sendAcks) 1101 if (m_sendAcks)
1102 SendAcks(udpClient); 1102 SendAcks(udpClient);
@@ -1152,7 +1152,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1152 nticksUnack++; 1152 nticksUnack++;
1153 watch2.Start(); 1153 watch2.Start();
1154 1154
1155 ResendUnacked(udpClient); 1155 HandleUnacked(udpClient);
1156 1156
1157 watch2.Stop(); 1157 watch2.Stop();
1158 avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack); 1158 avgResendUnackedTicks = (nticksUnack - 1)/(float)nticksUnack * avgResendUnackedTicks + (watch2.ElapsedTicks / (float)nticksUnack);
diff --git a/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
index 1a1a1cb..76c6c14 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
@@ -31,6 +31,8 @@ using OpenMetaverse;
31 31
32namespace OpenSim.Region.ClientStack.LindenUDP 32namespace OpenSim.Region.ClientStack.LindenUDP
33{ 33{
34
35 public delegate void UnackedPacketMethod(OutgoingPacket oPacket);
34 /// <summary> 36 /// <summary>
35 /// Holds a reference to the <seealso cref="LLUDPClient"/> this packet is 37 /// Holds a reference to the <seealso cref="LLUDPClient"/> this packet is
36 /// destined for, along with the serialized packet data, sequence number 38 /// destined for, along with the serialized packet data, sequence number
@@ -52,6 +54,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
52 public int TickCount; 54 public int TickCount;
53 /// <summary>Category this packet belongs to</summary> 55 /// <summary>Category this packet belongs to</summary>
54 public ThrottleOutPacketType Category; 56 public ThrottleOutPacketType Category;
57 /// <summary>The delegate to be called if this packet is determined to be unacknowledged</summary>
58 public UnackedPacketMethod UnackedMethod;
55 59
56 /// <summary> 60 /// <summary>
57 /// Default constructor 61 /// Default constructor
@@ -60,11 +64,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
60 /// <param name="buffer">Serialized packet data. If the flags or sequence number 64 /// <param name="buffer">Serialized packet data. If the flags or sequence number
61 /// need to be updated, they will be injected directly into this binary buffer</param> 65 /// need to be updated, they will be injected directly into this binary buffer</param>
62 /// <param name="category">Throttling category for this packet</param> 66 /// <param name="category">Throttling category for this packet</param>
63 public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category) 67 public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category, UnackedPacketMethod method)
64 { 68 {
65 Client = client; 69 Client = client;
66 Buffer = buffer; 70 Buffer = buffer;
67 Category = category; 71 Category = category;
72 UnackedMethod = method;
68 } 73 }
69 } 74 }
70} 75}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
index aaf6e26..c9aac0b 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
@@ -52,30 +52,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
52 public int Texture; 52 public int Texture;
53 /// <summary>Drip rate for asset packets</summary> 53 /// <summary>Drip rate for asset packets</summary>
54 public int Asset; 54 public int Asset;
55 /// <summary>Drip rate for state packets</summary> 55
56 public int State;
57 /// <summary>Drip rate for the parent token bucket</summary> 56 /// <summary>Drip rate for the parent token bucket</summary>
58 public int Total; 57 public int Total;
59 58
60 /// <summary>Maximum burst rate for resent packets</summary> 59 /// <summary>Flag used to enable adaptive throttles</summary>
61 public int ResendLimit; 60 public bool AdaptiveThrottlesEnabled;
62 /// <summary>Maximum burst rate for land packets</summary> 61
63 public int LandLimit;
64 /// <summary>Maximum burst rate for wind packets</summary>
65 public int WindLimit;
66 /// <summary>Maximum burst rate for cloud packets</summary>
67 public int CloudLimit;
68 /// <summary>Maximum burst rate for task (state and transaction) packets</summary>
69 public int TaskLimit;
70 /// <summary>Maximum burst rate for texture packets</summary>
71 public int TextureLimit;
72 /// <summary>Maximum burst rate for asset packets</summary>
73 public int AssetLimit;
74 /// <summary>Maximum burst rate for state packets</summary>
75 public int StateLimit;
76 /// <summary>Burst rate for the parent token bucket</summary>
77 public int TotalLimit;
78
79 /// <summary> 62 /// <summary>
80 /// Default constructor 63 /// Default constructor
81 /// </summary> 64 /// </summary>
@@ -86,26 +69,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
86 { 69 {
87 IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"]; 70 IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"];
88 71
89 Resend = throttleConfig.GetInt("resend_default", 12500); 72 Resend = throttleConfig.GetInt("resend_default", 6625);
90 Land = throttleConfig.GetInt("land_default", 1000); 73 Land = throttleConfig.GetInt("land_default", 9125);
91 Wind = throttleConfig.GetInt("wind_default", 1000); 74 Wind = throttleConfig.GetInt("wind_default", 1750);
92 Cloud = throttleConfig.GetInt("cloud_default", 1000); 75 Cloud = throttleConfig.GetInt("cloud_default", 1750);
93 Task = throttleConfig.GetInt("task_default", 1000); 76 Task = throttleConfig.GetInt("task_default", 18500);
94 Texture = throttleConfig.GetInt("texture_default", 1000); 77 Texture = throttleConfig.GetInt("texture_default", 18500);
95 Asset = throttleConfig.GetInt("asset_default", 1000); 78 Asset = throttleConfig.GetInt("asset_default", 10500);
96 State = throttleConfig.GetInt("state_default", 1000);
97
98 ResendLimit = throttleConfig.GetInt("resend_limit", 18750);
99 LandLimit = throttleConfig.GetInt("land_limit", 29750);
100 WindLimit = throttleConfig.GetInt("wind_limit", 18750);
101 CloudLimit = throttleConfig.GetInt("cloud_limit", 18750);
102 TaskLimit = throttleConfig.GetInt("task_limit", 18750);
103 TextureLimit = throttleConfig.GetInt("texture_limit", 55750);
104 AssetLimit = throttleConfig.GetInt("asset_limit", 27500);
105 StateLimit = throttleConfig.GetInt("state_limit", 37000);
106 79
107 Total = throttleConfig.GetInt("client_throttle_max_bps", 0); 80 Total = throttleConfig.GetInt("client_throttle_max_bps", 0);
108 TotalLimit = Total; 81
82 AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false);
109 } 83 }
110 catch (Exception) { } 84 catch (Exception) { }
111 } 85 }
@@ -128,34 +102,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
128 return Texture; 102 return Texture;
129 case ThrottleOutPacketType.Asset: 103 case ThrottleOutPacketType.Asset:
130 return Asset; 104 return Asset;
131 case ThrottleOutPacketType.State:
132 return State;
133 case ThrottleOutPacketType.Unknown:
134 default:
135 return 0;
136 }
137 }
138
139 public int GetLimit(ThrottleOutPacketType type)
140 {
141 switch (type)
142 {
143 case ThrottleOutPacketType.Resend:
144 return ResendLimit;
145 case ThrottleOutPacketType.Land:
146 return LandLimit;
147 case ThrottleOutPacketType.Wind:
148 return WindLimit;
149 case ThrottleOutPacketType.Cloud:
150 return CloudLimit;
151 case ThrottleOutPacketType.Task:
152 return TaskLimit;
153 case ThrottleOutPacketType.Texture:
154 return TextureLimit;
155 case ThrottleOutPacketType.Asset:
156 return AssetLimit;
157 case ThrottleOutPacketType.State:
158 return StateLimit;
159 case ThrottleOutPacketType.Unknown: 105 case ThrottleOutPacketType.Unknown:
160 default: 106 default:
161 return 0; 107 return 0;
diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
index 07b0a1d..2ec79ab 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
@@ -29,6 +29,8 @@ using System;
29using System.Collections; 29using System.Collections;
30using System.Collections.Generic; 30using System.Collections.Generic;
31using System.Reflection; 31using System.Reflection;
32using OpenSim.Framework;
33
32using log4net; 34using log4net;
33 35
34namespace OpenSim.Region.ClientStack.LindenUDP 36namespace OpenSim.Region.ClientStack.LindenUDP
@@ -48,31 +50,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP
48 /// Number of ticks (ms) per quantum, drip rate and max burst 50 /// Number of ticks (ms) per quantum, drip rate and max burst
49 /// are defined over this interval. 51 /// are defined over this interval.
50 /// </summary> 52 /// </summary>
51 private const Int32 m_ticksPerQuantum = 1000; 53 protected const Int32 m_ticksPerQuantum = 1000;
52 54
53 /// <summary> 55 /// <summary>
54 /// This is the number of quantums worth of packets that can 56 /// This is the number of quantums worth of packets that can
55 /// be accommodated during a burst 57 /// be accommodated during a burst
56 /// </summary> 58 /// </summary>
57 private const Double m_quantumsPerBurst = 1.5; 59 protected const Double m_quantumsPerBurst = 1.5;
58 60
59 /// <summary> 61 /// <summary>
60 /// </summary> 62 /// </summary>
61 private const Int32 m_minimumDripRate = 1400; 63 protected const Int32 m_minimumDripRate = 1400;
62 64
63 /// <summary>Time of the last drip, in system ticks</summary> 65 /// <summary>Time of the last drip, in system ticks</summary>
64 private Int32 m_lastDrip; 66 protected Int32 m_lastDrip;
65 67
66 /// <summary> 68 /// <summary>
67 /// The number of bytes that can be sent at this moment. This is the 69 /// The number of bytes that can be sent at this moment. This is the
68 /// current number of tokens in the bucket 70 /// current number of tokens in the bucket
69 /// </summary> 71 /// </summary>
70 private Int64 m_tokenCount; 72 protected Int64 m_tokenCount;
71 73
72 /// <summary> 74 /// <summary>
73 /// Map of children buckets and their requested maximum burst rate 75 /// Map of children buckets and their requested maximum burst rate
74 /// </summary> 76 /// </summary>
75 private Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>(); 77 protected Dictionary<TokenBucket,Int64> m_children = new Dictionary<TokenBucket,Int64>();
76 78
77#region Properties 79#region Properties
78 80
@@ -81,7 +83,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
81 /// parent. The parent bucket will limit the aggregate bandwidth of all 83 /// parent. The parent bucket will limit the aggregate bandwidth of all
82 /// of its children buckets 84 /// of its children buckets
83 /// </summary> 85 /// </summary>
84 private TokenBucket m_parent; 86 protected TokenBucket m_parent;
85 public TokenBucket Parent 87 public TokenBucket Parent
86 { 88 {
87 get { return m_parent; } 89 get { return m_parent; }
@@ -93,7 +95,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
93 /// of tokens that can accumulate in the bucket at any one time. This 95 /// of tokens that can accumulate in the bucket at any one time. This
94 /// also sets the total request for leaf nodes 96 /// also sets the total request for leaf nodes
95 /// </summary> 97 /// </summary>
96 private Int64 m_burstRate; 98 protected Int64 m_burstRate;
97 public Int64 RequestedBurstRate 99 public Int64 RequestedBurstRate
98 { 100 {
99 get { return m_burstRate; } 101 get { return m_burstRate; }
@@ -118,8 +120,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
118 /// <remarks>Tokens are added to the bucket any time 120 /// <remarks>Tokens are added to the bucket any time
119 /// <seealso cref="RemoveTokens"/> is called, at the granularity of 121 /// <seealso cref="RemoveTokens"/> is called, at the granularity of
120 /// the system tick interval (typically around 15-22ms)</remarks> 122 /// the system tick interval (typically around 15-22ms)</remarks>
121 private Int64 m_dripRate; 123 protected Int64 m_dripRate;
122 public Int64 RequestedDripRate 124 public virtual Int64 RequestedDripRate
123 { 125 {
124 get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); } 126 get { return (m_dripRate == 0 ? m_totalDripRequest : m_dripRate); }
125 set { 127 set {
@@ -131,7 +133,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
131 } 133 }
132 } 134 }
133 135
134 public Int64 DripRate 136 public virtual Int64 DripRate
135 { 137 {
136 get { 138 get {
137 if (m_parent == null) 139 if (m_parent == null)
@@ -149,7 +151,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
149 /// The current total of the requested maximum burst rates of 151 /// The current total of the requested maximum burst rates of
150 /// this bucket's children buckets. 152 /// this bucket's children buckets.
151 /// </summary> 153 /// </summary>
152 private Int64 m_totalDripRequest; 154 protected Int64 m_totalDripRequest;
153 public Int64 TotalDripRequest 155 public Int64 TotalDripRequest
154 { 156 {
155 get { return m_totalDripRequest; } 157 get { return m_totalDripRequest; }
@@ -177,7 +179,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
177 RequestedDripRate = dripRate; 179 RequestedDripRate = dripRate;
178 // TotalDripRequest = dripRate; // this will be overwritten when a child node registers 180 // TotalDripRequest = dripRate; // this will be overwritten when a child node registers
179 // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst); 181 // MaxBurst = (Int64)((double)dripRate * m_quantumsPerBurst);
180 m_lastDrip = Environment.TickCount & Int32.MaxValue; 182 m_lastDrip = Util.EnvironmentTickCount();
181 } 183 }
182 184
183#endregion Constructor 185#endregion Constructor
@@ -189,7 +191,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
189 /// hierarchy. However, if any of the parents is over-booked, then 191 /// hierarchy. However, if any of the parents is over-booked, then
190 /// the modifier will be less than 1. 192 /// the modifier will be less than 1.
191 /// </summary> 193 /// </summary>
192 private double DripRateModifier() 194 protected double DripRateModifier()
193 { 195 {
194 Int64 driprate = DripRate; 196 Int64 driprate = DripRate;
195 return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest; 197 return driprate >= TotalDripRequest ? 1.0 : (double)driprate / (double)TotalDripRequest;
@@ -197,7 +199,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
197 199
198 /// <summary> 200 /// <summary>
199 /// </summary> 201 /// </summary>
200 private double BurstRateModifier() 202 protected double BurstRateModifier()
201 { 203 {
202 // for now... burst rate is always m_quantumsPerBurst (constant) 204 // for now... burst rate is always m_quantumsPerBurst (constant)
203 // larger than drip rate so the ratio of burst requests is the 205 // larger than drip rate so the ratio of burst requests is the
@@ -211,12 +213,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
211 /// </summary> 213 /// </summary>
212 public void RegisterRequest(TokenBucket child, Int64 request) 214 public void RegisterRequest(TokenBucket child, Int64 request)
213 { 215 {
214 m_children[child] = request; 216 lock (m_children)
215 // m_totalDripRequest = m_children.Values.Sum(); 217 {
218 m_children[child] = request;
219 // m_totalDripRequest = m_children.Values.Sum();
216 220
217 m_totalDripRequest = 0; 221 m_totalDripRequest = 0;
218 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) 222 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
219 m_totalDripRequest += cref.Value; 223 m_totalDripRequest += cref.Value;
224 }
220 225
221 // Pass the new values up to the parent 226 // Pass the new values up to the parent
222 if (m_parent != null) 227 if (m_parent != null)
@@ -229,12 +234,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
229 /// </summary> 234 /// </summary>
230 public void UnregisterRequest(TokenBucket child) 235 public void UnregisterRequest(TokenBucket child)
231 { 236 {
232 m_children.Remove(child); 237 lock (m_children)
233 // m_totalDripRequest = m_children.Values.Sum(); 238 {
239 m_children.Remove(child);
240 // m_totalDripRequest = m_children.Values.Sum();
234 241
235 m_totalDripRequest = 0; 242 m_totalDripRequest = 0;
236 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children) 243 foreach (KeyValuePair<TokenBucket, Int64> cref in m_children)
237 m_totalDripRequest += cref.Value; 244 m_totalDripRequest += cref.Value;
245 }
246
238 247
239 // Pass the new values up to the parent 248 // Pass the new values up to the parent
240 if (m_parent != null) 249 if (m_parent != null)
@@ -268,7 +277,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
268 /// Deposit tokens into the bucket from a child bucket that did 277 /// Deposit tokens into the bucket from a child bucket that did
269 /// not use all of its available tokens 278 /// not use all of its available tokens
270 /// </summary> 279 /// </summary>
271 private void Deposit(Int64 count) 280 protected void Deposit(Int64 count)
272 { 281 {
273 m_tokenCount += count; 282 m_tokenCount += count;
274 283
@@ -285,7 +294,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
285 /// call to Drip 294 /// call to Drip
286 /// </summary> 295 /// </summary>
287 /// <returns>True if tokens were added to the bucket, otherwise false</returns> 296 /// <returns>True if tokens were added to the bucket, otherwise false</returns>
288 private void Drip() 297 protected void Drip()
289 { 298 {
290 // This should never happen... means we are a leaf node and were created 299 // This should never happen... means we are a leaf node and were created
291 // with no drip rate... 300 // with no drip rate...
@@ -297,10 +306,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
297 306
298 // Determine the interval over which we are adding tokens, never add 307 // Determine the interval over which we are adding tokens, never add
299 // more than a single quantum of tokens 308 // more than a single quantum of tokens
300 Int32 now = Environment.TickCount & Int32.MaxValue; 309 Int32 deltaMS = Math.Min(Util.EnvironmentTickCountSubtract(m_lastDrip), m_ticksPerQuantum);
301 Int32 deltaMS = Math.Min(now - m_lastDrip, m_ticksPerQuantum); 310 m_lastDrip = Util.EnvironmentTickCount();
302
303 m_lastDrip = now;
304 311
305 // This can be 0 in the very unusual case that the timer wrapped 312 // This can be 0 in the very unusual case that the timer wrapped
306 // It can be 0 if we try add tokens at a sub-tick rate 313 // It can be 0 if we try add tokens at a sub-tick rate
@@ -310,4 +317,77 @@ namespace OpenSim.Region.ClientStack.LindenUDP
310 Deposit(deltaMS * DripRate / m_ticksPerQuantum); 317 Deposit(deltaMS * DripRate / m_ticksPerQuantum);
311 } 318 }
312 } 319 }
320
321 public class AdaptiveTokenBucket : TokenBucket
322 {
323 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
324
325 /// <summary>
326 /// The minimum rate for flow control. Minimum drip rate is one
327 /// packet per second. Open the throttle to 15 packets per second
328 /// or about 160kbps.
329 /// </summary>
330 protected const Int64 m_minimumFlow = m_minimumDripRate * 15;
331
332 // <summary>
333 // The maximum rate for flow control. Drip rate can never be
334 // greater than this.
335 // </summary>
336 protected Int64 m_maxDripRate = 0;
337 protected Int64 MaxDripRate
338 {
339 get { return (m_maxDripRate == 0 ? m_totalDripRequest : m_maxDripRate); }
340 set { m_maxDripRate = (value == 0 ? 0 : Math.Max(value,m_minimumFlow)); }
341 }
342
343 private bool m_enabled = false;
344
345 // <summary>
346 //
347 // </summary>
348 public virtual Int64 AdjustedDripRate
349 {
350 get { return m_dripRate; }
351 set {
352 m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value,m_minimumFlow,MaxDripRate);
353 m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst);
354 if (m_parent != null)
355 m_parent.RegisterRequest(this,m_dripRate);
356 }
357 }
358
359 // <summary>
360 //
361 // </summary>
362 public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate, bool enabled) : base(parent,maxDripRate)
363 {
364 m_enabled = enabled;
365
366 if (m_enabled)
367 {
368 m_log.WarnFormat("[TOKENBUCKET] Adaptive throttle enabled");
369 MaxDripRate = maxDripRate;
370 AdjustedDripRate = m_minimumFlow;
371 }
372 }
373
374 // <summary>
375 //
376 // </summary>
377 public void ExpirePackets(Int32 count)
378 {
379 // m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count);
380 if (m_enabled)
381 AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count));
382 }
383
384 // <summary>
385 //
386 // </summary>
387 public void AcknowledgePackets(Int32 count)
388 {
389 if (m_enabled)
390 AdjustedDripRate = AdjustedDripRate + count;
391 }
392 }
313} 393}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
index d195110..793aefe 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
@@ -65,7 +65,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
65 /// <summary>Holds packets that need to be added to the unacknowledged list</summary> 65 /// <summary>Holds packets that need to be added to the unacknowledged list</summary>
66 private LocklessQueue<OutgoingPacket> m_pendingAdds = new LocklessQueue<OutgoingPacket>(); 66 private LocklessQueue<OutgoingPacket> m_pendingAdds = new LocklessQueue<OutgoingPacket>();
67 /// <summary>Holds information about pending acknowledgements</summary> 67 /// <summary>Holds information about pending acknowledgements</summary>
68 private LocklessQueue<PendingAck> m_pendingRemoves = new LocklessQueue<PendingAck>(); 68 private LocklessQueue<PendingAck> m_pendingAcknowledgements = new LocklessQueue<PendingAck>();
69 /// <summary>Holds information about pending removals</summary>
70 private LocklessQueue<uint> m_pendingRemoves = new LocklessQueue<uint>();
69 71
70 /// <summary> 72 /// <summary>
71 /// Add an unacked packet to the collection 73 /// Add an unacked packet to the collection
@@ -83,15 +85,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
83 85
84 /// <summary> 86 /// <summary>
85 /// Marks a packet as acknowledged 87 /// Marks a packet as acknowledged
88 /// This method is used when an acknowledgement is received from the network for a previously
89 /// sent packet. Effects of removal this way are to update unacked byte count, adjust RTT
90 /// and increase throttle to the coresponding client.
86 /// </summary> 91 /// </summary>
87 /// <param name="sequenceNumber">Sequence number of the packet to 92 /// <param name="sequenceNumber">Sequence number of the packet to
88 /// acknowledge</param> 93 /// acknowledge</param>
89 /// <param name="currentTime">Current value of Environment.TickCount</param> 94 /// <param name="currentTime">Current value of Environment.TickCount</param>
90 /// <remarks>This does not immediately acknowledge the packet, it only 95 /// <remarks>This does not immediately acknowledge the packet, it only
91 /// queues the ack so it can be handled in a thread-safe way later</remarks> 96 /// queues the ack so it can be handled in a thread-safe way later</remarks>
92 public void Remove(uint sequenceNumber, int currentTime, bool fromResend) 97 public void Acknowledge(uint sequenceNumber, int currentTime, bool fromResend)
93 { 98 {
94 m_pendingRemoves.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend)); 99 m_pendingAcknowledgements.Enqueue(new PendingAck(sequenceNumber, currentTime, fromResend));
100 }
101
102 /// <summary>
103 /// Marks a packet as no longer needing acknowledgement without a received acknowledgement.
104 /// This method is called when a packet expires and we no longer need an acknowledgement.
105 /// When some reliable packet types expire, they are handled in a way other than simply
106 /// resending them. The only effect of removal this way is to update unacked byte count.
107 /// </summary>
108 /// <param name="sequenceNumber">Sequence number of the packet to
109 /// acknowledge</param>
110 /// <remarks>The does not immediately remove the packet, it only queues the removal
111 /// so it can be handled in a thread safe way later</remarks>
112 public void Remove(uint sequenceNumber)
113 {
114 m_pendingRemoves.Enqueue(sequenceNumber);
95 } 115 }
96 116
97 /// <summary> 117 /// <summary>
@@ -130,6 +150,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
130 // is actually sent out again 150 // is actually sent out again
131 packet.TickCount = 0; 151 packet.TickCount = 0;
132 152
153 // As with other network applications, assume that an expired packet is
154 // an indication of some network problem, slow transmission
155 packet.Client.FlowThrottle.ExpirePackets(1);
156
133 expiredPackets.Add(packet); 157 expiredPackets.Add(packet);
134 } 158 }
135 } 159 }
@@ -147,29 +171,49 @@ namespace OpenSim.Region.ClientStack.LindenUDP
147 m_packets[pendingAdd.SequenceNumber] = pendingAdd; 171 m_packets[pendingAdd.SequenceNumber] = pendingAdd;
148 172
149 // Process all the pending removes, including updating statistics and round-trip times 173 // Process all the pending removes, including updating statistics and round-trip times
150 PendingAck pendingRemove; 174 PendingAck pendingAcknowledgement;
151 OutgoingPacket ackedPacket; 175 while (m_pendingAcknowledgements.TryDequeue(out pendingAcknowledgement))
152 while (m_pendingRemoves.TryDequeue(out pendingRemove))
153 { 176 {
154 if (m_packets.TryGetValue(pendingRemove.SequenceNumber, out ackedPacket)) 177 OutgoingPacket ackedPacket;
178 if (m_packets.TryGetValue(pendingAcknowledgement.SequenceNumber, out ackedPacket))
155 { 179 {
156 if (ackedPacket != null) 180 if (ackedPacket != null)
157 { 181 {
158 m_packets.Remove(pendingRemove.SequenceNumber); 182 m_packets.Remove(pendingAcknowledgement.SequenceNumber);
183
184 // As with other network applications, assume that an acknowledged packet is an
185 // indication that the network can handle a little more load, speed up the transmission
186 ackedPacket.Client.FlowThrottle.AcknowledgePackets(ackedPacket.Buffer.DataLength);
159 187
160 // Update stats 188 // Update stats
161 Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); 189 Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength);
162 190
163 if (!pendingRemove.FromResend) 191 if (!pendingAcknowledgement.FromResend)
164 { 192 {
165 // Calculate the round-trip time for this packet and its ACK 193 // Calculate the round-trip time for this packet and its ACK
166 int rtt = pendingRemove.RemoveTime - ackedPacket.TickCount; 194 int rtt = pendingAcknowledgement.RemoveTime - ackedPacket.TickCount;
167 if (rtt > 0) 195 if (rtt > 0)
168 ackedPacket.Client.UpdateRoundTrip(rtt); 196 ackedPacket.Client.UpdateRoundTrip(rtt);
169 } 197 }
170 } 198 }
171 } 199 }
172 } 200 }
201
202 uint pendingRemove;
203 while(m_pendingRemoves.TryDequeue(out pendingRemove))
204 {
205 OutgoingPacket removedPacket;
206 if (m_packets.TryGetValue(pendingRemove, out removedPacket))
207 {
208 if (removedPacket != null)
209 {
210 m_packets.Remove(pendingRemove);
211
212 // Update stats
213 Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength);
214 }
215 }
216 }
173 } 217 }
174 } 218 }
175} \ No newline at end of file 219}