aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
diff options
context:
space:
mode:
authorteravus2012-11-09 23:55:30 -0500
committerteravus2012-11-09 23:55:30 -0500
commitcda127e30f0049cda21137363e4d759fd7fd4959 (patch)
tree426814e4aa4c63eb5bd8c2f920bfa9aedeecd930 /OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
parentThis implements the Caps throttler. After some testing, the system seemed ... (diff)
downloadopensim-SC-cda127e30f0049cda21137363e4d759fd7fd4959.zip
opensim-SC-cda127e30f0049cda21137363e4d759fd7fd4959.tar.gz
opensim-SC-cda127e30f0049cda21137363e4d759fd7fd4959.tar.bz2
opensim-SC-cda127e30f0049cda21137363e4d759fd7fd4959.tar.xz
* Prep work switching the GetMeshModule over to a poll service.
* This still has the image throttler in it.. as is... so it's not suitable for live yet.... The throttler keeps track of the task throttle but doesn't balance the UDP throttle yet.
Diffstat (limited to 'OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs')
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs362
1 files changed, 349 insertions, 13 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
index 0d7b1fc..8deff81 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs
@@ -27,11 +27,14 @@
27 27
28using System; 28using System;
29using System.Collections; 29using System.Collections;
30using System.Collections.Generic;
30using System.Collections.Specialized; 31using System.Collections.Specialized;
31using System.Reflection; 32using System.Reflection;
32using System.IO; 33using System.IO;
34using System.Threading;
33using System.Web; 35using System.Web;
34using Mono.Addins; 36using Mono.Addins;
37using OpenSim.Framework.Monitoring;
35using log4net; 38using log4net;
36using Nini.Config; 39using Nini.Config;
37using OpenMetaverse; 40using OpenMetaverse;
@@ -57,8 +60,42 @@ namespace OpenSim.Region.ClientStack.Linden
57 private IAssetService m_AssetService; 60 private IAssetService m_AssetService;
58 private bool m_Enabled = true; 61 private bool m_Enabled = true;
59 private string m_URL; 62 private string m_URL;
63 struct aPollRequest
64 {
65 public PollServiceMeshEventArgs thepoll;
66 public UUID reqID;
67 public Hashtable request;
68 }
69
70 public class aPollResponse
71 {
72 public Hashtable response;
73 public int bytes;
74 }
75
76
77 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
78
79 private static GetMeshHandler m_getMeshHandler;
80
81 private IAssetService m_assetService = null;
82
83 private Dictionary<UUID, string> m_capsDict = new Dictionary<UUID, string>();
84 private static Thread[] m_workerThreads = null;
85
86 private static OpenMetaverse.BlockingQueue<aPollRequest> m_queue =
87 new OpenMetaverse.BlockingQueue<aPollRequest>();
88
89 private Dictionary<UUID, PollServiceMeshEventArgs> m_pollservices = new Dictionary<UUID, PollServiceMeshEventArgs>();
60 90
61 #region IRegionModuleBase Members 91 #region ISharedRegionModule Members
92
93 ~GetMeshModule()
94 {
95 foreach (Thread t in m_workerThreads)
96 Watchdog.AbortThread(t.ManagedThreadId);
97
98 }
62 99
63 public Type ReplaceableInterface 100 public Type ReplaceableInterface
64 { 101 {
@@ -83,6 +120,8 @@ namespace OpenSim.Region.ClientStack.Linden
83 return; 120 return;
84 121
85 m_scene = pScene; 122 m_scene = pScene;
123
124 m_assetService = pScene.AssetService;
86 } 125 }
87 126
88 public void RemoveRegion(Scene scene) 127 public void RemoveRegion(Scene scene)
@@ -91,6 +130,9 @@ namespace OpenSim.Region.ClientStack.Linden
91 return; 130 return;
92 131
93 m_scene.EventManager.OnRegisterCaps -= RegisterCaps; 132 m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
133 m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps;
134 m_scene.EventManager.OnThrottleUpdate -= ThrottleUpdate;
135
94 m_scene = null; 136 m_scene = null;
95 } 137 }
96 138
@@ -101,6 +143,27 @@ namespace OpenSim.Region.ClientStack.Linden
101 143
102 m_AssetService = m_scene.RequestModuleInterface<IAssetService>(); 144 m_AssetService = m_scene.RequestModuleInterface<IAssetService>();
103 m_scene.EventManager.OnRegisterCaps += RegisterCaps; 145 m_scene.EventManager.OnRegisterCaps += RegisterCaps;
146 // We'll reuse the same handler for all requests.
147 m_getMeshHandler = new GetMeshHandler(m_assetService);
148 m_scene.EventManager.OnDeregisterCaps += DeregisterCaps;
149 m_scene.EventManager.OnThrottleUpdate += ThrottleUpdate;
150
151 if (m_workerThreads == null)
152 {
153 m_workerThreads = new Thread[2];
154
155 for (uint i = 0; i < 2; i++)
156 {
157 m_workerThreads[i] = Watchdog.StartThread(DoMeshRequests,
158 String.Format("MeshWorkerThread{0}", i),
159 ThreadPriority.Normal,
160 false,
161 false,
162 null,
163 int.MaxValue);
164 }
165 }
166
104 } 167 }
105 168
106 169
@@ -110,25 +173,209 @@ namespace OpenSim.Region.ClientStack.Linden
110 173
111 #endregion 174 #endregion
112 175
176 private void DoMeshRequests()
177 {
178 while (true)
179 {
180 aPollRequest poolreq = m_queue.Dequeue();
181
182 poolreq.thepoll.Process(poolreq);
183 }
184 }
185
186 // Now we know when the throttle is changed by the client in the case of a root agent or by a neighbor region in the case of a child agent.
187 public void ThrottleUpdate(ScenePresence p)
188 {
189 byte[] throttles = p.ControllingClient.GetThrottlesPacked(1);
190 UUID user = p.UUID;
191 int imagethrottle = ExtractTaskThrottle(throttles);
192 PollServiceMeshEventArgs args;
193 if (m_pollservices.TryGetValue(user, out args))
194 {
195 args.UpdateThrottle(imagethrottle);
196 }
197 }
198
199 private int ExtractTaskThrottle(byte[] pthrottles)
200 {
201
202 byte[] adjData;
203 int pos = 0;
204
205 if (!BitConverter.IsLittleEndian)
206 {
207 byte[] newData = new byte[7 * 4];
208 Buffer.BlockCopy(pthrottles, 0, newData, 0, 7 * 4);
209
210 for (int i = 0; i < 7; i++)
211 Array.Reverse(newData, i * 4, 4);
212
213 adjData = newData;
214 }
215 else
216 {
217 adjData = pthrottles;
218 }
219
220 // 0.125f converts from bits to bytes
221 //int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
222 //pos += 4;
223 // int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
224 //pos += 4;
225 // int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
226 // pos += 4;
227 // int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
228 // pos += 4;
229 pos += 16;
230 int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
231 // pos += 4;
232 //int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); //pos += 4;
233 //int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
234 return task;
235 }
236
237 private class PollServiceMeshEventArgs : PollServiceEventArgs
238 {
239 private List<Hashtable> requests =
240 new List<Hashtable>();
241 private Dictionary<UUID, aPollResponse> responses =
242 new Dictionary<UUID, aPollResponse>();
243
244 private Scene m_scene;
245 private CapsDataThrottler m_throttler = new CapsDataThrottler(100000, 1400000, 10000);
246 public PollServiceMeshEventArgs(UUID pId, Scene scene) :
247 base(null, null, null, null, pId, int.MaxValue)
248 {
249 m_scene = scene;
250 // x is request id, y is userid
251 HasEvents = (x, y) =>
252 {
253 lock (responses)
254 {
255 bool ret = m_throttler.hasEvents(x, responses);
256 m_throttler.ProcessTime();
257 return ret;
258
259 }
260 };
261 GetEvents = (x, y) =>
262 {
263 lock (responses)
264 {
265 try
266 {
267 return responses[x].response;
268 }
269 finally
270 {
271 responses.Remove(x);
272 }
273 }
274 };
275 // x is request id, y is request data hashtable
276 Request = (x, y) =>
277 {
278 aPollRequest reqinfo = new aPollRequest();
279 reqinfo.thepoll = this;
280 reqinfo.reqID = x;
281 reqinfo.request = y;
282
283 m_queue.Enqueue(reqinfo);
284 };
285
286 // this should never happen except possible on shutdown
287 NoEvents = (x, y) =>
288 {
289 /*
290 lock (requests)
291 {
292 Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
293 requests.Remove(request);
294 }
295 */
296 Hashtable response = new Hashtable();
297
298 response["int_response_code"] = 500;
299 response["str_response_string"] = "Script timeout";
300 response["content_type"] = "text/plain";
301 response["keepalive"] = false;
302 response["reusecontext"] = false;
303
304 return response;
305 };
306 }
307
308 public void Process(aPollRequest requestinfo)
309 {
310 Hashtable response;
311
312 UUID requestID = requestinfo.reqID;
313
314 // If the avatar is gone, don't bother to get the texture
315 if (m_scene.GetScenePresence(Id) == null)
316 {
317 response = new Hashtable();
318
319 response["int_response_code"] = 500;
320 response["str_response_string"] = "Script timeout";
321 response["content_type"] = "text/plain";
322 response["keepalive"] = false;
323 response["reusecontext"] = false;
324
325 lock (responses)
326 responses[requestID] = new aPollResponse() { bytes = 0, response = response };
327
328 return;
329 }
330
331 response = m_getMeshHandler.Handle(requestinfo.request);
332 lock (responses)
333 {
334 responses[requestID] = new aPollResponse()
335 {
336 bytes = (int)response["int_bytes"],
337 response = response
338 };
339
340 }
341 m_throttler.ProcessTime();
342 }
343
344 internal void UpdateThrottle(int pimagethrottle)
345 {
346 m_throttler.ThrottleBytes = pimagethrottle;
347 }
348 }
113 349
114 public void RegisterCaps(UUID agentID, Caps caps) 350 public void RegisterCaps(UUID agentID, Caps caps)
115 { 351 {
116// UUID capID = UUID.Random(); 352// UUID capID = UUID.Random();
117
118 //caps.RegisterHandler("GetTexture", new StreamHandler("GET", "/CAPS/" + capID, ProcessGetTexture));
119 if (m_URL == "localhost") 353 if (m_URL == "localhost")
120 { 354 {
121// m_log.DebugFormat("[GETMESH]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); 355 string capUrl = "/CAPS/" + UUID.Random() + "/";
122 GetMeshHandler gmeshHandler = new GetMeshHandler(m_AssetService); 356
123 IRequestHandler reqHandler 357 // Register this as a poll service
124 = new RestHTTPHandler( 358 PollServiceMeshEventArgs args = new PollServiceMeshEventArgs(agentID, m_scene);
125 "GET", 359
126 "/CAPS/" + UUID.Random(), 360 args.Type = PollServiceEventArgs.EventType.Mesh;
127 httpMethod => gmeshHandler.ProcessGetMesh(httpMethod, UUID.Zero, null), 361 MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
128 "GetMesh", 362
129 agentID.ToString()); 363 string hostName = m_scene.RegionInfo.ExternalHostName;
364 uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
365 string protocol = "http";
130 366
131 caps.RegisterHandler("GetMesh", reqHandler); 367 if (MainServer.Instance.UseSSL)
368 {
369 hostName = MainServer.Instance.SSLCommonName;
370 port = MainServer.Instance.SSLPort;
371 protocol = "https";
372 }
373 caps.RegisterHandler("GetMesh", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
374 m_pollservices.Add(agentID, args);
375 m_capsDict[agentID] = capUrl;
376
377
378
132 } 379 }
133 else 380 else
134 { 381 {
@@ -136,6 +383,95 @@ namespace OpenSim.Region.ClientStack.Linden
136 caps.RegisterHandler("GetMesh", m_URL); 383 caps.RegisterHandler("GetMesh", m_URL);
137 } 384 }
138 } 385 }
386 private void DeregisterCaps(UUID agentID, Caps caps)
387 {
388 string capUrl;
389 PollServiceMeshEventArgs args;
390 if (m_capsDict.TryGetValue(agentID, out capUrl))
391 {
392 MainServer.Instance.RemoveHTTPHandler("", capUrl);
393 m_capsDict.Remove(agentID);
394 }
395 if (m_pollservices.TryGetValue(agentID, out args))
396 {
397 m_pollservices.Remove(agentID);
398 }
399 }
400
401 internal sealed class CapsDataThrottler
402 {
403
404 private volatile int currenttime = 0;
405 private volatile int lastTimeElapsed = 0;
406 private volatile int BytesSent = 0;
407 private int oversizedImages = 0;
408 public CapsDataThrottler(int pBytes, int max, int min)
409 {
410 ThrottleBytes = pBytes;
411 lastTimeElapsed = Util.EnvironmentTickCount();
412 }
413 public bool hasEvents(UUID key, Dictionary<UUID, aPollResponse> responses)
414 {
415 PassTime();
416 // Note, this is called IN LOCK
417 bool haskey = responses.ContainsKey(key);
418 if (!haskey)
419 {
420 return false;
421 }
422 aPollResponse response;
423 if (responses.TryGetValue(key, out response))
424 {
425
426 // Normal
427 if (BytesSent + response.bytes <= ThrottleBytes)
428 {
429 BytesSent += response.bytes;
430 //TimeBasedAction timeBasedAction = new TimeBasedAction { byteRemoval = response.bytes, requestId = key, timeMS = currenttime + 1000, unlockyn = false };
431 //m_actions.Add(timeBasedAction);
432 return true;
433 }
434 // Big textures
435 else if (response.bytes > ThrottleBytes && oversizedImages <= ((ThrottleBytes % 50000) + 1))
436 {
437 Interlocked.Increment(ref oversizedImages);
438 BytesSent += response.bytes;
439 //TimeBasedAction timeBasedAction = new TimeBasedAction { byteRemoval = response.bytes, requestId = key, timeMS = currenttime + (((response.bytes % ThrottleBytes)+1)*1000) , unlockyn = false };
440 //m_actions.Add(timeBasedAction);
441 return true;
442 }
443 else
444 {
445 return false;
446 }
447 }
448
449 return haskey;
450 }
451 public void ProcessTime()
452 {
453 PassTime();
454 }
455
456
457 private void PassTime()
458 {
459 currenttime = Util.EnvironmentTickCount();
460 int timeElapsed = Util.EnvironmentTickCountSubtract(currenttime, lastTimeElapsed);
461 //processTimeBasedActions(responses);
462 if (Util.EnvironmentTickCountSubtract(currenttime, timeElapsed) >= 1000)
463 {
464 lastTimeElapsed = Util.EnvironmentTickCount();
465 BytesSent -= ThrottleBytes;
466 if (BytesSent < 0) BytesSent = 0;
467 if (BytesSent < ThrottleBytes)
468 {
469 oversizedImages = 0;
470 }
471 }
472 }
473 public int ThrottleBytes;
474 }
139 475
140 } 476 }
141} 477}