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.cs242
1 files changed, 162 insertions, 80 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index 871e8e8..4eee6b6 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -28,8 +28,10 @@
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Net; 30using System.Net;
31using log4net;
31using OpenSim.Framework; 32using OpenSim.Framework;
32using OpenMetaverse; 33using OpenMetaverse;
34using OpenMetaverse.Packets;
33 35
34namespace OpenSim.Region.ClientStack.LindenUDP 36namespace OpenSim.Region.ClientStack.LindenUDP
35{ 37{
@@ -59,9 +61,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
59 /// </summary> 61 /// </summary>
60 public sealed class LLUDPClient 62 public sealed class LLUDPClient
61 { 63 {
64 // TODO: Make this a config setting
65 /// <summary>Percentage of the task throttle category that is allocated to avatar and prim
66 /// state updates</summary>
67 const float STATE_TASK_PERCENTAGE = 0.8f;
68
69 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
70
62 /// <summary>The number of packet categories to throttle on. If a throttle category is added 71 /// <summary>The number of packet categories to throttle on. If a throttle category is added
63 /// or removed, this number must also change</summary> 72 /// or removed, this number must also change</summary>
64 const int THROTTLE_CATEGORY_COUNT = 7; 73 const int THROTTLE_CATEGORY_COUNT = 8;
65 74
66 /// <summary>Fired when updated networking stats are produced for this client</summary> 75 /// <summary>Fired when updated networking stats are produced for this client</summary>
67 public event PacketStats OnPacketStats; 76 public event PacketStats OnPacketStats;
@@ -80,10 +89,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
80 /// <summary>Packets we have sent that need to be ACKed by the client</summary> 89 /// <summary>Packets we have sent that need to be ACKed by the client</summary>
81 public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); 90 public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection();
82 /// <summary>ACKs that are queued up, waiting to be sent to the client</summary> 91 /// <summary>ACKs that are queued up, waiting to be sent to the client</summary>
83 public readonly LocklessQueue<uint> PendingAcks = new LocklessQueue<uint>(); 92 public readonly OpenSim.Framework.LocklessQueue<uint> PendingAcks = new OpenSim.Framework.LocklessQueue<uint>();
84 93
85 /// <summary>Reference to the IClientAPI for this client</summary>
86 public LLClientView ClientAPI;
87 /// <summary>Current packet sequence number</summary> 94 /// <summary>Current packet sequence number</summary>
88 public int CurrentSequence; 95 public int CurrentSequence;
89 /// <summary>Current ping sequence number</summary> 96 /// <summary>Current ping sequence number</summary>
@@ -123,21 +130,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
123 private int m_packetsSentReported; 130 private int m_packetsSentReported;
124 131
125 /// <summary>Throttle bucket for this agent's connection</summary> 132 /// <summary>Throttle bucket for this agent's connection</summary>
126 private readonly TokenBucket throttle; 133 private readonly TokenBucket m_throttle;
127 /// <summary>Throttle buckets for each packet category</summary> 134 /// <summary>Throttle buckets for each packet category</summary>
128 private readonly TokenBucket[] throttleCategories; 135 private readonly TokenBucket[] m_throttleCategories;
129 /// <summary>Throttle rate defaults and limits</summary> 136 /// <summary>Throttle rate defaults and limits</summary>
130 private readonly ThrottleRates defaultThrottleRates; 137 private readonly ThrottleRates m_defaultThrottleRates;
131 /// <summary>Outgoing queues for throttled packets</summary> 138 /// <summary>Outgoing queues for throttled packets</summary>
132 private readonly LocklessQueue<OutgoingPacket>[] packetOutboxes = new LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT]; 139 private readonly OpenSim.Framework.LocklessQueue<OutgoingPacket>[] m_packetOutboxes = new OpenSim.Framework.LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT];
133 /// <summary>A container that can hold one packet for each outbox, used to store 140 /// <summary>A container that can hold one packet for each outbox, used to store
134 /// dequeued packets that are being held for throttling</summary> 141 /// dequeued packets that are being held for throttling</summary>
135 private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; 142 private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
136 /// <summary>An optimization to store the length of dequeued packets being held 143 /// <summary>Flags to prevent queue empty callbacks from stacking up on
137 /// for throttling. This avoids expensive calls to Packet.Length</summary> 144 /// top of each other</summary>
138 private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT]; 145 private readonly bool[] m_onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT];
139 /// <summary>A reference to the LLUDPServer that is managing this client</summary> 146 /// <summary>A reference to the LLUDPServer that is managing this client</summary>
140 private readonly LLUDPServer udpServer; 147 private readonly LLUDPServer m_udpServer;
141 148
142 /// <summary> 149 /// <summary>
143 /// Default constructor 150 /// Default constructor
@@ -151,24 +158,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
151 /// <param name="remoteEndPoint">Remote endpoint for this connection</param> 158 /// <param name="remoteEndPoint">Remote endpoint for this connection</param>
152 public LLUDPClient(LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint) 159 public LLUDPClient(LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint)
153 { 160 {
154 udpServer = server;
155 AgentID = agentID; 161 AgentID = agentID;
156 RemoteEndPoint = remoteEndPoint; 162 RemoteEndPoint = remoteEndPoint;
157 CircuitCode = circuitCode; 163 CircuitCode = circuitCode;
158 defaultThrottleRates = rates; 164 m_udpServer = server;
165 m_defaultThrottleRates = rates;
166 m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total);
167 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
159 168
160 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) 169 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
161 packetOutboxes[i] = new LocklessQueue<OutgoingPacket>(); 170 {
162 171 ThrottleOutPacketType type = (ThrottleOutPacketType)i;
163 throttle = new TokenBucket(parentThrottle, 0, 0); 172
164 throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; 173 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
165 throttleCategories[(int)ThrottleOutPacketType.Resend] = new TokenBucket(throttle, rates.ResendLimit, rates.Resend); 174 m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type));
166 throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land); 175 }
167 throttleCategories[(int)ThrottleOutPacketType.Wind] = new TokenBucket(throttle, rates.WindLimit, rates.Wind);
168 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);
171 throttleCategories[(int)ThrottleOutPacketType.Asset] = new TokenBucket(throttle, rates.AssetLimit, rates.Asset);
172 176
173 // Set the granularity variable used for retransmission calculations to 177 // Set the granularity variable used for retransmission calculations to
174 // the measured resolution of Environment.TickCount 178 // the measured resolution of Environment.TickCount
@@ -176,6 +180,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
176 180
177 // Default the retransmission timeout to three seconds 181 // Default the retransmission timeout to three seconds
178 RTO = 3000; 182 RTO = 3000;
183
184 // Initialize this to a sane value to prevent early disconnects
185 TickLastPacketReceived = Environment.TickCount;
179 } 186 }
180 187
181 /// <summary> 188 /// <summary>
@@ -183,8 +190,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
183 /// </summary> 190 /// </summary>
184 public void Shutdown() 191 public void Shutdown()
185 { 192 {
186 // TODO: Do we need to invalidate the circuit?
187 IsConnected = false; 193 IsConnected = false;
194 NeedAcks.Clear();
195 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
196 {
197 m_packetOutboxes[i].Clear();
198 m_nextPackets[i] = null;
199 }
200 OnPacketStats = null;
201 OnQueueEmpty = null;
188 } 202 }
189 203
190 /// <summary> 204 /// <summary>
@@ -200,13 +214,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
200 info.pendingAcks = new Dictionary<uint, uint>(); 214 info.pendingAcks = new Dictionary<uint, uint>();
201 info.needAck = new Dictionary<uint, byte[]>(); 215 info.needAck = new Dictionary<uint, byte[]>();
202 216
203 info.resendThrottle = throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; 217 info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
204 info.landThrottle = throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; 218 info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
205 info.windThrottle = throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; 219 info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
206 info.cloudThrottle = throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; 220 info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
207 info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; 221 info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
208 info.assetThrottle = throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; 222 info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
209 info.textureThrottle = throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; 223 info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
210 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + 224 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle +
211 info.taskThrottle + info.assetThrottle + info.textureThrottle; 225 info.taskThrottle + info.assetThrottle + info.textureThrottle;
212 226
@@ -267,6 +281,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
267 adjData = throttleData; 281 adjData = throttleData;
268 } 282 }
269 283
284 // 0.125f converts from bits to bytes
270 int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 285 int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
271 int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 286 int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
272 int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 287 int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
@@ -274,22 +289,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP
274 int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 289 int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
275 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 290 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
276 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); 291 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
292 // State is a subcategory of task that we allocate a percentage to
293 int state = (int)((float)task * STATE_TASK_PERCENTAGE);
294 task -= state;
295
296 int ceiling = Int32.MaxValue;
297 if (m_defaultThrottleRates.Total != 0)
298 {
299 ceiling = m_defaultThrottleRates.Total;
300 if (ceiling < Packet.MTU) ceiling = Packet.MTU;
301 }
277 302
278 resend = (resend <= defaultThrottleRates.ResendLimit) ? resend : defaultThrottleRates.ResendLimit; 303 resend = Utils.Clamp(resend, Packet.MTU, ceiling);
279 land = (land <= defaultThrottleRates.LandLimit) ? land : defaultThrottleRates.LandLimit; 304 land = Utils.Clamp(land, Packet.MTU, ceiling);
280 wind = (wind <= defaultThrottleRates.WindLimit) ? wind : defaultThrottleRates.WindLimit; 305 wind = Utils.Clamp(wind, Packet.MTU, ceiling);
281 cloud = (cloud <= defaultThrottleRates.CloudLimit) ? cloud : defaultThrottleRates.CloudLimit; 306 cloud = Utils.Clamp(cloud, Packet.MTU, ceiling);
282 task = (task <= defaultThrottleRates.TaskLimit) ? task : defaultThrottleRates.TaskLimit; 307 task = Utils.Clamp(task, Packet.MTU, ceiling);
283 texture = (texture <= defaultThrottleRates.TextureLimit) ? texture : defaultThrottleRates.TextureLimit; 308 texture = Utils.Clamp(texture, Packet.MTU, ceiling);
284 asset = (asset <= defaultThrottleRates.AssetLimit) ? asset : defaultThrottleRates.AssetLimit; 309 asset = Utils.Clamp(asset, Packet.MTU, ceiling);
285 310 state = Utils.Clamp(state, Packet.MTU, ceiling);
286 SetThrottle(ThrottleOutPacketType.Resend, resend); 311
287 SetThrottle(ThrottleOutPacketType.Land, land); 312 int total = resend + land + wind + cloud + task + texture + asset + state;
288 SetThrottle(ThrottleOutPacketType.Wind, wind); 313 int taskTotal = task + state;
289 SetThrottle(ThrottleOutPacketType.Cloud, cloud); 314
290 SetThrottle(ThrottleOutPacketType.Task, task); 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}",
291 SetThrottle(ThrottleOutPacketType.Texture, texture); 316 AgentID, resend, land, wind, cloud, task, texture, asset, state, total);
292 SetThrottle(ThrottleOutPacketType.Asset, asset); 317
318 SetThrottle(ThrottleOutPacketType.Resend, resend, resend);
319 SetThrottle(ThrottleOutPacketType.Land, land, land);
320 SetThrottle(ThrottleOutPacketType.Wind, wind, wind);
321 SetThrottle(ThrottleOutPacketType.Cloud, cloud, cloud);
322 SetThrottle(ThrottleOutPacketType.Task, task, taskTotal);
323 SetThrottle(ThrottleOutPacketType.Texture, texture, texture);
324 SetThrottle(ThrottleOutPacketType.Asset, asset, asset);
325 SetThrottle(ThrottleOutPacketType.State, state, taskTotal);
293 } 326 }
294 327
295 public byte[] GetThrottlesPacked() 328 public byte[] GetThrottlesPacked()
@@ -297,25 +330,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
297 byte[] data = new byte[7 * 4]; 330 byte[] data = new byte[7 * 4];
298 int i = 0; 331 int i = 0;
299 332
300 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; 333 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4;
301 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; 334 Buffer.BlockCopy(Utils.FloatToBytes((float)m_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; 335 Buffer.BlockCopy(Utils.FloatToBytes((float)m_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; 336 Buffer.BlockCopy(Utils.FloatToBytes((float)m_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; 337 Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) +
305 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; 338 m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4;
306 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; 339 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4;
340 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4;
307 341
308 return data; 342 return data;
309 } 343 }
310 344
311 public void SetThrottle(ThrottleOutPacketType category, int rate) 345 public void SetThrottle(ThrottleOutPacketType category, int rate, int maxBurst)
312 { 346 {
313 int i = (int)category; 347 int i = (int)category;
314 if (i >= 0 && i < throttleCategories.Length) 348 if (i >= 0 && i < m_throttleCategories.Length)
315 { 349 {
316 TokenBucket bucket = throttleCategories[(int)category]; 350 TokenBucket bucket = m_throttleCategories[(int)category];
317 bucket.MaxBurst = rate;
318 bucket.DripRate = rate; 351 bucket.DripRate = rate;
352 bucket.MaxBurst = maxBurst;
319 } 353 }
320 } 354 }
321 355
@@ -323,12 +357,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
323 { 357 {
324 int category = (int)packet.Category; 358 int category = (int)packet.Category;
325 359
326 if (category >= 0 && category < packetOutboxes.Length) 360 if (category >= 0 && category < m_packetOutboxes.Length)
327 { 361 {
328 LocklessQueue<OutgoingPacket> queue = packetOutboxes[category]; 362 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category];
329 TokenBucket bucket = throttleCategories[category]; 363 TokenBucket bucket = m_throttleCategories[category];
330 364
331 if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength)) 365 if (m_throttleCategories[category].RemoveTokens(packet.Buffer.DataLength))
332 { 366 {
333 // Enough tokens were removed from the bucket, the packet will not be queued 367 // Enough tokens were removed from the bucket, the packet will not be queued
334 return false; 368 return false;
@@ -357,24 +391,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
357 public bool DequeueOutgoing() 391 public bool DequeueOutgoing()
358 { 392 {
359 OutgoingPacket packet; 393 OutgoingPacket packet;
360 LocklessQueue<OutgoingPacket> queue; 394 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue;
361 TokenBucket bucket; 395 TokenBucket bucket;
362 bool packetSent = false; 396 bool packetSent = false;
363 397
364 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) 398 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
365 { 399 {
366 bucket = throttleCategories[i]; 400 bucket = m_throttleCategories[i];
367 401
368 if (nextPackets[i] != null) 402 if (m_nextPackets[i] != null)
369 { 403 {
370 // This bucket was empty the last time we tried to send a packet, 404 // This bucket was empty the last time we tried to send a packet,
371 // leaving a dequeued packet still waiting to be sent out. Try to 405 // leaving a dequeued packet still waiting to be sent out. Try to
372 // send it again 406 // send it again
373 if (bucket.RemoveTokens(nextPacketLengths[i])) 407 OutgoingPacket nextPacket = m_nextPackets[i];
408 if (bucket.RemoveTokens(nextPacket.Buffer.DataLength))
374 { 409 {
375 // Send the packet 410 // Send the packet
376 udpServer.SendPacketFinal(nextPackets[i]); 411 m_udpServer.SendPacketFinal(nextPacket);
377 nextPackets[i] = null; 412 m_nextPackets[i] = null;
378 packetSent = true; 413 packetSent = true;
379 } 414 }
380 } 415 }
@@ -382,7 +417,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
382 { 417 {
383 // No dequeued packet waiting to be sent, try to pull one off 418 // No dequeued packet waiting to be sent, try to pull one off
384 // this queue 419 // this queue
385 queue = packetOutboxes[i]; 420 queue = m_packetOutboxes[i];
386 if (queue.Dequeue(out packet)) 421 if (queue.Dequeue(out packet))
387 { 422 {
388 // A packet was pulled off the queue. See if we have 423 // A packet was pulled off the queue. See if we have
@@ -390,23 +425,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
390 if (bucket.RemoveTokens(packet.Buffer.DataLength)) 425 if (bucket.RemoveTokens(packet.Buffer.DataLength))
391 { 426 {
392 // Send the packet 427 // Send the packet
393 udpServer.SendPacketFinal(packet); 428 m_udpServer.SendPacketFinal(packet);
394 packetSent = true; 429 packetSent = true;
395 } 430 }
396 else 431 else
397 { 432 {
398 // Save the dequeued packet and the length calculation for 433 // Save the dequeued packet for the next iteration
399 // the next iteration 434 m_nextPackets[i] = packet;
400 nextPackets[i] = packet;
401 nextPacketLengths[i] = packet.Buffer.DataLength;
402 } 435 }
436
437 // If the queue is empty after this dequeue, fire the queue
438 // empty callback now so it has a chance to fill before we
439 // get back here
440 if (queue.Count == 0)
441 BeginFireQueueEmpty(i);
403 } 442 }
404 else 443 else
405 { 444 {
406 // No packets in this queue. Fire the queue empty callback 445 // No packets in this queue. Fire the queue empty callback
407 QueueEmpty callback = OnQueueEmpty; 446 // if it has not been called recently
408 if (callback != null) 447 BeginFireQueueEmpty(i);
409 callback((ThrottleOutPacketType)i);
410 } 448 }
411 } 449 }
412 } 450 }
@@ -414,6 +452,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
414 return packetSent; 452 return packetSent;
415 } 453 }
416 454
455 /// <summary>
456 /// Called when an ACK packet is received and a round-trip time for a
457 /// packet is calculated. This is used to calculate the smoothed
458 /// round-trip time, round trip time variance, and finally the
459 /// retransmission timeout
460 /// </summary>
461 /// <param name="r">Round-trip time of a single packet and its
462 /// acknowledgement</param>
417 public void UpdateRoundTrip(float r) 463 public void UpdateRoundTrip(float r)
418 { 464 {
419 const float ALPHA = 0.125f; 465 const float ALPHA = 0.125f;
@@ -435,8 +481,44 @@ namespace OpenSim.Region.ClientStack.LindenUDP
435 481
436 // Always round retransmission timeout up to two seconds 482 // Always round retransmission timeout up to two seconds
437 RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR))); 483 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 " + 484 //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"); 485 // RTTVAR + " based on new RTT of " + r + "ms");
440 } 486 }
487
488 /// <summary>
489 /// Does an early check to see if this queue empty callback is already
490 /// running, then asynchronously firing the event
491 /// </summary>
492 /// <param name="throttleIndex">Throttle category to fire the callback
493 /// for</param>
494 private void BeginFireQueueEmpty(int throttleIndex)
495 {
496 if (!m_onQueueEmptyRunning[throttleIndex])
497 Util.FireAndForget(FireQueueEmpty, throttleIndex);
498 }
499
500 /// <summary>
501 /// Checks to see if this queue empty callback is already running,
502 /// then firing the event
503 /// </summary>
504 /// <param name="o">Throttle category to fire the callback for, stored
505 /// as an object to match the WaitCallback delegate signature</param>
506 private void FireQueueEmpty(object o)
507 {
508 int i = (int)o;
509 ThrottleOutPacketType type = (ThrottleOutPacketType)i;
510 QueueEmpty callback = OnQueueEmpty;
511
512 if (callback != null)
513 {
514 if (!m_onQueueEmptyRunning[i])
515 {
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 }
522 }
441 } 523 }
442} 524}