diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 140 |
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; | |||
33 | using OpenMetaverse; | 33 | using OpenMetaverse; |
34 | using OpenMetaverse.Packets; | 34 | using OpenMetaverse.Packets; |
35 | 35 | ||
36 | using TokenBucket = OpenSim.Region.ClientStack.LindenUDP.TokenBucket; | ||
37 | |||
36 | namespace OpenSim.Region.ClientStack.LindenUDP | 38 | namespace 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 | } |