diff options
author | Melanie | 2009-10-08 08:07:38 +0100 |
---|---|---|
committer | Melanie | 2009-10-08 08:07:38 +0100 |
commit | fe679be9e76190ac0dc8892469787e63a7a48b5c (patch) | |
tree | 220ef33da75f09b3e7ef3684c5ed7367e175691e /OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs | |
parent | store owner_uuid in the region table (diff) | |
parent | One last attempt at tunning the locking/no locking behaviour. The previous on... (diff) | |
download | opensim-SC-fe679be9e76190ac0dc8892469787e63a7a48b5c.zip opensim-SC-fe679be9e76190ac0dc8892469787e63a7a48b5c.tar.gz opensim-SC-fe679be9e76190ac0dc8892469787e63a7a48b5c.tar.bz2 opensim-SC-fe679be9e76190ac0dc8892469787e63a7a48b5c.tar.xz |
Merge branch 'htb-throttle'
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs | 742 |
1 files changed, 0 insertions, 742 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs deleted file mode 100644 index 3eed2e0..0000000 --- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs +++ /dev/null | |||
@@ -1,742 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using System.Threading; | ||
32 | using System.Timers; | ||
33 | using log4net; | ||
34 | using OpenMetaverse; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Framework.Statistics; | ||
37 | using OpenSim.Framework.Statistics.Interfaces; | ||
38 | using Timer=System.Timers.Timer; | ||
39 | |||
40 | namespace OpenSim.Region.ClientStack.LindenUDP | ||
41 | { | ||
42 | public class LLPacketQueue : IPullStatsProvider, IDisposable | ||
43 | { | ||
44 | private static readonly ILog m_log | ||
45 | = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
46 | |||
47 | /// <summary> | ||
48 | /// Is queueing enabled at all? | ||
49 | /// </summary> | ||
50 | private bool m_enabled = true; | ||
51 | |||
52 | private OpenSim.Framework.BlockingQueue<LLQueItem> SendQueue; | ||
53 | |||
54 | private Queue<LLQueItem> IncomingPacketQueue; | ||
55 | private Queue<LLQueItem> OutgoingPacketQueue; | ||
56 | private Queue<LLQueItem> ResendOutgoingPacketQueue; | ||
57 | private Queue<LLQueItem> LandOutgoingPacketQueue; | ||
58 | private Queue<LLQueItem> WindOutgoingPacketQueue; | ||
59 | private Queue<LLQueItem> CloudOutgoingPacketQueue; | ||
60 | private Queue<LLQueItem> TaskOutgoingPacketQueue; | ||
61 | private Queue<LLQueItem> TaskLowpriorityPacketQueue; | ||
62 | private Queue<LLQueItem> TextureOutgoingPacketQueue; | ||
63 | private Queue<LLQueItem> AssetOutgoingPacketQueue; | ||
64 | |||
65 | private List<ThrottleOutPacketType> Empty = new List<ThrottleOutPacketType>(); | ||
66 | // m_log.Info("[THROTTLE]: Entering Throttle"); | ||
67 | // private Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>(); | ||
68 | // private Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>(); | ||
69 | |||
70 | // All throttle times and number of bytes are calculated by dividing by this value | ||
71 | // This value also determines how many times per throttletimems the timer will run | ||
72 | // If throttleimems is 1000 ms, then the timer will fire every 1000/7 milliseconds | ||
73 | |||
74 | private float throttleMultiplier = 2.0f; // Default value really doesn't matter. | ||
75 | private int throttleTimeDivisor = 7; | ||
76 | |||
77 | private int throttletimems = 1000; | ||
78 | |||
79 | internal LLPacketThrottle ResendThrottle; | ||
80 | internal LLPacketThrottle LandThrottle; | ||
81 | internal LLPacketThrottle WindThrottle; | ||
82 | internal LLPacketThrottle CloudThrottle; | ||
83 | internal LLPacketThrottle TaskThrottle; | ||
84 | internal LLPacketThrottle AssetThrottle; | ||
85 | internal LLPacketThrottle TextureThrottle; | ||
86 | internal LLPacketThrottle TotalThrottle; | ||
87 | |||
88 | private Dictionary<uint,int> contents = new Dictionary<uint, int>(); | ||
89 | |||
90 | // private long LastThrottle; | ||
91 | // private long ThrottleInterval; | ||
92 | private Timer throttleTimer; | ||
93 | |||
94 | private UUID m_agentId; | ||
95 | |||
96 | public event QueueEmpty OnQueueEmpty; | ||
97 | |||
98 | public LLPacketQueue(UUID agentId, ClientStackUserSettings userSettings) | ||
99 | { | ||
100 | // While working on this, the BlockingQueue had me fooled for a bit. | ||
101 | // The Blocking queue causes the thread to stop until there's something | ||
102 | // in it to process. it's an on-purpose threadlock though because | ||
103 | // without it, the clientloop will suck up all sim resources. | ||
104 | |||
105 | SendQueue = new OpenSim.Framework.BlockingQueue<LLQueItem>(); | ||
106 | |||
107 | IncomingPacketQueue = new Queue<LLQueItem>(); | ||
108 | OutgoingPacketQueue = new Queue<LLQueItem>(); | ||
109 | ResendOutgoingPacketQueue = new Queue<LLQueItem>(); | ||
110 | LandOutgoingPacketQueue = new Queue<LLQueItem>(); | ||
111 | WindOutgoingPacketQueue = new Queue<LLQueItem>(); | ||
112 | CloudOutgoingPacketQueue = new Queue<LLQueItem>(); | ||
113 | TaskOutgoingPacketQueue = new Queue<LLQueItem>(); | ||
114 | TaskLowpriorityPacketQueue = new Queue<LLQueItem>(); | ||
115 | TextureOutgoingPacketQueue = new Queue<LLQueItem>(); | ||
116 | AssetOutgoingPacketQueue = new Queue<LLQueItem>(); | ||
117 | |||
118 | // Store the throttle multiplier for posterity. | ||
119 | throttleMultiplier = userSettings.ClientThrottleMultipler; | ||
120 | |||
121 | |||
122 | int throttleMaxBPS = 1500000; | ||
123 | if (userSettings.TotalThrottleSettings != null) | ||
124 | throttleMaxBPS = userSettings.TotalThrottleSettings.Max; | ||
125 | |||
126 | // Set up the throttle classes (min, max, current) in bits per second | ||
127 | ResendThrottle = new LLPacketThrottle(5000, throttleMaxBPS / 15, 16000, userSettings.ClientThrottleMultipler); | ||
128 | LandThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 15, 2000, userSettings.ClientThrottleMultipler); | ||
129 | WindThrottle = new LLPacketThrottle(0, throttleMaxBPS / 15, 0, userSettings.ClientThrottleMultipler); | ||
130 | CloudThrottle = new LLPacketThrottle(0, throttleMaxBPS / 15, 0, userSettings.ClientThrottleMultipler); | ||
131 | TaskThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 3000, userSettings.ClientThrottleMultipler); | ||
132 | AssetThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 1000, userSettings.ClientThrottleMultipler); | ||
133 | TextureThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 4000, userSettings.ClientThrottleMultipler); | ||
134 | |||
135 | |||
136 | // Total Throttle trumps all - it is the number of bits in total that are allowed to go out per second. | ||
137 | |||
138 | |||
139 | ThrottleSettings totalThrottleSettings = userSettings.TotalThrottleSettings; | ||
140 | if (null == totalThrottleSettings) | ||
141 | { | ||
142 | totalThrottleSettings = new ThrottleSettings(0, throttleMaxBPS, 28000); | ||
143 | } | ||
144 | |||
145 | TotalThrottle | ||
146 | = new LLPacketThrottle( | ||
147 | totalThrottleSettings.Min, totalThrottleSettings.Max, totalThrottleSettings.Current, | ||
148 | userSettings.ClientThrottleMultipler); | ||
149 | |||
150 | throttleTimer = new Timer((int)(throttletimems / throttleTimeDivisor)); | ||
151 | throttleTimer.Elapsed += ThrottleTimerElapsed; | ||
152 | throttleTimer.Start(); | ||
153 | |||
154 | // TIMERS needed for this | ||
155 | // LastThrottle = DateTime.Now.Ticks; | ||
156 | // ThrottleInterval = (long)(throttletimems/throttleTimeDivisor); | ||
157 | |||
158 | m_agentId = agentId; | ||
159 | |||
160 | if (StatsManager.SimExtraStats != null) | ||
161 | { | ||
162 | StatsManager.SimExtraStats.RegisterPacketQueueStatsProvider(m_agentId, this); | ||
163 | } | ||
164 | } | ||
165 | |||
166 | /* STANDARD QUEUE MANIPULATION INTERFACES */ | ||
167 | |||
168 | public void Enqueue(LLQueItem item) | ||
169 | { | ||
170 | if (!m_enabled) | ||
171 | { | ||
172 | return; | ||
173 | } | ||
174 | // We could micro lock, but that will tend to actually | ||
175 | // probably be worse than just synchronizing on SendQueue | ||
176 | |||
177 | if (item == null) | ||
178 | { | ||
179 | SendQueue.Enqueue(item); | ||
180 | return; | ||
181 | } | ||
182 | |||
183 | if (item.Incoming) | ||
184 | { | ||
185 | SendQueue.PriorityEnqueue(item); | ||
186 | return; | ||
187 | } | ||
188 | |||
189 | if (item.Sequence != 0) | ||
190 | lock (contents) | ||
191 | { | ||
192 | if (contents.ContainsKey(item.Sequence)) | ||
193 | contents[item.Sequence] += 1; | ||
194 | else | ||
195 | contents.Add(item.Sequence, 1); | ||
196 | } | ||
197 | |||
198 | lock (this) | ||
199 | { | ||
200 | switch (item.throttleType & ThrottleOutPacketType.TypeMask) | ||
201 | { | ||
202 | case ThrottleOutPacketType.Resend: | ||
203 | ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item, ThrottleOutPacketType.Resend); | ||
204 | break; | ||
205 | case ThrottleOutPacketType.Texture: | ||
206 | ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item, ThrottleOutPacketType.Texture); | ||
207 | break; | ||
208 | case ThrottleOutPacketType.Task: | ||
209 | if ((item.throttleType & ThrottleOutPacketType.LowPriority) != 0) | ||
210 | ThrottleCheck(ref TaskThrottle, ref TaskLowpriorityPacketQueue, item, ThrottleOutPacketType.Task); | ||
211 | else | ||
212 | ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item, ThrottleOutPacketType.Task); | ||
213 | break; | ||
214 | case ThrottleOutPacketType.Land: | ||
215 | ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item, ThrottleOutPacketType.Land); | ||
216 | break; | ||
217 | case ThrottleOutPacketType.Asset: | ||
218 | ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item, ThrottleOutPacketType.Asset); | ||
219 | break; | ||
220 | case ThrottleOutPacketType.Cloud: | ||
221 | ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item, ThrottleOutPacketType.Cloud); | ||
222 | break; | ||
223 | case ThrottleOutPacketType.Wind: | ||
224 | ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item, ThrottleOutPacketType.Wind); | ||
225 | break; | ||
226 | |||
227 | default: | ||
228 | // Acknowledgements and other such stuff should go directly to the blocking Queue | ||
229 | // Throttling them may and likely 'will' be problematic | ||
230 | SendQueue.PriorityEnqueue(item); | ||
231 | break; | ||
232 | } | ||
233 | } | ||
234 | } | ||
235 | |||
236 | public LLQueItem Dequeue() | ||
237 | { | ||
238 | while (true) | ||
239 | { | ||
240 | LLQueItem item = SendQueue.Dequeue(); | ||
241 | if (item == null) | ||
242 | return null; | ||
243 | if (item.Incoming) | ||
244 | return item; | ||
245 | item.TickCount = System.Environment.TickCount; | ||
246 | if (item.Sequence == 0) | ||
247 | return item; | ||
248 | lock (contents) | ||
249 | { | ||
250 | if (contents.ContainsKey(item.Sequence)) | ||
251 | { | ||
252 | if (contents[item.Sequence] == 1) | ||
253 | contents.Remove(item.Sequence); | ||
254 | else | ||
255 | contents[item.Sequence] -= 1; | ||
256 | return item; | ||
257 | } | ||
258 | } | ||
259 | } | ||
260 | } | ||
261 | |||
262 | public void Cancel(uint sequence) | ||
263 | { | ||
264 | lock (contents) contents.Remove(sequence); | ||
265 | } | ||
266 | |||
267 | public bool Contains(uint sequence) | ||
268 | { | ||
269 | lock (contents) return contents.ContainsKey(sequence); | ||
270 | } | ||
271 | |||
272 | public void Flush() | ||
273 | { | ||
274 | lock (this) | ||
275 | { | ||
276 | // These categories do not contain transactional packets so we can safely drop any pending data in them | ||
277 | LandOutgoingPacketQueue.Clear(); | ||
278 | WindOutgoingPacketQueue.Clear(); | ||
279 | CloudOutgoingPacketQueue.Clear(); | ||
280 | TextureOutgoingPacketQueue.Clear(); | ||
281 | AssetOutgoingPacketQueue.Clear(); | ||
282 | |||
283 | // Now comes the fun part.. we dump all remaining resend and task packets into the send queue | ||
284 | while (ResendOutgoingPacketQueue.Count > 0 || TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) | ||
285 | { | ||
286 | if (ResendOutgoingPacketQueue.Count > 0) | ||
287 | SendQueue.Enqueue(ResendOutgoingPacketQueue.Dequeue()); | ||
288 | |||
289 | if (TaskOutgoingPacketQueue.Count > 0) | ||
290 | SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue()); | ||
291 | |||
292 | if (TaskLowpriorityPacketQueue.Count > 0) | ||
293 | SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue()); | ||
294 | } | ||
295 | } | ||
296 | } | ||
297 | |||
298 | public void WipeClean() | ||
299 | { | ||
300 | lock (this) | ||
301 | { | ||
302 | ResendOutgoingPacketQueue.Clear(); | ||
303 | LandOutgoingPacketQueue.Clear(); | ||
304 | WindOutgoingPacketQueue.Clear(); | ||
305 | CloudOutgoingPacketQueue.Clear(); | ||
306 | TaskOutgoingPacketQueue.Clear(); | ||
307 | TaskLowpriorityPacketQueue.Clear(); | ||
308 | TextureOutgoingPacketQueue.Clear(); | ||
309 | AssetOutgoingPacketQueue.Clear(); | ||
310 | SendQueue.Clear(); | ||
311 | lock (contents) contents.Clear(); | ||
312 | } | ||
313 | } | ||
314 | |||
315 | public void Close() | ||
316 | { | ||
317 | Dispose(); | ||
318 | } | ||
319 | |||
320 | public void Dispose() | ||
321 | { | ||
322 | Flush(); | ||
323 | WipeClean(); // I'm sure there's a dirty joke in here somewhere. -AFrisby | ||
324 | |||
325 | m_enabled = false; | ||
326 | throttleTimer.Stop(); | ||
327 | throttleTimer.Close(); | ||
328 | |||
329 | if (StatsManager.SimExtraStats != null) | ||
330 | { | ||
331 | StatsManager.SimExtraStats.DeregisterPacketQueueStatsProvider(m_agentId); | ||
332 | } | ||
333 | } | ||
334 | |||
335 | private void ResetCounters() | ||
336 | { | ||
337 | ResendThrottle.Reset(); | ||
338 | LandThrottle.Reset(); | ||
339 | WindThrottle.Reset(); | ||
340 | CloudThrottle.Reset(); | ||
341 | TaskThrottle.Reset(); | ||
342 | AssetThrottle.Reset(); | ||
343 | TextureThrottle.Reset(); | ||
344 | TotalThrottle.Reset(); | ||
345 | } | ||
346 | |||
347 | private bool PacketsWaiting() | ||
348 | { | ||
349 | return (ResendOutgoingPacketQueue.Count > 0 || | ||
350 | LandOutgoingPacketQueue.Count > 0 || | ||
351 | WindOutgoingPacketQueue.Count > 0 || | ||
352 | CloudOutgoingPacketQueue.Count > 0 || | ||
353 | TaskOutgoingPacketQueue.Count > 0 || | ||
354 | TaskLowpriorityPacketQueue.Count > 0 || | ||
355 | AssetOutgoingPacketQueue.Count > 0 || | ||
356 | TextureOutgoingPacketQueue.Count > 0); | ||
357 | } | ||
358 | |||
359 | public void ProcessThrottle() | ||
360 | { | ||
361 | // I was considering this.. Will an event fire if the thread it's on is blocked? | ||
362 | |||
363 | // Then I figured out.. it doesn't really matter.. because this thread won't be blocked for long | ||
364 | // The General overhead of the UDP protocol gets sent to the queue un-throttled by this | ||
365 | // so This'll pick up about around the right time. | ||
366 | |||
367 | int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once. | ||
368 | int throttleLoops = 0; | ||
369 | List<ThrottleOutPacketType> e; | ||
370 | |||
371 | // We're going to dequeue all of the saved up packets until | ||
372 | // we've hit the throttle limit or there's no more packets to send | ||
373 | lock (this) | ||
374 | { | ||
375 | // this variable will be true if there was work done in the last execution of the | ||
376 | // loop, since each pass through the loop checks the queue length, we no longer | ||
377 | // need the check on entering the loop | ||
378 | bool qchanged = true; | ||
379 | |||
380 | ResetCounters(); | ||
381 | |||
382 | while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops) | ||
383 | { | ||
384 | qchanged = false; // We will break out of the loop if no work was accomplished | ||
385 | |||
386 | throttleLoops++; | ||
387 | //Now comes the fun part.. we dump all our elements into m_packetQueue that we've saved up. | ||
388 | if ((ResendOutgoingPacketQueue.Count > 0) && ResendThrottle.UnderLimit()) | ||
389 | { | ||
390 | LLQueItem qpack = ResendOutgoingPacketQueue.Dequeue(); | ||
391 | |||
392 | SendQueue.Enqueue(qpack); | ||
393 | TotalThrottle.AddBytes(qpack.Length); | ||
394 | ResendThrottle.AddBytes(qpack.Length); | ||
395 | |||
396 | qchanged = true; | ||
397 | } | ||
398 | |||
399 | if ((LandOutgoingPacketQueue.Count > 0) && LandThrottle.UnderLimit()) | ||
400 | { | ||
401 | LLQueItem qpack = LandOutgoingPacketQueue.Dequeue(); | ||
402 | |||
403 | SendQueue.Enqueue(qpack); | ||
404 | TotalThrottle.AddBytes(qpack.Length); | ||
405 | LandThrottle.AddBytes(qpack.Length); | ||
406 | qchanged = true; | ||
407 | |||
408 | if (LandOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Land)) | ||
409 | Empty.Add(ThrottleOutPacketType.Land); | ||
410 | } | ||
411 | |||
412 | if ((WindOutgoingPacketQueue.Count > 0) && WindThrottle.UnderLimit()) | ||
413 | { | ||
414 | LLQueItem qpack = WindOutgoingPacketQueue.Dequeue(); | ||
415 | |||
416 | SendQueue.Enqueue(qpack); | ||
417 | TotalThrottle.AddBytes(qpack.Length); | ||
418 | WindThrottle.AddBytes(qpack.Length); | ||
419 | qchanged = true; | ||
420 | |||
421 | if (WindOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Wind)) | ||
422 | Empty.Add(ThrottleOutPacketType.Wind); | ||
423 | } | ||
424 | |||
425 | if ((CloudOutgoingPacketQueue.Count > 0) && CloudThrottle.UnderLimit()) | ||
426 | { | ||
427 | LLQueItem qpack = CloudOutgoingPacketQueue.Dequeue(); | ||
428 | |||
429 | SendQueue.Enqueue(qpack); | ||
430 | TotalThrottle.AddBytes(qpack.Length); | ||
431 | CloudThrottle.AddBytes(qpack.Length); | ||
432 | qchanged = true; | ||
433 | |||
434 | if (CloudOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Cloud)) | ||
435 | Empty.Add(ThrottleOutPacketType.Cloud); | ||
436 | } | ||
437 | |||
438 | if ((TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) && TaskThrottle.UnderLimit()) | ||
439 | { | ||
440 | LLQueItem qpack; | ||
441 | if (TaskOutgoingPacketQueue.Count > 0) | ||
442 | { | ||
443 | qpack = TaskOutgoingPacketQueue.Dequeue(); | ||
444 | SendQueue.PriorityEnqueue(qpack); | ||
445 | } | ||
446 | else | ||
447 | { | ||
448 | qpack = TaskLowpriorityPacketQueue.Dequeue(); | ||
449 | SendQueue.Enqueue(qpack); | ||
450 | } | ||
451 | |||
452 | TotalThrottle.AddBytes(qpack.Length); | ||
453 | TaskThrottle.AddBytes(qpack.Length); | ||
454 | qchanged = true; | ||
455 | |||
456 | if (TaskOutgoingPacketQueue.Count == 0 && TaskLowpriorityPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Task)) | ||
457 | Empty.Add(ThrottleOutPacketType.Task); | ||
458 | } | ||
459 | |||
460 | if ((TextureOutgoingPacketQueue.Count > 0) && TextureThrottle.UnderLimit()) | ||
461 | { | ||
462 | LLQueItem qpack = TextureOutgoingPacketQueue.Dequeue(); | ||
463 | |||
464 | SendQueue.Enqueue(qpack); | ||
465 | TotalThrottle.AddBytes(qpack.Length); | ||
466 | TextureThrottle.AddBytes(qpack.Length); | ||
467 | qchanged = true; | ||
468 | |||
469 | if (TextureOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Texture)) | ||
470 | Empty.Add(ThrottleOutPacketType.Texture); | ||
471 | } | ||
472 | |||
473 | if ((AssetOutgoingPacketQueue.Count > 0) && AssetThrottle.UnderLimit()) | ||
474 | { | ||
475 | LLQueItem qpack = AssetOutgoingPacketQueue.Dequeue(); | ||
476 | |||
477 | SendQueue.Enqueue(qpack); | ||
478 | TotalThrottle.AddBytes(qpack.Length); | ||
479 | AssetThrottle.AddBytes(qpack.Length); | ||
480 | qchanged = true; | ||
481 | |||
482 | if (AssetOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Asset)) | ||
483 | Empty.Add(ThrottleOutPacketType.Asset); | ||
484 | } | ||
485 | } | ||
486 | // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets"); | ||
487 | |||
488 | e = new List<ThrottleOutPacketType>(Empty); | ||
489 | Empty.Clear(); | ||
490 | } | ||
491 | |||
492 | foreach (ThrottleOutPacketType t in e) | ||
493 | { | ||
494 | if (GetQueueCount(t) == 0) | ||
495 | TriggerOnQueueEmpty(t); | ||
496 | } | ||
497 | } | ||
498 | |||
499 | private void TriggerOnQueueEmpty(ThrottleOutPacketType queue) | ||
500 | { | ||
501 | QueueEmpty handlerQueueEmpty = OnQueueEmpty; | ||
502 | |||
503 | if (handlerQueueEmpty != null) | ||
504 | handlerQueueEmpty(queue); | ||
505 | } | ||
506 | |||
507 | private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e) | ||
508 | { | ||
509 | // just to change the signature, and that ProcessThrottle | ||
510 | // will be used elsewhere possibly | ||
511 | ProcessThrottle(); | ||
512 | } | ||
513 | |||
514 | private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue<LLQueItem> q, LLQueItem item, ThrottleOutPacketType itemType) | ||
515 | { | ||
516 | // The idea.. is if the packet throttle queues are empty | ||
517 | // and the client is under throttle for the type. Queue | ||
518 | // it up directly. This basically short cuts having to | ||
519 | // wait for the timer to fire to put things into the | ||
520 | // output queue | ||
521 | |||
522 | if ((q.Count == 0) && (throttle.UnderLimit())) | ||
523 | { | ||
524 | try | ||
525 | { | ||
526 | Monitor.Enter(this); | ||
527 | throttle.AddBytes(item.Length); | ||
528 | TotalThrottle.AddBytes(item.Length); | ||
529 | SendQueue.Enqueue(item); | ||
530 | lock (this) | ||
531 | { | ||
532 | if (!Empty.Contains(itemType)) | ||
533 | Empty.Add(itemType); | ||
534 | } | ||
535 | } | ||
536 | catch (Exception e) | ||
537 | { | ||
538 | // Probably a serialization exception | ||
539 | m_log.WarnFormat("ThrottleCheck: {0}", e.ToString()); | ||
540 | } | ||
541 | finally | ||
542 | { | ||
543 | Monitor.Pulse(this); | ||
544 | Monitor.Exit(this); | ||
545 | } | ||
546 | } | ||
547 | else | ||
548 | { | ||
549 | q.Enqueue(item); | ||
550 | } | ||
551 | } | ||
552 | |||
553 | private static int ScaleThrottle(int value, int curmax, int newmax) | ||
554 | { | ||
555 | return (int)((value / (float)curmax) * newmax); | ||
556 | } | ||
557 | |||
558 | public byte[] GetThrottlesPacked(float multiplier) | ||
559 | { | ||
560 | int singlefloat = 4; | ||
561 | float tResend = ResendThrottle.Throttle*multiplier; | ||
562 | float tLand = LandThrottle.Throttle*multiplier; | ||
563 | float tWind = WindThrottle.Throttle*multiplier; | ||
564 | float tCloud = CloudThrottle.Throttle*multiplier; | ||
565 | float tTask = TaskThrottle.Throttle*multiplier; | ||
566 | float tTexture = TextureThrottle.Throttle*multiplier; | ||
567 | float tAsset = AssetThrottle.Throttle*multiplier; | ||
568 | |||
569 | byte[] throttles = new byte[singlefloat*7]; | ||
570 | int i = 0; | ||
571 | Buffer.BlockCopy(BitConverter.GetBytes(tResend), 0, throttles, singlefloat*i, singlefloat); | ||
572 | i++; | ||
573 | Buffer.BlockCopy(BitConverter.GetBytes(tLand), 0, throttles, singlefloat*i, singlefloat); | ||
574 | i++; | ||
575 | Buffer.BlockCopy(BitConverter.GetBytes(tWind), 0, throttles, singlefloat*i, singlefloat); | ||
576 | i++; | ||
577 | Buffer.BlockCopy(BitConverter.GetBytes(tCloud), 0, throttles, singlefloat*i, singlefloat); | ||
578 | i++; | ||
579 | Buffer.BlockCopy(BitConverter.GetBytes(tTask), 0, throttles, singlefloat*i, singlefloat); | ||
580 | i++; | ||
581 | Buffer.BlockCopy(BitConverter.GetBytes(tTexture), 0, throttles, singlefloat*i, singlefloat); | ||
582 | i++; | ||
583 | Buffer.BlockCopy(BitConverter.GetBytes(tAsset), 0, throttles, singlefloat*i, singlefloat); | ||
584 | |||
585 | return throttles; | ||
586 | } | ||
587 | |||
588 | public void SetThrottleFromClient(byte[] throttle) | ||
589 | { | ||
590 | // From mantis http://opensimulator.org/mantis/view.php?id=1374 | ||
591 | // it appears that sometimes we are receiving empty throttle byte arrays. | ||
592 | // TODO: Investigate this behaviour | ||
593 | if (throttle.Length == 0) | ||
594 | { | ||
595 | m_log.Warn("[PACKET QUEUE]: SetThrottleFromClient unexpectedly received a throttle byte array containing no elements!"); | ||
596 | return; | ||
597 | } | ||
598 | |||
599 | int tResend = -1; | ||
600 | int tLand = -1; | ||
601 | int tWind = -1; | ||
602 | int tCloud = -1; | ||
603 | int tTask = -1; | ||
604 | int tTexture = -1; | ||
605 | int tAsset = -1; | ||
606 | int tall = -1; | ||
607 | int singlefloat = 4; | ||
608 | |||
609 | //Agent Throttle Block contains 7 single floatingpoint values. | ||
610 | int j = 0; | ||
611 | |||
612 | // Some Systems may be big endian... | ||
613 | // it might be smart to do this check more often... | ||
614 | if (!BitConverter.IsLittleEndian) | ||
615 | for (int i = 0; i < 7; i++) | ||
616 | Array.Reverse(throttle, j + i*singlefloat, singlefloat); | ||
617 | |||
618 | // values gotten from OpenMetaverse.org/wiki/Throttle. Thanks MW_ | ||
619 | // bytes | ||
620 | // Convert to integer, since.. the full fp space isn't used. | ||
621 | tResend = (int) BitConverter.ToSingle(throttle, j); | ||
622 | j += singlefloat; | ||
623 | tLand = (int) BitConverter.ToSingle(throttle, j); | ||
624 | j += singlefloat; | ||
625 | tWind = (int) BitConverter.ToSingle(throttle, j); | ||
626 | j += singlefloat; | ||
627 | tCloud = (int) BitConverter.ToSingle(throttle, j); | ||
628 | j += singlefloat; | ||
629 | tTask = (int) BitConverter.ToSingle(throttle, j); | ||
630 | j += singlefloat; | ||
631 | tTexture = (int) BitConverter.ToSingle(throttle, j); | ||
632 | j += singlefloat; | ||
633 | tAsset = (int) BitConverter.ToSingle(throttle, j); | ||
634 | |||
635 | tall = tResend + tLand + tWind + tCloud + tTask + tTexture + tAsset; | ||
636 | |||
637 | /* | ||
638 | m_log.Info("[CLIENT]: Client AgentThrottle - Got throttle:resendbits=" + tResend + | ||
639 | " landbits=" + tLand + | ||
640 | " windbits=" + tWind + | ||
641 | " cloudbits=" + tCloud + | ||
642 | " taskbits=" + tTask + | ||
643 | " texturebits=" + tTexture + | ||
644 | " Assetbits=" + tAsset + | ||
645 | " Allbits=" + tall); | ||
646 | */ | ||
647 | |||
648 | |||
649 | // Total Sanity | ||
650 | // Make sure that the client sent sane total values. | ||
651 | |||
652 | // If the client didn't send acceptable values.... | ||
653 | // Scale the clients values down until they are acceptable. | ||
654 | |||
655 | if (tall <= TotalThrottle.Max) | ||
656 | { | ||
657 | ResendThrottle.Throttle = tResend; | ||
658 | LandThrottle.Throttle = tLand; | ||
659 | WindThrottle.Throttle = tWind; | ||
660 | CloudThrottle.Throttle = tCloud; | ||
661 | TaskThrottle.Throttle = tTask; | ||
662 | TextureThrottle.Throttle = tTexture; | ||
663 | AssetThrottle.Throttle = tAsset; | ||
664 | TotalThrottle.Throttle = tall; | ||
665 | } | ||
666 | // else if (tall < 1) | ||
667 | // { | ||
668 | // // client is stupid, penalize him by minning everything | ||
669 | // ResendThrottle.Throttle = ResendThrottle.Min; | ||
670 | // LandThrottle.Throttle = LandThrottle.Min; | ||
671 | // WindThrottle.Throttle = WindThrottle.Min; | ||
672 | // CloudThrottle.Throttle = CloudThrottle.Min; | ||
673 | // TaskThrottle.Throttle = TaskThrottle.Min; | ||
674 | // TextureThrottle.Throttle = TextureThrottle.Min; | ||
675 | // AssetThrottle.Throttle = AssetThrottle.Min; | ||
676 | // TotalThrottle.Throttle = TotalThrottle.Min; | ||
677 | // } | ||
678 | else | ||
679 | { | ||
680 | // we're over so figure out percentages and use those | ||
681 | ResendThrottle.Throttle = tResend; | ||
682 | |||
683 | LandThrottle.Throttle = ScaleThrottle(tLand, tall, TotalThrottle.Max); | ||
684 | WindThrottle.Throttle = ScaleThrottle(tWind, tall, TotalThrottle.Max); | ||
685 | CloudThrottle.Throttle = ScaleThrottle(tCloud, tall, TotalThrottle.Max); | ||
686 | TaskThrottle.Throttle = ScaleThrottle(tTask, tall, TotalThrottle.Max); | ||
687 | TextureThrottle.Throttle = ScaleThrottle(tTexture, tall, TotalThrottle.Max); | ||
688 | AssetThrottle.Throttle = ScaleThrottle(tAsset, tall, TotalThrottle.Max); | ||
689 | TotalThrottle.Throttle = TotalThrottle.Max; | ||
690 | } | ||
691 | // effectively wiggling the slider causes things reset | ||
692 | // ResetCounters(); // DO NOT reset, better to send less for one period than more | ||
693 | } | ||
694 | |||
695 | // See IPullStatsProvider | ||
696 | public string GetStats() | ||
697 | { | ||
698 | return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}", | ||
699 | SendQueue.Count(), | ||
700 | IncomingPacketQueue.Count, | ||
701 | OutgoingPacketQueue.Count, | ||
702 | ResendOutgoingPacketQueue.Count, | ||
703 | LandOutgoingPacketQueue.Count, | ||
704 | WindOutgoingPacketQueue.Count, | ||
705 | CloudOutgoingPacketQueue.Count, | ||
706 | TaskOutgoingPacketQueue.Count, | ||
707 | TextureOutgoingPacketQueue.Count, | ||
708 | AssetOutgoingPacketQueue.Count); | ||
709 | } | ||
710 | |||
711 | public LLQueItem[] GetQueueArray() | ||
712 | { | ||
713 | return SendQueue.GetQueueArray(); | ||
714 | } | ||
715 | |||
716 | public float ThrottleMultiplier | ||
717 | { | ||
718 | get { return throttleMultiplier; } | ||
719 | } | ||
720 | |||
721 | public int GetQueueCount(ThrottleOutPacketType queue) | ||
722 | { | ||
723 | switch (queue) | ||
724 | { | ||
725 | case ThrottleOutPacketType.Land: | ||
726 | return LandOutgoingPacketQueue.Count; | ||
727 | case ThrottleOutPacketType.Wind: | ||
728 | return WindOutgoingPacketQueue.Count; | ||
729 | case ThrottleOutPacketType.Cloud: | ||
730 | return CloudOutgoingPacketQueue.Count; | ||
731 | case ThrottleOutPacketType.Task: | ||
732 | return TaskOutgoingPacketQueue.Count; | ||
733 | case ThrottleOutPacketType.Texture: | ||
734 | return TextureOutgoingPacketQueue.Count; | ||
735 | case ThrottleOutPacketType.Asset: | ||
736 | return AssetOutgoingPacketQueue.Count; | ||
737 | } | ||
738 | |||
739 | return 0; | ||
740 | } | ||
741 | } | ||
742 | } | ||