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.cs219
1 files changed, 148 insertions, 71 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index 4eee6b6..a9bc7d2 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
@@ -48,11 +50,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
48 /// are waiting on ACKs for</param> 50 /// are waiting on ACKs for</param>
49 public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes); 51 public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
50 /// <summary> 52 /// <summary>
51 /// Fired when the queue for a packet category is empty. This event can be 53 /// Fired when the queue for one or more packet categories is empty. This
52 /// hooked to put more data on the empty queue 54 /// event can be hooked to put more data on the empty queues
53 /// </summary> 55 /// </summary>
54 /// <param name="category">Category of the packet queue that is empty</param> 56 /// <param name="category">Categories of the packet queues that are empty</param>
55 public delegate void QueueEmpty(ThrottleOutPacketType category); 57 public delegate void QueueEmpty(ThrottleOutPacketTypeFlags categories);
56 58
57 #endregion Delegates 59 #endregion Delegates
58 60
@@ -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;
@@ -128,6 +128,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
128 private int m_packetsReceivedReported; 128 private int m_packetsReceivedReported;
129 /// <summary>Total number of sent packets that we have reported to the OnPacketStats event(s)</summary> 129 /// <summary>Total number of sent packets that we have reported to the OnPacketStats event(s)</summary>
130 private int m_packetsSentReported; 130 private int m_packetsSentReported;
131 /// <summary>Holds the Environment.TickCount value of when the next OnQueueEmpty can be fired</summary>
132 private int m_nextOnQueueEmpty = 1;
131 133
132 /// <summary>Throttle bucket for this agent's connection</summary> 134 /// <summary>Throttle bucket for this agent's connection</summary>
133 private readonly TokenBucket m_throttle; 135 private readonly TokenBucket m_throttle;
@@ -140,9 +142,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
140 /// <summary>A container that can hold one packet for each outbox, used to store 142 /// <summary>A container that can hold one packet for each outbox, used to store
141 /// dequeued packets that are being held for throttling</summary> 143 /// dequeued packets that are being held for throttling</summary>
142 private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; 144 private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
143 /// <summary>Flags to prevent queue empty callbacks from stacking up on
144 /// top of each other</summary>
145 private readonly bool[] m_onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT];
146 /// <summary>A reference to the LLUDPServer that is managing this client</summary> 145 /// <summary>A reference to the LLUDPServer that is managing this client</summary>
147 private readonly LLUDPServer m_udpServer; 146 private readonly LLUDPServer m_udpServer;
148 147
@@ -163,26 +162,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
163 CircuitCode = circuitCode; 162 CircuitCode = circuitCode;
164 m_udpServer = server; 163 m_udpServer = server;
165 m_defaultThrottleRates = rates; 164 m_defaultThrottleRates = rates;
165 // 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); 166 m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total);
167 // Create an array of token buckets for this clients different throttle categories
167 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; 168 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
168 169
169 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) 170 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
170 { 171 {
171 ThrottleOutPacketType type = (ThrottleOutPacketType)i; 172 ThrottleOutPacketType type = (ThrottleOutPacketType)i;
172 173
174 // Initialize the packet outboxes, where packets sit while they are waiting for tokens
173 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>(); 175 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
176 // 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)); 177 m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type));
175 } 178 }
176 179
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 180 // Default the retransmission timeout to three seconds
182 RTO = 3000; 181 RTO = 3000;
183 182
184 // Initialize this to a sane value to prevent early disconnects 183 // Initialize this to a sane value to prevent early disconnects
185 TickLastPacketReceived = Environment.TickCount; 184 TickLastPacketReceived = Environment.TickCount & Int32.MaxValue;
186 } 185 }
187 186
188 /// <summary> 187 /// <summary>
@@ -191,7 +190,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
191 public void Shutdown() 190 public void Shutdown()
192 { 191 {
193 IsConnected = false; 192 IsConnected = false;
194 NeedAcks.Clear();
195 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) 193 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
196 { 194 {
197 m_packetOutboxes[i].Clear(); 195 m_packetOutboxes[i].Clear();
@@ -293,36 +291,59 @@ namespace OpenSim.Region.ClientStack.LindenUDP
293 int state = (int)((float)task * STATE_TASK_PERCENTAGE); 291 int state = (int)((float)task * STATE_TASK_PERCENTAGE);
294 task -= state; 292 task -= state;
295 293
296 int ceiling = Int32.MaxValue; 294 // Make sure none of the throttles are set below our packet MTU,
297 if (m_defaultThrottleRates.Total != 0) 295 // otherwise a throttle could become permanently clogged
298 { 296 resend = Math.Max(resend, LLUDPServer.MTU);
299 ceiling = m_defaultThrottleRates.Total; 297 land = Math.Max(land, LLUDPServer.MTU);
300 if (ceiling < Packet.MTU) ceiling = Packet.MTU; 298 wind = Math.Max(wind, LLUDPServer.MTU);
301 } 299 cloud = Math.Max(cloud, LLUDPServer.MTU);
302 300 task = Math.Max(task, LLUDPServer.MTU);
303 resend = Utils.Clamp(resend, Packet.MTU, ceiling); 301 texture = Math.Max(texture, LLUDPServer.MTU);
304 land = Utils.Clamp(land, Packet.MTU, ceiling); 302 asset = Math.Max(asset, LLUDPServer.MTU);
305 wind = Utils.Clamp(wind, Packet.MTU, ceiling); 303 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 304
312 int total = resend + land + wind + cloud + task + texture + asset + state; 305 int total = resend + land + wind + cloud + task + texture + asset + state;
313 int taskTotal = task + state;
314 306
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}", 307 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); 308 AgentID, resend, land, wind, cloud, task, texture, asset, state, total);
317 309
318 SetThrottle(ThrottleOutPacketType.Resend, resend, resend); 310 // Update the token buckets with new throttle values
319 SetThrottle(ThrottleOutPacketType.Land, land, land); 311 TokenBucket bucket;
320 SetThrottle(ThrottleOutPacketType.Wind, wind, wind); 312
321 SetThrottle(ThrottleOutPacketType.Cloud, cloud, cloud); 313 bucket = m_throttle;
322 SetThrottle(ThrottleOutPacketType.Task, task, taskTotal); 314 bucket.MaxBurst = total;
323 SetThrottle(ThrottleOutPacketType.Texture, texture, texture); 315
324 SetThrottle(ThrottleOutPacketType.Asset, asset, asset); 316 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Resend];
325 SetThrottle(ThrottleOutPacketType.State, state, taskTotal); 317 bucket.DripRate = resend;
318 bucket.MaxBurst = resend;
319
320 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Land];
321 bucket.DripRate = land;
322 bucket.MaxBurst = land;
323
324 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Wind];
325 bucket.DripRate = wind;
326 bucket.MaxBurst = wind;
327
328 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Cloud];
329 bucket.DripRate = cloud;
330 bucket.MaxBurst = cloud;
331
332 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Asset];
333 bucket.DripRate = asset;
334 bucket.MaxBurst = asset;
335
336 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Task];
337 bucket.DripRate = task + state;
338 bucket.MaxBurst = task + state;
339
340 bucket = m_throttleCategories[(int)ThrottleOutPacketType.State];
341 bucket.DripRate = state;
342 bucket.MaxBurst = state;
343
344 bucket = m_throttleCategories[(int)ThrottleOutPacketType.Texture];
345 bucket.DripRate = texture;
346 bucket.MaxBurst = texture;
326 } 347 }
327 348
328 public byte[] GetThrottlesPacked() 349 public byte[] GetThrottlesPacked()
@@ -342,17 +363,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
342 return data; 363 return data;
343 } 364 }
344 365
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) 366 public bool EnqueueOutgoing(OutgoingPacket packet)
357 { 367 {
358 int category = (int)packet.Category; 368 int category = (int)packet.Category;
@@ -394,10 +404,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
394 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue; 404 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue;
395 TokenBucket bucket; 405 TokenBucket bucket;
396 bool packetSent = false; 406 bool packetSent = false;
407 ThrottleOutPacketTypeFlags emptyCategories = 0;
408
409 //string queueDebugOutput = String.Empty; // Serious debug business
397 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 {
@@ -438,17 +452,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
438 // empty callback now so it has a chance to fill before we 452 // empty callback now so it has a chance to fill before we
439 // get back here 453 // get back here
440 if (queue.Count == 0) 454 if (queue.Count == 0)
441 BeginFireQueueEmpty(i); 455 emptyCategories |= CategoryToFlag(i);
442 } 456 }
443 else 457 else
444 { 458 {
445 // No packets in this queue. Fire the queue empty callback 459 // No packets in this queue. Fire the queue empty callback
446 // if it has not been called recently 460 // if it has not been called recently
447 BeginFireQueueEmpty(i); 461 emptyCategories |= CategoryToFlag(i);
448 } 462 }
449 } 463 }
450 } 464 }
451 465
466 if (emptyCategories != 0)
467 BeginFireQueueEmpty(emptyCategories);
468
469 //m_log.Info("[LLUDPCLIENT]: Queues: " + queueDebugOutput); // Serious debug business
452 return packetSent; 470 return packetSent;
453 } 471 }
454 472
@@ -479,8 +497,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
479 SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r; 497 SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r;
480 } 498 }
481 499
482 // Always round retransmission timeout up to two seconds 500 RTO = (int)(SRTT + Math.Max(m_udpServer.TickCountResolution, K * RTTVAR));
483 RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR))); 501
502 // Clamp the retransmission timeout to manageable values
503 RTO = Utils.Clamp(RTO, 3000, 10000);
504
484 //m_log.Debug("[LLUDPCLIENT]: Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " + 505 //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"); 506 // RTTVAR + " based on new RTT of " + r + "ms");
486 } 507 }
@@ -491,33 +512,89 @@ namespace OpenSim.Region.ClientStack.LindenUDP
491 /// </summary> 512 /// </summary>
492 /// <param name="throttleIndex">Throttle category to fire the callback 513 /// <param name="throttleIndex">Throttle category to fire the callback
493 /// for</param> 514 /// for</param>
494 private void BeginFireQueueEmpty(int throttleIndex) 515 private void BeginFireQueueEmpty(ThrottleOutPacketTypeFlags categories)
495 { 516 {
496 if (!m_onQueueEmptyRunning[throttleIndex]) 517 if (m_nextOnQueueEmpty != 0 && (Environment.TickCount & Int32.MaxValue) >= m_nextOnQueueEmpty)
497 Util.FireAndForget(FireQueueEmpty, throttleIndex); 518 {
519 // Use a value of 0 to signal that FireQueueEmpty is running
520 m_nextOnQueueEmpty = 0;
521 // Asynchronously run the callback
522 Util.FireAndForget(FireQueueEmpty, categories);
523 }
498 } 524 }
499 525
500 /// <summary> 526 /// <summary>
501 /// Checks to see if this queue empty callback is already running, 527 /// Fires the OnQueueEmpty callback and sets the minimum time that it
502 /// then firing the event 528 /// can be called again
503 /// </summary> 529 /// </summary>
504 /// <param name="o">Throttle category to fire the callback for, stored 530 /// <param name="o">Throttle categories to fire the callback for,
505 /// as an object to match the WaitCallback delegate signature</param> 531 /// stored as an object to match the WaitCallback delegate
532 /// signature</param>
506 private void FireQueueEmpty(object o) 533 private void FireQueueEmpty(object o)
507 { 534 {
508 int i = (int)o; 535 const int MIN_CALLBACK_MS = 30;
509 ThrottleOutPacketType type = (ThrottleOutPacketType)i; 536
537 ThrottleOutPacketTypeFlags categories = (ThrottleOutPacketTypeFlags)o;
510 QueueEmpty callback = OnQueueEmpty; 538 QueueEmpty callback = OnQueueEmpty;
539
540 int start = Environment.TickCount & Int32.MaxValue;
511 541
512 if (callback != null) 542 if (callback != null)
513 { 543 {
514 if (!m_onQueueEmptyRunning[i]) 544 try { callback(categories); }
515 { 545 catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + categories + ") threw an exception: " + e.Message, e); }
516 m_onQueueEmptyRunning[i] = true; 546 }
517 try { callback(type); } 547
518 catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); } 548 m_nextOnQueueEmpty = start + MIN_CALLBACK_MS;
519 m_onQueueEmptyRunning[i] = false; 549 if (m_nextOnQueueEmpty == 0)
520 } 550 m_nextOnQueueEmpty = 1;
551 }
552
553 /// <summary>
554 /// Converts a <seealso cref="ThrottleOutPacketType"/> integer to a
555 /// flag value
556 /// </summary>
557 /// <param name="i">Throttle category to convert</param>
558 /// <returns>Flag representation of the throttle category</returns>
559 private static ThrottleOutPacketTypeFlags CategoryToFlag(int i)
560 {
561 ThrottleOutPacketType category = (ThrottleOutPacketType)i;
562
563 /*
564 * Land = 1,
565 /// <summary>Wind data</summary>
566 Wind = 2,
567 /// <summary>Cloud data</summary>
568 Cloud = 3,
569 /// <summary>Any packets that do not fit into the other throttles</summary>
570 Task = 4,
571 /// <summary>Texture assets</summary>
572 Texture = 5,
573 /// <summary>Non-texture assets</summary>
574 Asset = 6,
575 /// <summary>Avatar and primitive data</summary>
576 /// <remarks>This is a sub-category of Task</remarks>
577 State = 7,
578 */
579
580 switch (category)
581 {
582 case ThrottleOutPacketType.Land:
583 return ThrottleOutPacketTypeFlags.Land;
584 case ThrottleOutPacketType.Wind:
585 return ThrottleOutPacketTypeFlags.Wind;
586 case ThrottleOutPacketType.Cloud:
587 return ThrottleOutPacketTypeFlags.Cloud;
588 case ThrottleOutPacketType.Task:
589 return ThrottleOutPacketTypeFlags.Task;
590 case ThrottleOutPacketType.Texture:
591 return ThrottleOutPacketTypeFlags.Texture;
592 case ThrottleOutPacketType.Asset:
593 return ThrottleOutPacketTypeFlags.Asset;
594 case ThrottleOutPacketType.State:
595 return ThrottleOutPacketTypeFlags.State;
596 default:
597 return 0;
521 } 598 }
522 } 599 }
523 } 600 }