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.cs140
1 files changed, 85 insertions, 55 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index 4eee6b6..71f4c47 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -33,6 +33,8 @@ using OpenSim.Framework;
33using OpenMetaverse; 33using OpenMetaverse;
34using OpenMetaverse.Packets; 34using OpenMetaverse.Packets;
35 35
36using TokenBucket = OpenSim.Region.ClientStack.LindenUDP.TokenBucket;
37
36namespace OpenSim.Region.ClientStack.LindenUDP 38namespace OpenSim.Region.ClientStack.LindenUDP
37{ 39{
38 #region Delegates 40 #region Delegates
@@ -98,12 +100,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
98 /// <summary>True when this connection is alive, otherwise false</summary> 100 /// <summary>True when this connection is alive, otherwise false</summary>
99 public bool IsConnected = true; 101 public bool IsConnected = true;
100 /// <summary>True when this connection is paused, otherwise false</summary> 102 /// <summary>True when this connection is paused, otherwise false</summary>
101 public bool IsPaused = true; 103 public bool IsPaused;
102 /// <summary>Environment.TickCount when the last packet was received for this client</summary> 104 /// <summary>Environment.TickCount when the last packet was received for this client</summary>
103 public int TickLastPacketReceived; 105 public int TickLastPacketReceived;
104 106
105 /// <summary>Timer granularity. This is set to the measured resolution of Environment.TickCount</summary>
106 public readonly float G;
107 /// <summary>Smoothed round-trip time. A smoothed average of the round-trip time for sending a 107 /// <summary>Smoothed round-trip time. A smoothed average of the round-trip time for sending a
108 /// reliable packet to the client and receiving an ACK</summary> 108 /// reliable packet to the client and receiving an ACK</summary>
109 public float SRTT; 109 public float SRTT;
@@ -163,26 +163,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
163 CircuitCode = circuitCode; 163 CircuitCode = circuitCode;
164 m_udpServer = server; 164 m_udpServer = server;
165 m_defaultThrottleRates = rates; 165 m_defaultThrottleRates = rates;
166 // Create a token bucket throttle for this client that has the scene token bucket as a parent
166 m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); 167 m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total);
168 // Create an array of token buckets for this clients different throttle categories
167 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; 169 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
168 170
169 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) 171 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
170 { 172 {
171 ThrottleOutPacketType type = (ThrottleOutPacketType)i; 173 ThrottleOutPacketType type = (ThrottleOutPacketType)i;
172 174
175 // Initialize the packet outboxes, where packets sit while they are waiting for tokens
173 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); 176 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
177 // Initialize the token buckets that control the throttling for each category
174 m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); 178 m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type));
175 } 179 }
176 180
177 // Set the granularity variable used for retransmission calculations to
178 // the measured resolution of Environment.TickCount
179 G = server.TickCountResolution;
180
181 // Default the retransmission timeout to three seconds 181 // Default the retransmission timeout to three seconds
182 RTO = 3000; 182 RTO = 3000;
183 183
184 // Initialize this to a sane value to prevent early disconnects 184 // Initialize this to a sane value to prevent early disconnects
185 TickLastPacketReceived = Environment.TickCount; 185 TickLastPacketReceived = Environment.TickCount & Int32.MaxValue;
186 } 186 }
187 187
188 /// <summary> 188 /// <summary>
@@ -191,7 +191,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
191 public void Shutdown() 191 public void Shutdown()
192 { 192 {
193 IsConnected = false; 193 IsConnected = false;
194 NeedAcks.Clear();
195 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) 194 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
196 { 195 {
197 m_packetOutboxes[i].Clear(); 196 m_packetOutboxes[i].Clear();
@@ -293,36 +292,59 @@ namespace OpenSim.Region.ClientStack.LindenUDP
293 int state = (int)((float)task * STATE_TASK_PERCENTAGE); 292 int state = (int)((float)task * STATE_TASK_PERCENTAGE);
294 task -= state; 293 task -= state;
295 294
296 int ceiling = Int32.MaxValue; 295 // Make sure none of the throttles are set below our packet MTU,
297 if (m_defaultThrottleRates.Total != 0) 296 // otherwise a throttle could become permanently clogged
298 { 297 resend = Math.Max(resend, LLUDPServer.MTU);
299 ceiling = m_defaultThrottleRates.Total; 298 land = Math.Max(land, LLUDPServer.MTU);
300 if (ceiling < Packet.MTU) ceiling = Packet.MTU; 299 wind = Math.Max(wind, LLUDPServer.MTU);
301 } 300 cloud = Math.Max(cloud, LLUDPServer.MTU);
302 301 task = Math.Max(task, LLUDPServer.MTU);
303 resend = Utils.Clamp(resend, Packet.MTU, ceiling); 302 texture = Math.Max(texture, LLUDPServer.MTU);
304 land = Utils.Clamp(land, Packet.MTU, ceiling); 303 asset = Math.Max(asset, LLUDPServer.MTU);
305 wind = Utils.Clamp(wind, Packet.MTU, ceiling); 304 state = Math.Max(state, LLUDPServer.MTU);
306 cloud = Utils.Clamp(cloud, Packet.MTU, ceiling);
307 task = Utils.Clamp(task, Packet.MTU, ceiling);
308 texture = Utils.Clamp(texture, Packet.MTU, ceiling);
309 asset = Utils.Clamp(asset, Packet.MTU, ceiling);
310 state = Utils.Clamp(state, Packet.MTU, ceiling);
311 305
312 int total = resend + land + wind + cloud + task + texture + asset + state; 306 int total = resend + land + wind + cloud + task + texture + asset + state;
313 int taskTotal = task + state;
314 307
315 m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}", 308 m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}",
316 AgentID, resend, land, wind, cloud, task, texture, asset, state, total); 309 AgentID, resend, land, wind, cloud, task, texture, asset, state, total);
317 310
318 SetThrottle(ThrottleOutPacketType.Resend, resend, resend); 311 // Update the token buckets with new throttle values
319 SetThrottle(ThrottleOutPacketType.Land, land, land); 312 TokenBucket bucket;
320 SetThrottle(ThrottleOutPacketType.Wind, wind, wind); 313
321 SetThrottle(ThrottleOutPacketType.Cloud, cloud, cloud); 314 bucket = m_throttle;
322 SetThrottle(ThrottleOutPacketType.Task, task, taskTotal); 315 bucket.MaxBurst = total;
323 SetThrottle(ThrottleOutPacketType.Texture, texture, texture); 316
324 SetThrottle(ThrottleOutPacketType.Asset, asset, asset); 317 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
325 SetThrottle(ThrottleOutPacketType.State, state, taskTotal); 318 bucket.DripRate = resend;
319 bucket.MaxBurst = resend;
320
321 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land];
322 bucket.DripRate = land;
323 bucket.MaxBurst = land;
324
325 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind];
326 bucket.DripRate = wind;
327 bucket.MaxBurst = wind;
328
329 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud];
330 bucket.DripRate = cloud;
331 bucket.MaxBurst = cloud;
332
333 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset];
334 bucket.DripRate = asset;
335 bucket.MaxBurst = asset;
336
337 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task];
338 bucket.DripRate = task + state;
339 bucket.MaxBurst = task + state;
340
341 bucket = m_throttleCategories[(int)ThrottleOutPacketType.State];
342 bucket.DripRate = state;
343 bucket.MaxBurst = state;
344
345 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture];
346 bucket.DripRate = texture;
347 bucket.MaxBurst = texture;
326 } 348 }
327 349
328 public byte[] GetThrottlesPacked() 350 public byte[] GetThrottlesPacked()
@@ -342,17 +364,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
342 return data; 364 return data;
343 } 365 }
344 366
345 public void SetThrottle(ThrottleOutPacketType category, int rate, int maxBurst)
346 {
347 int i = (int)category;
348 if (i >= 0 && i < m_throttleCategories.Length)
349 {
350 TokenBucket bucket = m_throttleCategories[(int)category];
351 bucket.DripRate = rate;
352 bucket.MaxBurst = maxBurst;
353 }
354 }
355
356 public bool EnqueueOutgoing(OutgoingPacket packet) 367 public bool EnqueueOutgoing(OutgoingPacket packet)
357 { 368 {
358 int category = (int)packet.Category; 369 int category = (int)packet.Category;
@@ -395,9 +406,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
395 TokenBucket bucket; 406 TokenBucket bucket;
396 bool packetSent = false; 407 bool packetSent = false;
397 408
409 //string queueDebugOutput = String.Empty; // Serious debug business
410
398 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) 411 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
399 { 412 {
400 bucket = m_throttleCategories[i]; 413 bucket = m_throttleCategories[i];
414 //queueDebugOutput += m_packetOutboxes[i].Count + " "; // Serious debug business
401 415
402 if (m_nextPackets[i] != null) 416 if (m_nextPackets[i] != null)
403 { 417 {
@@ -449,6 +463,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
449 } 463 }
450 } 464 }
451 465
466 //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business
452 return packetSent; 467 return packetSent;
453 } 468 }
454 469
@@ -479,8 +494,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
479 SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r; 494 SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r;
480 } 495 }
481 496
482 // Always round retransmission timeout up to two seconds 497 RTO = (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR));
483 RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR))); 498
499 // Clamp the retransmission timeout to manageable values
500 RTO = Utils.Clamp(RTO, 3000, 10000);
501
484 //m_log.Debug("[LLUDPCLIENT]: Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " + 502 //m_log.Debug("[LLUDPCLIENT]: Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " +
485 // RTTVAR + " based on new RTT of " + r + "ms"); 503 // RTTVAR + " based on new RTT of " + r + "ms");
486 } 504 }
@@ -493,8 +511,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
493 /// for</param> 511 /// for</param>
494 private void BeginFireQueueEmpty(int throttleIndex) 512 private void BeginFireQueueEmpty(int throttleIndex)
495 { 513 {
496 if (!m_onQueueEmptyRunning[throttleIndex]) 514 // Unknown is -1 and Resend is 0. Make sure we are only firing the
497 Util.FireAndForget(FireQueueEmpty, throttleIndex); 515 // callback for categories other than those
516 if (throttleIndex > 0)
517 {
518 if (!m_onQueueEmptyRunning[throttleIndex])
519 {
520 m_onQueueEmptyRunning[throttleIndex] = true;
521 Util.FireAndForget(FireQueueEmpty, throttleIndex);
522 }
523 }
498 } 524 }
499 525
500 /// <summary> 526 /// <summary>
@@ -509,16 +535,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
509 ThrottleOutPacketType type = (ThrottleOutPacketType)i; 535 ThrottleOutPacketType type = (ThrottleOutPacketType)i;
510 QueueEmpty callback = OnQueueEmpty; 536 QueueEmpty callback = OnQueueEmpty;
511 537
538 int start = Environment.TickCount;
539
512 if (callback != null) 540 if (callback != null)
513 { 541 {
514 if (!m_onQueueEmptyRunning[i]) 542 try { callback(type); }
515 { 543 catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); }
516 m_onQueueEmptyRunning[i] = true;
517 try { callback(type); }
518 catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); }
519 m_onQueueEmptyRunning[i] = false;
520 }
521 } 544 }
545
546 // Make sure all queue empty calls take at least a measurable amount of time,
547 // otherwise we'll peg a CPU trying to fire these too fast
548 if (Environment.TickCount == start)
549 System.Threading.Thread.Sleep((int)m_udpServer.TickCountResolution);
550
551 m_onQueueEmptyRunning[i] = false;
522 } 552 }
523 } 553 }
524} 554}