aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs85
1 files changed, 64 insertions, 21 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index 871e8e8..e7707a9 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -59,9 +59,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
59 /// </summary> 59 /// </summary>
60 public sealed class LLUDPClient 60 public sealed class LLUDPClient
61 { 61 {
62 // FIXME: Make this a config setting
63 /// <summary>Percentage of the task throttle category that is allocated to avatar and prim
64 /// state updates</summary>
65 const float STATE_TASK_PERCENTAGE = 0.8f;
66
62 /// <summary>The number of packet categories to throttle on. If a throttle category is added 67 /// <summary>The number of packet categories to throttle on. If a throttle category is added
63 /// or removed, this number must also change</summary> 68 /// or removed, this number must also change</summary>
64 const int THROTTLE_CATEGORY_COUNT = 7; 69 const int THROTTLE_CATEGORY_COUNT = 8;
65 70
66 /// <summary>Fired when updated networking stats are produced for this client</summary> 71 /// <summary>Fired when updated networking stats are produced for this client</summary>
67 public event PacketStats OnPacketStats; 72 public event PacketStats OnPacketStats;
@@ -80,10 +85,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
80 /// <summary>Packets we have sent that need to be ACKed by the client</summary> 85 /// <summary>Packets we have sent that need to be ACKed by the client</summary>
81 public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); 86 public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection();
82 /// <summary>ACKs that are queued up, waiting to be sent to the client</summary> 87 /// <summary>ACKs that are queued up, waiting to be sent to the client</summary>
83 public readonly LocklessQueue<uint> PendingAcks = new LocklessQueue<uint>(); 88 public readonly OpenSim.Framework.LocklessQueue<uint> PendingAcks = new OpenSim.Framework.LocklessQueue<uint>();
84 89
85 /// <summary>Reference to the IClientAPI for this client</summary>
86 public LLClientView ClientAPI;
87 /// <summary>Current packet sequence number</summary> 90 /// <summary>Current packet sequence number</summary>
88 public int CurrentSequence; 91 public int CurrentSequence;
89 /// <summary>Current ping sequence number</summary> 92 /// <summary>Current ping sequence number</summary>
@@ -129,7 +132,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
129 /// <summary>Throttle rate defaults and limits</summary> 132 /// <summary>Throttle rate defaults and limits</summary>
130 private readonly ThrottleRates defaultThrottleRates; 133 private readonly ThrottleRates defaultThrottleRates;
131 /// <summary>Outgoing queues for throttled packets</summary> 134 /// <summary>Outgoing queues for throttled packets</summary>
132 private readonly LocklessQueue<OutgoingPacket>[] packetOutboxes = new LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT]; 135 private readonly OpenSim.Framework.LocklessQueue<OutgoingPacket>[] packetOutboxes = new OpenSim.Framework.LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT];
133 /// <summary>A container that can hold one packet for each outbox, used to store 136 /// <summary>A container that can hold one packet for each outbox, used to store
134 /// dequeued packets that are being held for throttling</summary> 137 /// dequeued packets that are being held for throttling</summary>
135 private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; 138 private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
@@ -158,7 +161,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
158 defaultThrottleRates = rates; 161 defaultThrottleRates = rates;
159 162
160 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) 163 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
161 packetOutboxes[i] = new LocklessQueue<OutgoingPacket>(); 164 packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
162 165
163 throttle = new TokenBucket(parentThrottle, 0, 0); 166 throttle = new TokenBucket(parentThrottle, 0, 0);
164 throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; 167 throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
@@ -166,9 +169,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
166 throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land); 169 throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land);
167 throttleCategories[(int)ThrottleOutPacketType.Wind] = new TokenBucket(throttle, rates.WindLimit, rates.Wind); 170 throttleCategories[(int)ThrottleOutPacketType.Wind] = new TokenBucket(throttle, rates.WindLimit, rates.Wind);
168 throttleCategories[(int)ThrottleOutPacketType.Cloud] = new TokenBucket(throttle, rates.CloudLimit, rates.Cloud); 171 throttleCategories[(int)ThrottleOutPacketType.Cloud] = new TokenBucket(throttle, rates.CloudLimit, rates.Cloud);
169 throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit, rates.Task);
170 throttleCategories[(int)ThrottleOutPacketType.Texture] = new TokenBucket(throttle, rates.TextureLimit, rates.Texture); 172 throttleCategories[(int)ThrottleOutPacketType.Texture] = new TokenBucket(throttle, rates.TextureLimit, rates.Texture);
171 throttleCategories[(int)ThrottleOutPacketType.Asset] = new TokenBucket(throttle, rates.AssetLimit, rates.Asset); 173 throttleCategories[(int)ThrottleOutPacketType.Asset] = new TokenBucket(throttle, rates.AssetLimit, rates.Asset);
174 // State and Transaction are actually sub-categories of the LLUDP generic "Task" category
175 TokenBucket stateBucket = new TokenBucket(throttle, (int)((float)rates.TaskLimit * STATE_TASK_PERCENTAGE), (int)((float)rates.Task * STATE_TASK_PERCENTAGE));
176 throttleCategories[(int)ThrottleOutPacketType.State] = stateBucket;
177 throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit - stateBucket.MaxBurst, rates.Task - stateBucket.DripRate);
172 178
173 // Set the granularity variable used for retransmission calculations to 179 // Set the granularity variable used for retransmission calculations to
174 // the measured resolution of Environment.TickCount 180 // the measured resolution of Environment.TickCount
@@ -176,6 +182,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
176 182
177 // Default the retransmission timeout to three seconds 183 // Default the retransmission timeout to three seconds
178 RTO = 3000; 184 RTO = 3000;
185
186 // Initialize this to a sane value to prevent early disconnects
187 TickLastPacketReceived = Environment.TickCount;
179 } 188 }
180 189
181 /// <summary> 190 /// <summary>
@@ -183,8 +192,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
183 /// </summary> 192 /// </summary>
184 public void Shutdown() 193 public void Shutdown()
185 { 194 {
186 // TODO: Do we need to invalidate the circuit?
187 IsConnected = false; 195 IsConnected = false;
196 NeedAcks.Clear();
197 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
198 {
199 packetOutboxes[i].Clear();
200 nextPackets[i] = null;
201 }
202 OnPacketStats = null;
203 OnQueueEmpty = null;
188 } 204 }
189 205
190 /// <summary> 206 /// <summary>
@@ -204,7 +220,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
204 info.landThrottle = throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; 220 info.landThrottle = throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
205 info.windThrottle = throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; 221 info.windThrottle = throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
206 info.cloudThrottle = throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; 222 info.cloudThrottle = throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
207 info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; 223 info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.State].DripRate + throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
208 info.assetThrottle = throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; 224 info.assetThrottle = throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
209 info.textureThrottle = throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; 225 info.textureThrottle = throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
210 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + 226 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle +
@@ -301,7 +317,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
301 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; 317 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4;
302 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; 318 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4;
303 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; 319 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4;
304 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Task].DripRate), 0, data, i, 4); i += 4; 320 Buffer.BlockCopy(Utils.FloatToBytes((float)(throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) +
321 throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4;
305 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; 322 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4;
306 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; 323 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4;
307 324
@@ -310,12 +327,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
310 327
311 public void SetThrottle(ThrottleOutPacketType category, int rate) 328 public void SetThrottle(ThrottleOutPacketType category, int rate)
312 { 329 {
313 int i = (int)category; 330 if (category == ThrottleOutPacketType.Task)
314 if (i >= 0 && i < throttleCategories.Length)
315 { 331 {
316 TokenBucket bucket = throttleCategories[(int)category]; 332 TokenBucket stateBucket = throttleCategories[(int)ThrottleOutPacketType.State];
317 bucket.MaxBurst = rate; 333 TokenBucket taskBucket = throttleCategories[(int)ThrottleOutPacketType.Task];
318 bucket.DripRate = rate; 334
335 stateBucket.MaxBurst = (int)((float)rate * STATE_TASK_PERCENTAGE);
336 stateBucket.DripRate = (int)((float)rate * STATE_TASK_PERCENTAGE);
337
338 taskBucket.MaxBurst = rate - stateBucket.MaxBurst;
339 taskBucket.DripRate = rate - stateBucket.DripRate;
340 }
341 else
342 {
343 int i = (int)category;
344 if (i >= 0 && i < throttleCategories.Length)
345 {
346 TokenBucket bucket = throttleCategories[(int)category];
347 bucket.MaxBurst = rate;
348 bucket.DripRate = rate;
349 }
319 } 350 }
320 } 351 }
321 352
@@ -325,7 +356,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
325 356
326 if (category >= 0 && category < packetOutboxes.Length) 357 if (category >= 0 && category < packetOutboxes.Length)
327 { 358 {
328 LocklessQueue<OutgoingPacket> queue = packetOutboxes[category]; 359 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = packetOutboxes[category];
329 TokenBucket bucket = throttleCategories[category]; 360 TokenBucket bucket = throttleCategories[category];
330 361
331 if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength)) 362 if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength))
@@ -357,7 +388,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
357 public bool DequeueOutgoing() 388 public bool DequeueOutgoing()
358 { 389 {
359 OutgoingPacket packet; 390 OutgoingPacket packet;
360 LocklessQueue<OutgoingPacket> queue; 391 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue;
361 TokenBucket bucket; 392 TokenBucket bucket;
362 bool packetSent = false; 393 bool packetSent = false;
363 394
@@ -400,13 +431,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
400 nextPackets[i] = packet; 431 nextPackets[i] = packet;
401 nextPacketLengths[i] = packet.Buffer.DataLength; 432 nextPacketLengths[i] = packet.Buffer.DataLength;
402 } 433 }
434
435 // If the queue is empty after this dequeue, fire the queue
436 // empty callback now so it has a chance to fill before we
437 // get back here
438 if (queue.Count == 0)
439 FireQueueEmpty(i);
403 } 440 }
404 else 441 else
405 { 442 {
406 // No packets in this queue. Fire the queue empty callback 443 // No packets in this queue. Fire the queue empty callback
407 QueueEmpty callback = OnQueueEmpty; 444 // if it has not been called recently
408 if (callback != null) 445 FireQueueEmpty(i);
409 callback((ThrottleOutPacketType)i);
410 } 446 }
411 } 447 }
412 } 448 }
@@ -435,8 +471,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
435 471
436 // Always round retransmission timeout up to two seconds 472 // Always round retransmission timeout up to two seconds
437 RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR))); 473 RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR)));
438 //Logger.Debug("Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " + 474 //m_log.Debug("[LLUDPCLIENT]: Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " +
439 // RTTVAR + " based on new RTT of " + r + "ms"); 475 // RTTVAR + " based on new RTT of " + r + "ms");
440 } 476 }
477
478 private void FireQueueEmpty(int queueIndex)
479 {
480 QueueEmpty callback = OnQueueEmpty;
481 if (callback != null)
482 Util.FireAndForget(delegate(object o) { callback((ThrottleOutPacketType)(int)o); }, queueIndex);
483 }
441 } 484 }
442} 485}