diff options
author | Sean Dague | 2007-12-07 21:30:01 +0000 |
---|---|---|
committer | Sean Dague | 2007-12-07 21:30:01 +0000 |
commit | 0aa982c25271f91e1b3002022d3e02e55707d476 (patch) | |
tree | 8ca6318c6ef76140887a08d4c99186d2f72b2c26 /OpenSim | |
parent | further screwing around with the PacketQueue data structure. (diff) | |
download | opensim-SC-0aa982c25271f91e1b3002022d3e02e55707d476.zip opensim-SC-0aa982c25271f91e1b3002022d3e02e55707d476.tar.gz opensim-SC-0aa982c25271f91e1b3002022d3e02e55707d476.tar.bz2 opensim-SC-0aa982c25271f91e1b3002022d3e02e55707d476.tar.xz |
move to PacketQueue for throttling. This has been tested with a couple
of people, but is enough of a change that more should try it out. This
removes 500 lines from ClientView.cs in the process.
Diffstat (limited to 'OpenSim')
-rw-r--r-- | OpenSim/Region/ClientStack/ClientView.cs | 529 | ||||
-rw-r--r-- | OpenSim/Region/ClientStack/PacketQueue.cs | 142 |
2 files changed, 50 insertions, 621 deletions
diff --git a/OpenSim/Region/ClientStack/ClientView.cs b/OpenSim/Region/ClientStack/ClientView.cs index b2db4cb..2ec4fef 100644 --- a/OpenSim/Region/ClientStack/ClientView.cs +++ b/OpenSim/Region/ClientStack/ClientView.cs | |||
@@ -96,58 +96,6 @@ namespace OpenSim.Region.ClientStack | |||
96 | private int probesWithNoIngressPackets = 0; | 96 | private int probesWithNoIngressPackets = 0; |
97 | private int lastPacketsReceived = 0; | 97 | private int lastPacketsReceived = 0; |
98 | 98 | ||
99 | // 1536000 | ||
100 | private int throttleOutboundMax = 1536000; // Number of bytes allowed to go out per second. (256kbps per client) | ||
101 | // TODO: Make this variable. Lower throttle on un-ack. Raise over time? | ||
102 | private int bytesSent = 0; // Number of bytes sent this period | ||
103 | |||
104 | private int throttleOutbound = 162144; // Number of bytes allowed to go out per second. (256kbps per client) | ||
105 | // TODO: Make this variable. Lower throttle on un-ack. Raise over time | ||
106 | |||
107 | // All throttle times and number of bytes are calculated by dividing by this value | ||
108 | // This value also determines how many times per throttletimems the timer will run | ||
109 | // If throttleimems is 1000 ms, then the timer will fire every 1000/7 milliseconds | ||
110 | |||
111 | private int throttleTimeDivisor = 7; | ||
112 | |||
113 | private int throttletimems = 1000; | ||
114 | |||
115 | // Maximum -per type- throttle | ||
116 | private int ResendthrottleMAX = 100000; | ||
117 | private int LandthrottleMax = 100000; | ||
118 | private int WindthrottleMax = 100000; | ||
119 | private int CloudthrottleMax = 100000; | ||
120 | private int TaskthrottleMax = 800000; | ||
121 | private int AssetthrottleMax = 800000; | ||
122 | private int TexturethrottleMax = 800000; | ||
123 | |||
124 | // Minimum -per type- throttle | ||
125 | private int ResendthrottleMin = 5000; // setting resendmin to 0 results in mostly dropped packets | ||
126 | private int LandthrottleMin = 1000; | ||
127 | private int WindthrottleMin = 1000; | ||
128 | private int CloudthrottleMin = 1000; | ||
129 | private int TaskthrottleMin = 1000; | ||
130 | private int AssetthrottleMin = 1000; | ||
131 | private int TexturethrottleMin = 1000; | ||
132 | |||
133 | // Sim default per-client settings. | ||
134 | private int ResendthrottleOutbound = 50000; | ||
135 | private int ResendBytesSent = 0; | ||
136 | private int LandthrottleOutbound = 100000; | ||
137 | private int LandBytesSent = 0; | ||
138 | private int WindthrottleOutbound = 10000; | ||
139 | private int WindBytesSent = 0; | ||
140 | private int CloudthrottleOutbound = 5000; | ||
141 | private int CloudBytesSent = 0; | ||
142 | private int TaskthrottleOutbound = 100000; | ||
143 | private int TaskBytesSent = 0; | ||
144 | private int AssetthrottleOutbound = 80000; | ||
145 | private int AssetBytesSent = 0; | ||
146 | private int TexturethrottleOutbound = 100000; | ||
147 | private int TextureBytesSent = 0; | ||
148 | |||
149 | private Timer throttleTimer; | ||
150 | |||
151 | public ClientView(EndPoint remoteEP, UseCircuitCodePacket initialcirpack, ClientManager clientManager, | 99 | public ClientView(EndPoint remoteEP, UseCircuitCodePacket initialcirpack, ClientManager clientManager, |
152 | IScene scene, AssetCache assetCache, PacketServer packServer, | 100 | IScene scene, AssetCache assetCache, PacketServer packServer, |
153 | AgentCircuitManager authenSessions) | 101 | AgentCircuitManager authenSessions) |
@@ -168,34 +116,18 @@ namespace OpenSim.Region.ClientStack | |||
168 | 116 | ||
169 | startpos = m_authenticateSessionsHandler.GetPosition(initialcirpack.CircuitCode.Code); | 117 | startpos = m_authenticateSessionsHandler.GetPosition(initialcirpack.CircuitCode.Code); |
170 | 118 | ||
171 | |||
172 | // While working on this, the BlockingQueue had me fooled for a bit. | 119 | // While working on this, the BlockingQueue had me fooled for a bit. |
173 | // The Blocking queue causes the thread to stop until there's something | 120 | // The Blocking queue causes the thread to stop until there's something |
174 | // in it to process. it's an on-purpose threadlock though because | 121 | // in it to process. it's an on-purpose threadlock though because |
175 | // without it, the clientloop will suck up all sim resources. | 122 | // without it, the clientloop will suck up all sim resources. |
176 | 123 | ||
177 | PacketQueue = new BlockingQueue<QueItem>(); | 124 | PacketQueue = new PacketQueue(); |
178 | |||
179 | IncomingPacketQueue = new Queue<QueItem>(); | ||
180 | OutgoingPacketQueue = new Queue<QueItem>(); | ||
181 | ResendOutgoingPacketQueue = new Queue<QueItem>(); | ||
182 | LandOutgoingPacketQueue = new Queue<QueItem>(); | ||
183 | WindOutgoingPacketQueue = new Queue<QueItem>(); | ||
184 | CloudOutgoingPacketQueue = new Queue<QueItem>(); | ||
185 | TaskOutgoingPacketQueue = new Queue<QueItem>(); | ||
186 | TextureOutgoingPacketQueue = new Queue<QueItem>(); | ||
187 | AssetOutgoingPacketQueue = new Queue<QueItem>(); | ||
188 | |||
189 | 125 | ||
190 | //this.UploadAssets = new AgentAssetUpload(this, m_assetCache, m_inventoryCache); | 126 | //this.UploadAssets = new AgentAssetUpload(this, m_assetCache, m_inventoryCache); |
191 | AckTimer = new Timer(750); | 127 | AckTimer = new Timer(750); |
192 | AckTimer.Elapsed += new ElapsedEventHandler(AckTimer_Elapsed); | 128 | AckTimer.Elapsed += new ElapsedEventHandler(AckTimer_Elapsed); |
193 | AckTimer.Start(); | 129 | AckTimer.Start(); |
194 | 130 | ||
195 | throttleTimer = new Timer((int)(throttletimems/throttleTimeDivisor)); | ||
196 | throttleTimer.Elapsed += new ElapsedEventHandler(throttleTimer_Elapsed); | ||
197 | throttleTimer.Start(); | ||
198 | |||
199 | RegisterLocalPacketHandlers(); | 131 | RegisterLocalPacketHandlers(); |
200 | 132 | ||
201 | ClientThread = new Thread(new ThreadStart(AuthUser)); | 133 | ClientThread = new Thread(new ThreadStart(AuthUser)); |
@@ -203,100 +135,6 @@ namespace OpenSim.Region.ClientStack | |||
203 | ClientThread.Start(); | 135 | ClientThread.Start(); |
204 | } | 136 | } |
205 | 137 | ||
206 | void throttleTimer_Elapsed(object sender, ElapsedEventArgs e) | ||
207 | { | ||
208 | bytesSent = 0; | ||
209 | ResendBytesSent = 0; | ||
210 | LandBytesSent = 0; | ||
211 | WindBytesSent = 0; | ||
212 | CloudBytesSent = 0; | ||
213 | TaskBytesSent = 0; | ||
214 | AssetBytesSent = 0; | ||
215 | TextureBytesSent = 0; | ||
216 | |||
217 | // I was considering this.. Will an event fire if the thread it's on is blocked? | ||
218 | |||
219 | // Then I figured out.. it doesn't really matter.. because this thread won't be blocked for long | ||
220 | // The General overhead of the UDP protocol gets sent to the queue un-throttled by this | ||
221 | // so This'll pick up about around the right time. | ||
222 | |||
223 | int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once. | ||
224 | int throttleLoops = 0; | ||
225 | |||
226 | // We're going to dequeue all of the saved up packets until | ||
227 | // we've hit the throttle limit or there's no more packets to send | ||
228 | lock (throttleTimer) { | ||
229 | while ((bytesSent <= ((int)(throttleOutbound/throttleTimeDivisor)) && | ||
230 | (ResendOutgoingPacketQueue.Count > 0 || | ||
231 | LandOutgoingPacketQueue.Count > 0 || | ||
232 | WindOutgoingPacketQueue.Count > 0 || | ||
233 | CloudOutgoingPacketQueue.Count > 0 || | ||
234 | TaskOutgoingPacketQueue.Count > 0 || | ||
235 | AssetOutgoingPacketQueue.Count > 0 || | ||
236 | TextureOutgoingPacketQueue.Count > 0)) && throttleLoops <= MaxThrottleLoops) | ||
237 | { | ||
238 | throttleLoops++; | ||
239 | //Now comes the fun part.. we dump all our elements into PacketQueue that we've saved up. | ||
240 | if (ResendBytesSent <= ((int)(ResendthrottleOutbound/throttleTimeDivisor)) && ResendOutgoingPacketQueue.Count > 0) | ||
241 | { | ||
242 | QueItem qpack = ResendOutgoingPacketQueue.Dequeue(); | ||
243 | |||
244 | PacketQueue.Enqueue(qpack); | ||
245 | bytesSent += qpack.Packet.ToBytes().Length; | ||
246 | ResendBytesSent += qpack.Packet.ToBytes().Length; | ||
247 | } | ||
248 | if (LandBytesSent <= ((int)(LandthrottleOutbound/throttleTimeDivisor)) && LandOutgoingPacketQueue.Count > 0) | ||
249 | { | ||
250 | QueItem qpack = LandOutgoingPacketQueue.Dequeue(); | ||
251 | |||
252 | PacketQueue.Enqueue(qpack); | ||
253 | bytesSent += qpack.Packet.ToBytes().Length; | ||
254 | LandBytesSent += qpack.Packet.ToBytes().Length; | ||
255 | } | ||
256 | if (WindBytesSent <= ((int)(WindthrottleOutbound/throttleTimeDivisor)) && WindOutgoingPacketQueue.Count > 0) | ||
257 | { | ||
258 | QueItem qpack = WindOutgoingPacketQueue.Dequeue(); | ||
259 | |||
260 | PacketQueue.Enqueue(qpack); | ||
261 | bytesSent += qpack.Packet.ToBytes().Length; | ||
262 | WindBytesSent += qpack.Packet.ToBytes().Length; | ||
263 | } | ||
264 | if (CloudBytesSent <= ((int)(CloudthrottleOutbound/throttleTimeDivisor)) && CloudOutgoingPacketQueue.Count > 0) | ||
265 | { | ||
266 | QueItem qpack = CloudOutgoingPacketQueue.Dequeue(); | ||
267 | |||
268 | PacketQueue.Enqueue(qpack); | ||
269 | bytesSent += qpack.Packet.ToBytes().Length; | ||
270 | CloudBytesSent += qpack.Packet.ToBytes().Length; | ||
271 | } | ||
272 | if (TaskBytesSent <= ((int)(TaskthrottleOutbound/throttleTimeDivisor)) && TaskOutgoingPacketQueue.Count > 0) | ||
273 | { | ||
274 | QueItem qpack = TaskOutgoingPacketQueue.Dequeue(); | ||
275 | |||
276 | PacketQueue.Enqueue(qpack); | ||
277 | bytesSent += qpack.Packet.ToBytes().Length; | ||
278 | TaskBytesSent += qpack.Packet.ToBytes().Length; | ||
279 | } | ||
280 | if (TextureBytesSent <= ((int)(TexturethrottleOutbound/throttleTimeDivisor)) && TextureOutgoingPacketQueue.Count > 0) | ||
281 | { | ||
282 | QueItem qpack = TextureOutgoingPacketQueue.Dequeue(); | ||
283 | |||
284 | PacketQueue.Enqueue(qpack); | ||
285 | bytesSent += qpack.Packet.ToBytes().Length; | ||
286 | TextureBytesSent += qpack.Packet.ToBytes().Length; | ||
287 | } | ||
288 | if (AssetBytesSent <= ((int)(AssetthrottleOutbound/throttleTimeDivisor)) && AssetOutgoingPacketQueue.Count > 0) | ||
289 | { | ||
290 | QueItem qpack = AssetOutgoingPacketQueue.Dequeue(); | ||
291 | |||
292 | PacketQueue.Enqueue(qpack); | ||
293 | bytesSent += qpack.Packet.ToBytes().Length; | ||
294 | AssetBytesSent += qpack.Packet.ToBytes().Length; | ||
295 | } | ||
296 | } | ||
297 | } | ||
298 | } | ||
299 | |||
300 | public LLUUID SessionId | 138 | public LLUUID SessionId |
301 | { | 139 | { |
302 | get { return m_sessionId; } | 140 | get { return m_sessionId; } |
@@ -315,6 +153,8 @@ namespace OpenSim.Region.ClientStack | |||
315 | 153 | ||
316 | m_scene.RemoveClient(AgentId); | 154 | m_scene.RemoveClient(AgentId); |
317 | 155 | ||
156 | PacketQueue.Close(); | ||
157 | |||
318 | ClientThread.Abort(); | 158 | ClientThread.Abort(); |
319 | } | 159 | } |
320 | public void Kick(string message) | 160 | public void Kick(string message) |
@@ -429,16 +269,12 @@ namespace OpenSim.Region.ClientStack | |||
429 | 269 | ||
430 | protected virtual void ClientLoop() | 270 | protected virtual void ClientLoop() |
431 | { | 271 | { |
432 | bool queuedLast = false; | ||
433 | |||
434 | MainLog.Instance.Verbose("CLIENT", "Entered loop"); | 272 | MainLog.Instance.Verbose("CLIENT", "Entered loop"); |
435 | while (true) | 273 | while (true) |
436 | { | 274 | { |
437 | QueItem nextPacket = PacketQueue.Dequeue(); | 275 | QueItem nextPacket = PacketQueue.Dequeue(); |
438 | if (nextPacket.Incoming) | 276 | if (nextPacket.Incoming) |
439 | { | 277 | { |
440 | queuedLast = false; | ||
441 | |||
442 | //is a incoming packet | 278 | //is a incoming packet |
443 | if (nextPacket.Packet.Type != PacketType.AgentUpdate) | 279 | if (nextPacket.Packet.Type != PacketType.AgentUpdate) |
444 | { | 280 | { |
@@ -449,36 +285,8 @@ namespace OpenSim.Region.ClientStack | |||
449 | } | 285 | } |
450 | else | 286 | else |
451 | { | 287 | { |
452 | // Throw it back on the queue if it's going to cause us to flood the client | 288 | DebugPacket("OUT", nextPacket.Packet); |
453 | if (bytesSent > throttleOutboundMax) | 289 | ProcessOutPacket(nextPacket.Packet); |
454 | { | ||
455 | PacketQueue.Enqueue(nextPacket); | ||
456 | MainLog.Instance.Verbose("THROTTLE", "Client over throttle limit, requeuing packet"); | ||
457 | |||
458 | if (queuedLast) | ||
459 | { | ||
460 | MainLog.Instance.Verbose("THROTTLE", "No more sendable packets, need to sleep now"); | ||
461 | Thread.Sleep(100); // Wait a little while if this was the last packet we saw | ||
462 | } | ||
463 | |||
464 | queuedLast = true; | ||
465 | } | ||
466 | else | ||
467 | { | ||
468 | queuedLast = false; | ||
469 | |||
470 | // TODO: May be a bit expensive doing this twice. | ||
471 | |||
472 | //Don't throttle AvatarPickerReplies!, they return a null .ToBytes()! | ||
473 | if (nextPacket.Packet.Type != PacketType.AvatarPickerReply) | ||
474 | bytesSent += nextPacket.Packet.ToBytes().Length; | ||
475 | |||
476 | |||
477 | //is a out going packet | ||
478 | DebugPacket("OUT", nextPacket.Packet); | ||
479 | ProcessOutPacket(nextPacket.Packet); | ||
480 | |||
481 | } | ||
482 | } | 290 | } |
483 | } | 291 | } |
484 | } | 292 | } |
@@ -2207,7 +2015,7 @@ namespace OpenSim.Region.ClientStack | |||
2207 | } | 2015 | } |
2208 | 2016 | ||
2209 | // Previously ClientView.PacketQueue | 2017 | // Previously ClientView.PacketQueue |
2210 | protected BlockingQueue<QueItem> PacketQueue; | 2018 | protected PacketQueue PacketQueue; |
2211 | 2019 | ||
2212 | protected Queue<QueItem> IncomingPacketQueue; | 2020 | protected Queue<QueItem> IncomingPacketQueue; |
2213 | protected Queue<QueItem> OutgoingPacketQueue; | 2021 | protected Queue<QueItem> OutgoingPacketQueue; |
@@ -2400,66 +2208,13 @@ namespace OpenSim.Region.ClientStack | |||
2400 | } | 2208 | } |
2401 | } | 2209 | } |
2402 | 2210 | ||
2403 | private void ThrottleCheck(ref int TypeBytesSent, int Throttle, ref Queue<QueItem> q, QueItem item) | ||
2404 | { | ||
2405 | // The idea.. is if the packet throttle queues are empty | ||
2406 | // and the client is under throttle for the type. Queue | ||
2407 | // it up directly. This basically short cuts having to | ||
2408 | // wait for the timer to fire to put things into the | ||
2409 | // output queue | ||
2410 | |||
2411 | if((q.Count == 0) && (TypeBytesSent <= ((int)(Throttle / throttleTimeDivisor)))) | ||
2412 | { | ||
2413 | bytesSent += item.Packet.ToBytes().Length; | ||
2414 | TypeBytesSent += item.Packet.ToBytes().Length; | ||
2415 | PacketQueue.Enqueue(item); | ||
2416 | } | ||
2417 | else | ||
2418 | { | ||
2419 | q.Enqueue(item); | ||
2420 | } | ||
2421 | } | ||
2422 | |||
2423 | public virtual void OutPacket(Packet NewPack, ThrottleOutPacketType throttlePacketType) | 2211 | public virtual void OutPacket(Packet NewPack, ThrottleOutPacketType throttlePacketType) |
2424 | { | 2212 | { |
2425 | QueItem item = new QueItem(); | 2213 | QueItem item = new QueItem(); |
2426 | item.Packet = NewPack; | 2214 | item.Packet = NewPack; |
2427 | item.Incoming = false; | 2215 | item.Incoming = false; |
2428 | item.throttleType = throttlePacketType; // Packet throttle type | 2216 | item.throttleType = throttlePacketType; // Packet throttle type |
2429 | 2217 | PacketQueue.Enqueue(item); | |
2430 | // The idea.. is if the packet throttle queues are empty and the client is under throttle for the type. | ||
2431 | // Queue it up directly. | ||
2432 | switch (throttlePacketType) | ||
2433 | { | ||
2434 | case ThrottleOutPacketType.Resend: | ||
2435 | ThrottleCheck(ref ResendBytesSent, ResendthrottleOutbound, ref ResendOutgoingPacketQueue, item); | ||
2436 | break; | ||
2437 | case ThrottleOutPacketType.Texture: | ||
2438 | ThrottleCheck(ref TextureBytesSent, TexturethrottleOutbound, ref TextureOutgoingPacketQueue, item); | ||
2439 | break; | ||
2440 | case ThrottleOutPacketType.Task: | ||
2441 | ThrottleCheck(ref TaskBytesSent, TaskthrottleOutbound, ref TaskOutgoingPacketQueue, item); | ||
2442 | break; | ||
2443 | case ThrottleOutPacketType.Land: | ||
2444 | ThrottleCheck(ref LandBytesSent, LandthrottleOutbound, ref LandOutgoingPacketQueue, item); | ||
2445 | break; | ||
2446 | case ThrottleOutPacketType.Asset: | ||
2447 | ThrottleCheck(ref AssetBytesSent, AssetthrottleOutbound, ref AssetOutgoingPacketQueue, item); | ||
2448 | break; | ||
2449 | case ThrottleOutPacketType.Cloud: | ||
2450 | ThrottleCheck(ref CloudBytesSent, CloudthrottleOutbound, ref CloudOutgoingPacketQueue, item); | ||
2451 | break; | ||
2452 | case ThrottleOutPacketType.Wind: | ||
2453 | ThrottleCheck(ref WindBytesSent, WindthrottleOutbound, ref WindOutgoingPacketQueue, item); | ||
2454 | break; | ||
2455 | |||
2456 | default: | ||
2457 | // Acknowledgements and other such stuff should go directly to the blocking Queue | ||
2458 | // Throttling them may and likely 'will' be problematic | ||
2459 | PacketQueue.Enqueue(item); | ||
2460 | break; | ||
2461 | } | ||
2462 | //OutgoingPacketQueue.Enqueue(item); | ||
2463 | } | 2218 | } |
2464 | 2219 | ||
2465 | # region Low Level Packet Methods | 2220 | # region Low Level Packet Methods |
@@ -3333,275 +3088,7 @@ namespace OpenSim.Region.ClientStack | |||
3333 | break; | 3088 | break; |
3334 | 3089 | ||
3335 | case PacketType.AgentThrottle: | 3090 | case PacketType.AgentThrottle: |
3336 | AgentThrottlePacket atpack = (AgentThrottlePacket)Pack; | 3091 | PacketQueue.SetThrottleFromClient(Pack); |
3337 | |||
3338 | byte[] throttle = atpack.Throttle.Throttles; | ||
3339 | int tResend = -1; | ||
3340 | int tLand = -1; | ||
3341 | int tWind = -1; | ||
3342 | int tCloud = -1; | ||
3343 | int tTask = -1; | ||
3344 | int tTexture = -1; | ||
3345 | int tAsset = -1; | ||
3346 | int tall = -1; | ||
3347 | int singlefloat = 4; | ||
3348 | |||
3349 | //Agent Throttle Block contains 7 single floatingpoint values. | ||
3350 | int j = 0; | ||
3351 | |||
3352 | // Some Systems may be big endian... | ||
3353 | // it might be smart to do this check more often... | ||
3354 | if (!BitConverter.IsLittleEndian) | ||
3355 | for (int i = 0; i < 7; i++) | ||
3356 | Array.Reverse(throttle, j + i * singlefloat, singlefloat); | ||
3357 | |||
3358 | // values gotten from libsecondlife.org/wiki/Throttle. Thanks MW_ | ||
3359 | // bytes | ||
3360 | // Convert to integer, since.. the full fp space isn't used. | ||
3361 | tResend = (int)BitConverter.ToSingle(throttle, j); | ||
3362 | j += singlefloat; | ||
3363 | tLand = (int)BitConverter.ToSingle(throttle, j); | ||
3364 | j += singlefloat; | ||
3365 | tWind = (int)BitConverter.ToSingle(throttle, j); | ||
3366 | j += singlefloat; | ||
3367 | tCloud = (int)BitConverter.ToSingle(throttle, j); | ||
3368 | j += singlefloat; | ||
3369 | tTask = (int)BitConverter.ToSingle(throttle, j); | ||
3370 | j += singlefloat; | ||
3371 | tTexture = (int)BitConverter.ToSingle(throttle, j); | ||
3372 | j += singlefloat; | ||
3373 | tAsset = (int)BitConverter.ToSingle(throttle, j); | ||
3374 | |||
3375 | tall = tResend + tLand + tWind + tCloud + tTask + tTexture + tAsset; | ||
3376 | /* | ||
3377 | MainLog.Instance.Verbose("CLIENT", "Client AgentThrottle - Got throttle:resendbytes=" + tResend + | ||
3378 | " landbytes=" + tLand + | ||
3379 | " windbytes=" + tWind + | ||
3380 | " cloudbytes=" + tCloud + | ||
3381 | " taskbytes=" + tTask + | ||
3382 | " texturebytes=" + tTexture + | ||
3383 | " Assetbytes=" + tAsset + | ||
3384 | " Allbytes=" + tall); | ||
3385 | */ | ||
3386 | |||
3387 | // Total Sanity | ||
3388 | // Make sure that the client sent sane total values. | ||
3389 | |||
3390 | // If the client didn't send acceptable values.... | ||
3391 | // Scale the clients values down until they are acceptable. | ||
3392 | |||
3393 | if (tall <= throttleOutboundMax) | ||
3394 | { | ||
3395 | // Sanity | ||
3396 | // Making sure the client sends sane values | ||
3397 | // This gives us a measure of control of the comms | ||
3398 | // Check Max of Type | ||
3399 | // Then Check Min of type | ||
3400 | |||
3401 | // Resend throttle | ||
3402 | if (tResend <= ResendthrottleMAX) | ||
3403 | ResendthrottleOutbound = tResend; | ||
3404 | |||
3405 | if (tResend < ResendthrottleMin) | ||
3406 | ResendthrottleOutbound = ResendthrottleMin; | ||
3407 | |||
3408 | // Land throttle | ||
3409 | if (tLand <= LandthrottleMax) | ||
3410 | LandthrottleOutbound = tLand; | ||
3411 | |||
3412 | if (tLand < LandthrottleMin) | ||
3413 | LandthrottleOutbound = LandthrottleMin; | ||
3414 | |||
3415 | // Wind throttle | ||
3416 | if (tWind <= WindthrottleMax) | ||
3417 | WindthrottleOutbound = tWind; | ||
3418 | |||
3419 | if (tWind < WindthrottleMin) | ||
3420 | WindthrottleOutbound = WindthrottleMin; | ||
3421 | |||
3422 | // Cloud throttle | ||
3423 | if (tCloud <= CloudthrottleMax) | ||
3424 | CloudthrottleOutbound = tCloud; | ||
3425 | |||
3426 | if (tCloud < CloudthrottleMin) | ||
3427 | CloudthrottleOutbound = CloudthrottleMin; | ||
3428 | |||
3429 | // Task throttle | ||
3430 | if (tTask <= TaskthrottleMax) | ||
3431 | TaskthrottleOutbound = tTask; | ||
3432 | |||
3433 | if (tTask < TaskthrottleMin) | ||
3434 | TaskthrottleOutbound = TaskthrottleMin; | ||
3435 | |||
3436 | // Texture throttle | ||
3437 | if (tTexture <= TexturethrottleMax) | ||
3438 | TexturethrottleOutbound = tTexture; | ||
3439 | |||
3440 | if (tTexture < TexturethrottleMin) | ||
3441 | TexturethrottleOutbound = TexturethrottleMin; | ||
3442 | |||
3443 | //Asset throttle | ||
3444 | if (tAsset <= AssetthrottleMax) | ||
3445 | AssetthrottleOutbound = tAsset; | ||
3446 | |||
3447 | if (tAsset < AssetthrottleMin) | ||
3448 | AssetthrottleOutbound = AssetthrottleMin; | ||
3449 | |||
3450 | /* MainLog.Instance.Verbose("THROTTLE", "Using:resendbytes=" + ResendthrottleOutbound + | ||
3451 | " landbytes=" + LandthrottleOutbound + | ||
3452 | " windbytes=" + WindthrottleOutbound + | ||
3453 | " cloudbytes=" + CloudthrottleOutbound + | ||
3454 | " taskbytes=" + TaskthrottleOutbound + | ||
3455 | " texturebytes=" + TexturethrottleOutbound + | ||
3456 | " Assetbytes=" + AssetthrottleOutbound + | ||
3457 | " Allbytes=" + tall); | ||
3458 | */ | ||
3459 | } | ||
3460 | else | ||
3461 | { | ||
3462 | // The client didn't send acceptable values.. | ||
3463 | // so it's our job now to turn them into acceptable values | ||
3464 | // We're going to first scale the values down | ||
3465 | // After that we're going to check if the scaled values are sane | ||
3466 | |||
3467 | // We're going to be dividing by a user value.. so make sure | ||
3468 | // we don't get a divide by zero error. | ||
3469 | if (tall > 0) | ||
3470 | { | ||
3471 | // Find out the percentage of all communications | ||
3472 | // the client requests for each type. We'll keep resend at | ||
3473 | // it's client recommended level (won't scale it down) | ||
3474 | // unless it's beyond sane values itself. | ||
3475 | |||
3476 | if (tResend <= ResendthrottleMAX) | ||
3477 | { | ||
3478 | // This is nexted because we only want to re-set the values | ||
3479 | // the packet throttler uses once. | ||
3480 | |||
3481 | if (tResend >= ResendthrottleMin) | ||
3482 | { | ||
3483 | ResendthrottleOutbound = tResend; | ||
3484 | } | ||
3485 | else | ||
3486 | { | ||
3487 | ResendthrottleOutbound = ResendthrottleMin; | ||
3488 | } | ||
3489 | } | ||
3490 | else | ||
3491 | { | ||
3492 | ResendthrottleOutbound = ResendthrottleMAX; | ||
3493 | } | ||
3494 | |||
3495 | |||
3496 | // Getting Percentages of communication for each type of data | ||
3497 | float LandPercent = (float)(tLand / tall); | ||
3498 | float WindPercent = (float)(tWind / tall); | ||
3499 | float CloudPercent = (float)(tCloud / tall); | ||
3500 | float TaskPercent = (float)(tTask / tall); | ||
3501 | float TexturePercent = (float)(tTexture / tall); | ||
3502 | float AssetPercent = (float)(tAsset / tall); | ||
3503 | |||
3504 | // Okay.. now we've got the percentages of total communication. | ||
3505 | // Apply them to a new max total | ||
3506 | |||
3507 | int tLandResult = (int)(LandPercent * throttleOutboundMax); | ||
3508 | int tWindResult = (int)(WindPercent * throttleOutboundMax); | ||
3509 | int tCloudResult = (int)(CloudPercent * throttleOutboundMax); | ||
3510 | int tTaskResult = (int)(TaskPercent * throttleOutboundMax); | ||
3511 | int tTextureResult = (int)(TexturePercent * throttleOutboundMax); | ||
3512 | int tAssetResult = (int)(AssetPercent * throttleOutboundMax); | ||
3513 | |||
3514 | // Now we have to check our scaled values for sanity | ||
3515 | |||
3516 | // Check Max of Type | ||
3517 | // Then Check Min of type | ||
3518 | |||
3519 | // Land throttle | ||
3520 | if (tLandResult <= LandthrottleMax) | ||
3521 | LandthrottleOutbound = tLandResult; | ||
3522 | |||
3523 | if (tLandResult < LandthrottleMin) | ||
3524 | LandthrottleOutbound = LandthrottleMin; | ||
3525 | |||
3526 | // Wind throttle | ||
3527 | if (tWindResult <= WindthrottleMax) | ||
3528 | WindthrottleOutbound = tWindResult; | ||
3529 | |||
3530 | if (tWindResult < WindthrottleMin) | ||
3531 | WindthrottleOutbound = WindthrottleMin; | ||
3532 | |||
3533 | // Cloud throttle | ||
3534 | if (tCloudResult <= CloudthrottleMax) | ||
3535 | CloudthrottleOutbound = tCloudResult; | ||
3536 | |||
3537 | if (tCloudResult < CloudthrottleMin) | ||
3538 | CloudthrottleOutbound = CloudthrottleMin; | ||
3539 | |||
3540 | // Task throttle | ||
3541 | if (tTaskResult <= TaskthrottleMax) | ||
3542 | TaskthrottleOutbound = tTaskResult; | ||
3543 | |||
3544 | if (tTaskResult < TaskthrottleMin) | ||
3545 | TaskthrottleOutbound = TaskthrottleMin; | ||
3546 | |||
3547 | // Texture throttle | ||
3548 | if (tTextureResult <= TexturethrottleMax) | ||
3549 | TexturethrottleOutbound = tTextureResult; | ||
3550 | |||
3551 | if (tTextureResult < TexturethrottleMin) | ||
3552 | TexturethrottleOutbound = TexturethrottleMin; | ||
3553 | |||
3554 | //Asset throttle | ||
3555 | if (tAssetResult <= AssetthrottleMax) | ||
3556 | AssetthrottleOutbound = tAssetResult; | ||
3557 | |||
3558 | if (tAssetResult < AssetthrottleMin) | ||
3559 | AssetthrottleOutbound = AssetthrottleMin; | ||
3560 | |||
3561 | /* MainLog.Instance.Verbose("THROTTLE", "Using:resendbytes=" + ResendthrottleOutbound + | ||
3562 | " landbytes=" + LandthrottleOutbound + | ||
3563 | " windbytes=" + WindthrottleOutbound + | ||
3564 | " cloudbytes=" + CloudthrottleOutbound + | ||
3565 | " taskbytes=" + TaskthrottleOutbound + | ||
3566 | " texturebytes=" + TexturethrottleOutbound + | ||
3567 | " Assetbytes=" + AssetthrottleOutbound + | ||
3568 | " Allbytes=" + tall); | ||
3569 | */ | ||
3570 | } | ||
3571 | else | ||
3572 | { | ||
3573 | // The client sent a stupid value.. | ||
3574 | // We're going to set the throttles to the minimum possible | ||
3575 | ResendthrottleOutbound = ResendthrottleMin; | ||
3576 | LandthrottleOutbound = LandthrottleMin; | ||
3577 | WindthrottleOutbound = WindthrottleMin; | ||
3578 | CloudthrottleOutbound = CloudthrottleMin; | ||
3579 | TaskthrottleOutbound = TaskthrottleMin; | ||
3580 | TexturethrottleOutbound = TexturethrottleMin; | ||
3581 | AssetthrottleOutbound = AssetthrottleMin; | ||
3582 | MainLog.Instance.Verbose("THROTTLE", "ClientSentBadThrottle Using:resendbytes=" + ResendthrottleOutbound + | ||
3583 | " landbytes=" + LandthrottleOutbound + | ||
3584 | " windbytes=" + WindthrottleOutbound + | ||
3585 | " cloudbytes=" + CloudthrottleOutbound + | ||
3586 | " taskbytes=" + TaskthrottleOutbound + | ||
3587 | " texturebytes=" + TexturethrottleOutbound + | ||
3588 | " Assetbytes=" + AssetthrottleOutbound + | ||
3589 | " Allbytes=" + tall); | ||
3590 | } | ||
3591 | } | ||
3592 | // Reset Client Throttles | ||
3593 | // This has the effect of 'wiggling the slider | ||
3594 | // causes prim and stuck textures that didn't download to download | ||
3595 | |||
3596 | ResendBytesSent = 0; | ||
3597 | LandBytesSent = 0; | ||
3598 | WindBytesSent = 0; | ||
3599 | CloudBytesSent = 0; | ||
3600 | TaskBytesSent = 0; | ||
3601 | AssetBytesSent = 0; | ||
3602 | TextureBytesSent = 0; | ||
3603 | |||
3604 | //Yay, we've finally handled the agent Throttle packet! | ||
3605 | break; | 3092 | break; |
3606 | 3093 | ||
3607 | #endregion | 3094 | #endregion |
diff --git a/OpenSim/Region/ClientStack/PacketQueue.cs b/OpenSim/Region/ClientStack/PacketQueue.cs index f2d270c..5deede5 100644 --- a/OpenSim/Region/ClientStack/PacketQueue.cs +++ b/OpenSim/Region/ClientStack/PacketQueue.cs | |||
@@ -44,7 +44,7 @@ namespace OpenSim.Region.ClientStack | |||
44 | { | 44 | { |
45 | public class PacketQueue | 45 | public class PacketQueue |
46 | { | 46 | { |
47 | private Queue<QueItem> SendQueue; | 47 | private BlockingQueue<QueItem> SendQueue; |
48 | 48 | ||
49 | private Queue<QueItem> IncomingPacketQueue; | 49 | private Queue<QueItem> IncomingPacketQueue; |
50 | private Queue<QueItem> OutgoingPacketQueue; | 50 | private Queue<QueItem> OutgoingPacketQueue; |
@@ -76,8 +76,9 @@ namespace OpenSim.Region.ClientStack | |||
76 | private PacketThrottle TextureThrottle; | 76 | private PacketThrottle TextureThrottle; |
77 | private PacketThrottle TotalThrottle; | 77 | private PacketThrottle TotalThrottle; |
78 | 78 | ||
79 | private long LastThrottle; | 79 | // private long LastThrottle; |
80 | private long ThrottleInterval; | 80 | // private long ThrottleInterval; |
81 | private Timer throttleTimer; | ||
81 | 82 | ||
82 | public PacketQueue() | 83 | public PacketQueue() |
83 | { | 84 | { |
@@ -86,7 +87,7 @@ namespace OpenSim.Region.ClientStack | |||
86 | // in it to process. it's an on-purpose threadlock though because | 87 | // in it to process. it's an on-purpose threadlock though because |
87 | // without it, the clientloop will suck up all sim resources. | 88 | // without it, the clientloop will suck up all sim resources. |
88 | 89 | ||
89 | SendQueue = new Queue<QueItem>(); | 90 | SendQueue = new BlockingQueue<QueItem>(); |
90 | 91 | ||
91 | IncomingPacketQueue = new Queue<QueItem>(); | 92 | IncomingPacketQueue = new Queue<QueItem>(); |
92 | OutgoingPacketQueue = new Queue<QueItem>(); | 93 | OutgoingPacketQueue = new Queue<QueItem>(); |
@@ -110,10 +111,15 @@ namespace OpenSim.Region.ClientStack | |||
110 | // Total Throttle trumps all | 111 | // Total Throttle trumps all |
111 | // Number of bytes allowed to go out per second. (256kbps per client) | 112 | // Number of bytes allowed to go out per second. (256kbps per client) |
112 | TotalThrottle = new PacketThrottle(0, 162144, 1536000); | 113 | TotalThrottle = new PacketThrottle(0, 162144, 1536000); |
114 | |||
115 | throttleTimer = new Timer((int)(throttletimems/throttleTimeDivisor)); | ||
116 | throttleTimer.Elapsed += new ElapsedEventHandler(ThrottleTimerElapsed); | ||
117 | throttleTimer.Start(); | ||
113 | 118 | ||
114 | // TIMERS needed for this | 119 | // TIMERS needed for this |
115 | LastThrottle = DateTime.Now.Ticks; | 120 | // LastThrottle = DateTime.Now.Ticks; |
116 | ThrottleInterval = (long)(throttletimems/throttleTimeDivisor); | 121 | // ThrottleInterval = (long)(throttletimems/throttleTimeDivisor); |
122 | |||
117 | } | 123 | } |
118 | 124 | ||
119 | /* STANDARD QUEUE MANIPULATION INTERFACES */ | 125 | /* STANDARD QUEUE MANIPULATION INTERFACES */ |
@@ -123,7 +129,7 @@ namespace OpenSim.Region.ClientStack | |||
123 | { | 129 | { |
124 | // We could micro lock, but that will tend to actually | 130 | // We could micro lock, but that will tend to actually |
125 | // probably be worse than just synchronizing on SendQueue | 131 | // probably be worse than just synchronizing on SendQueue |
126 | lock (SendQueue) { | 132 | lock (this) { |
127 | switch (item.throttleType) | 133 | switch (item.throttleType) |
128 | { | 134 | { |
129 | case ThrottleOutPacketType.Resend: | 135 | case ThrottleOutPacketType.Resend: |
@@ -159,12 +165,14 @@ namespace OpenSim.Region.ClientStack | |||
159 | 165 | ||
160 | public QueItem Dequeue() | 166 | public QueItem Dequeue() |
161 | { | 167 | { |
162 | if (ThrottlingTime()) { | 168 | return SendQueue.Dequeue(); |
163 | ProcessThrottle(); | 169 | } |
164 | } | 170 | |
165 | lock (SendQueue) { | 171 | public void Close() |
166 | return SendQueue.Dequeue(); | 172 | { |
167 | } | 173 | // one last push |
174 | ProcessThrottle(); | ||
175 | throttleTimer.Stop(); | ||
168 | } | 176 | } |
169 | 177 | ||
170 | private void ResetCounters() | 178 | private void ResetCounters() |
@@ -192,15 +200,15 @@ namespace OpenSim.Region.ClientStack | |||
192 | 200 | ||
193 | // Run through our wait queues and flush out allotted numbers of bytes into the process queue | 201 | // Run through our wait queues and flush out allotted numbers of bytes into the process queue |
194 | 202 | ||
195 | private bool ThrottlingTime() | 203 | // private bool ThrottlingTime() |
196 | { | 204 | // { |
197 | if(DateTime.Now.Ticks < (LastThrottle + ThrottleInterval)) { | 205 | // if(DateTime.Now.Ticks > (LastThrottle + ThrottleInterval)) { |
198 | LastThrottle = DateTime.Now.Ticks; | 206 | // LastThrottle = DateTime.Now.Ticks; |
199 | return true; | 207 | // return true; |
200 | } else { | 208 | // } else { |
201 | return false; | 209 | // return false; |
202 | } | 210 | // } |
203 | } | 211 | // } |
204 | 212 | ||
205 | public void ProcessThrottle() | 213 | public void ProcessThrottle() |
206 | { | 214 | { |
@@ -216,8 +224,9 @@ namespace OpenSim.Region.ClientStack | |||
216 | 224 | ||
217 | // We're going to dequeue all of the saved up packets until | 225 | // We're going to dequeue all of the saved up packets until |
218 | // we've hit the throttle limit or there's no more packets to send | 226 | // we've hit the throttle limit or there's no more packets to send |
219 | lock (SendQueue) { | 227 | lock (this) { |
220 | ResetCounters(); | 228 | ResetCounters(); |
229 | // MainLog.Instance.Verbose("THROTTLE", "Entering Throttle"); | ||
221 | while (TotalThrottle.UnderLimit() && PacketsWaiting() && | 230 | while (TotalThrottle.UnderLimit() && PacketsWaiting() && |
222 | (throttleLoops <= MaxThrottleLoops)) | 231 | (throttleLoops <= MaxThrottleLoops)) |
223 | { | 232 | { |
@@ -280,86 +289,16 @@ namespace OpenSim.Region.ClientStack | |||
280 | AssetThrottle.Add(qpack.Packet.ToBytes().Length); | 289 | AssetThrottle.Add(qpack.Packet.ToBytes().Length); |
281 | } | 290 | } |
282 | } | 291 | } |
292 | // MainLog.Instance.Verbose("THROTTLE", "Processed " + throttleLoops + " packets"); | ||
293 | |||
283 | } | 294 | } |
284 | } | 295 | } |
285 | 296 | ||
286 | private void throttleTimer_Elapsed(object sender, ElapsedEventArgs e) | 297 | private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e) |
287 | { | 298 | { |
288 | ResetCounters(); | 299 | // just to change the signature, and that ProcessThrottle |
289 | 300 | // will be used elsewhere possibly | |
290 | // I was considering this.. Will an event fire if the thread it's on is blocked? | 301 | ProcessThrottle(); |
291 | |||
292 | // Then I figured out.. it doesn't really matter.. because this thread won't be blocked for long | ||
293 | // The General overhead of the UDP protocol gets sent to the queue un-throttled by this | ||
294 | // so This'll pick up about around the right time. | ||
295 | |||
296 | int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once. | ||
297 | int throttleLoops = 0; | ||
298 | |||
299 | // We're going to dequeue all of the saved up packets until | ||
300 | // we've hit the throttle limit or there's no more packets to send | ||
301 | while (TotalThrottle.UnderLimit() && PacketsWaiting() && | ||
302 | (throttleLoops <= MaxThrottleLoops)) | ||
303 | { | ||
304 | throttleLoops++; | ||
305 | //Now comes the fun part.. we dump all our elements into PacketQueue that we've saved up. | ||
306 | if (ResendThrottle.UnderLimit() && ResendOutgoingPacketQueue.Count > 0) | ||
307 | { | ||
308 | QueItem qpack = ResendOutgoingPacketQueue.Dequeue(); | ||
309 | |||
310 | SendQueue.Enqueue(qpack); | ||
311 | TotalThrottle.Add(qpack.Packet.ToBytes().Length); | ||
312 | ResendThrottle.Add(qpack.Packet.ToBytes().Length); | ||
313 | } | ||
314 | if (LandThrottle.UnderLimit() && LandOutgoingPacketQueue.Count > 0) | ||
315 | { | ||
316 | QueItem qpack = LandOutgoingPacketQueue.Dequeue(); | ||
317 | |||
318 | SendQueue.Enqueue(qpack); | ||
319 | TotalThrottle.Add(qpack.Packet.ToBytes().Length); | ||
320 | LandThrottle.Add(qpack.Packet.ToBytes().Length); | ||
321 | } | ||
322 | if (WindThrottle.UnderLimit() && WindOutgoingPacketQueue.Count > 0) | ||
323 | { | ||
324 | QueItem qpack = WindOutgoingPacketQueue.Dequeue(); | ||
325 | |||
326 | SendQueue.Enqueue(qpack); | ||
327 | TotalThrottle.Add(qpack.Packet.ToBytes().Length); | ||
328 | WindThrottle.Add(qpack.Packet.ToBytes().Length); | ||
329 | } | ||
330 | if (CloudThrottle.UnderLimit() && CloudOutgoingPacketQueue.Count > 0) | ||
331 | { | ||
332 | QueItem qpack = CloudOutgoingPacketQueue.Dequeue(); | ||
333 | |||
334 | SendQueue.Enqueue(qpack); | ||
335 | TotalThrottle.Add(qpack.Packet.ToBytes().Length); | ||
336 | CloudThrottle.Add(qpack.Packet.ToBytes().Length); | ||
337 | } | ||
338 | if (TaskThrottle.UnderLimit() && TaskOutgoingPacketQueue.Count > 0) | ||
339 | { | ||
340 | QueItem qpack = TaskOutgoingPacketQueue.Dequeue(); | ||
341 | |||
342 | SendQueue.Enqueue(qpack); | ||
343 | TotalThrottle.Add(qpack.Packet.ToBytes().Length); | ||
344 | TaskThrottle.Add(qpack.Packet.ToBytes().Length); | ||
345 | } | ||
346 | if (TextureThrottle.UnderLimit() && TextureOutgoingPacketQueue.Count > 0) | ||
347 | { | ||
348 | QueItem qpack = TextureOutgoingPacketQueue.Dequeue(); | ||
349 | |||
350 | SendQueue.Enqueue(qpack); | ||
351 | TotalThrottle.Add(qpack.Packet.ToBytes().Length); | ||
352 | TextureThrottle.Add(qpack.Packet.ToBytes().Length); | ||
353 | } | ||
354 | if (AssetThrottle.UnderLimit() && AssetOutgoingPacketQueue.Count > 0) | ||
355 | { | ||
356 | QueItem qpack = AssetOutgoingPacketQueue.Dequeue(); | ||
357 | |||
358 | SendQueue.Enqueue(qpack); | ||
359 | TotalThrottle.Add(qpack.Packet.ToBytes().Length); | ||
360 | AssetThrottle.Add(qpack.Packet.ToBytes().Length); | ||
361 | } | ||
362 | } | ||
363 | } | 302 | } |
364 | 303 | ||
365 | private void ThrottleCheck(ref PacketThrottle throttle, ref Queue<QueItem> q, QueItem item) | 304 | private void ThrottleCheck(ref PacketThrottle throttle, ref Queue<QueItem> q, QueItem item) |
@@ -372,9 +311,12 @@ namespace OpenSim.Region.ClientStack | |||
372 | 311 | ||
373 | if((q.Count == 0) && (throttle.UnderLimit())) | 312 | if((q.Count == 0) && (throttle.UnderLimit())) |
374 | { | 313 | { |
314 | Monitor.Enter(this); | ||
375 | throttle.Add(item.Packet.ToBytes().Length); | 315 | throttle.Add(item.Packet.ToBytes().Length); |
376 | TotalThrottle.Add(item.Packet.ToBytes().Length); | 316 | TotalThrottle.Add(item.Packet.ToBytes().Length); |
377 | SendQueue.Enqueue(item); | 317 | SendQueue.Enqueue(item); |
318 | Monitor.Pulse(this); | ||
319 | Monitor.Exit(this); | ||
378 | } | 320 | } |
379 | else | 321 | else |
380 | { | 322 | { |