diff options
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.cs | 192 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 33 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 68 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs | 7 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs | 131 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs | 66 |
7 files changed, 458 insertions, 171 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; | |||
34 | using OpenSim.Framework.Client; | 34 | using OpenSim.Framework.Client; |
35 | using log4net; | 35 | using log4net; |
36 | 36 | ||
37 | namespace OpenSim.Region.ClientStack.LindenUDP | 37 | namespace 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..32a075a 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); |
@@ -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..103ec66 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,7 +181,7 @@ 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.TotalLimit); |
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.TotalLimit); |
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 |
@@ -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 | } |
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 | ||
32 | namespace OpenSim.Region.ClientStack.LindenUDP | 32 | namespace 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/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs index 07b0a1d..677d3d1 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs | |||
@@ -29,6 +29,8 @@ using System; | |||
29 | using System.Collections; | 29 | using System.Collections; |
30 | using System.Collections.Generic; | 30 | using System.Collections.Generic; |
31 | using System.Reflection; | 31 | using System.Reflection; |
32 | using OpenSim.Framework; | ||
33 | |||
32 | using log4net; | 34 | using log4net; |
33 | 35 | ||
34 | namespace OpenSim.Region.ClientStack.LindenUDP | 36 | namespace 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,66 @@ 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 | // <summary> | ||
344 | // | ||
345 | // </summary> | ||
346 | public virtual Int64 AdjustedDripRate | ||
347 | { | ||
348 | get { return m_dripRate; } | ||
349 | set { | ||
350 | m_dripRate = OpenSim.Framework.Util.Clamp<Int64>(value,m_minimumFlow,MaxDripRate); | ||
351 | m_burstRate = (Int64)((double)m_dripRate * m_quantumsPerBurst); | ||
352 | if (m_parent != null) | ||
353 | m_parent.RegisterRequest(this,m_dripRate); | ||
354 | } | ||
355 | } | ||
356 | |||
357 | // <summary> | ||
358 | // | ||
359 | // </summary> | ||
360 | public AdaptiveTokenBucket(TokenBucket parent, Int64 maxDripRate) : base(parent,m_minimumFlow) | ||
361 | { | ||
362 | MaxDripRate = maxDripRate; | ||
363 | } | ||
364 | |||
365 | // <summary> | ||
366 | // | ||
367 | // </summary> | ||
368 | public void ExpirePackets(Int32 count) | ||
369 | { | ||
370 | // m_log.WarnFormat("[ADAPTIVEBUCKET] drop {0} by {1} expired packets",AdjustedDripRate,count); | ||
371 | AdjustedDripRate = (Int64) (AdjustedDripRate / Math.Pow(2,count)); | ||
372 | } | ||
373 | |||
374 | // <summary> | ||
375 | // | ||
376 | // </summary> | ||
377 | public void AcknowledgePackets(Int32 count) | ||
378 | { | ||
379 | AdjustedDripRate = AdjustedDripRate + count; | ||
380 | } | ||
381 | } | ||
313 | } | 382 | } |
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 | } |