diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 85 |
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 | } |