diff options
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs | 242 |
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 @@ | |||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Net; | 30 | using System.Net; |
31 | using log4net; | ||
31 | using OpenSim.Framework; | 32 | using OpenSim.Framework; |
32 | using OpenMetaverse; | 33 | using OpenMetaverse; |
34 | using OpenMetaverse.Packets; | ||
33 | 35 | ||
34 | namespace OpenSim.Region.ClientStack.LindenUDP | 36 | namespace 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 | } |