diff options
author | teravus | 2012-11-09 23:55:30 -0500 |
---|---|---|
committer | teravus | 2012-11-09 23:55:30 -0500 |
commit | cda127e30f0049cda21137363e4d759fd7fd4959 (patch) | |
tree | 426814e4aa4c63eb5bd8c2f920bfa9aedeecd930 | |
parent | This implements the Caps throttler. After some testing, the system seemed ... (diff) | |
download | opensim-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.
4 files changed, 538 insertions, 79 deletions
diff --git a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs index 720640e..d29bed9 100644 --- a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs +++ b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs | |||
@@ -45,16 +45,53 @@ namespace OpenSim.Capabilities.Handlers | |||
45 | { | 45 | { |
46 | public class GetMeshHandler | 46 | public class GetMeshHandler |
47 | { | 47 | { |
48 | // private static readonly ILog m_log = | 48 | private static readonly ILog m_log = |
49 | // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 49 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
50 | 50 | ||
51 | private IAssetService m_assetService; | 51 | private IAssetService m_assetService; |
52 | 52 | ||
53 | public const string DefaultFormat = "vnd.ll.mesh"; | ||
54 | |||
53 | public GetMeshHandler(IAssetService assService) | 55 | public GetMeshHandler(IAssetService assService) |
54 | { | 56 | { |
55 | m_assetService = assService; | 57 | m_assetService = assService; |
56 | } | 58 | } |
59 | public Hashtable Handle(Hashtable request) | ||
60 | { | ||
61 | Hashtable ret = new Hashtable(); | ||
62 | ret["int_response_code"] = (int)System.Net.HttpStatusCode.NotFound; | ||
63 | ret["content_type"] = "text/plain"; | ||
64 | ret["keepalive"] = false; | ||
65 | ret["reusecontext"] = false; | ||
66 | ret["int_bytes"] = 0; | ||
67 | string MeshStr = (string)request["mesh_id"]; | ||
68 | |||
69 | |||
70 | //m_log.DebugFormat("[GETMESH]: called {0}", MeshStr); | ||
71 | |||
72 | if (m_assetService == null) | ||
73 | { | ||
74 | m_log.Error("[GETMESH]: Cannot fetch mesh " + MeshStr + " without an asset service"); | ||
75 | } | ||
76 | |||
77 | UUID meshID; | ||
78 | if (!String.IsNullOrEmpty(MeshStr) && UUID.TryParse(MeshStr, out meshID)) | ||
79 | { | ||
80 | // m_log.DebugFormat("[GETMESH]: Received request for mesh id {0}", meshID); | ||
57 | 81 | ||
82 | |||
83 | ret = ProcessGetMesh(request, UUID.Zero, null); | ||
84 | |||
85 | |||
86 | } | ||
87 | else | ||
88 | { | ||
89 | m_log.Warn("[GETMESH]: Failed to parse a mesh_id from GetMesh request: " + (string)request["uri"]); | ||
90 | } | ||
91 | |||
92 | |||
93 | return ret; | ||
94 | } | ||
58 | public Hashtable ProcessGetMesh(Hashtable request, UUID AgentId, Caps cap) | 95 | public Hashtable ProcessGetMesh(Hashtable request, UUID AgentId, Caps cap) |
59 | { | 96 | { |
60 | Hashtable responsedata = new Hashtable(); | 97 | Hashtable responsedata = new Hashtable(); |
@@ -86,9 +123,78 @@ namespace OpenSim.Capabilities.Handlers | |||
86 | { | 123 | { |
87 | if (mesh.Type == (SByte)AssetType.Mesh) | 124 | if (mesh.Type == (SByte)AssetType.Mesh) |
88 | { | 125 | { |
89 | responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data); | 126 | |
90 | responsedata["content_type"] = "application/vnd.ll.mesh"; | 127 | Hashtable headers = new Hashtable(); |
91 | responsedata["int_response_code"] = 200; | 128 | responsedata["headers"] = headers; |
129 | |||
130 | string range = String.Empty; | ||
131 | |||
132 | if (((Hashtable)request["headers"])["range"] != null) | ||
133 | range = (string)((Hashtable)request["headers"])["range"]; | ||
134 | |||
135 | else if (((Hashtable)request["headers"])["Range"] != null) | ||
136 | range = (string)((Hashtable)request["headers"])["Range"]; | ||
137 | |||
138 | if (!String.IsNullOrEmpty(range)) // Mesh Asset LOD // Physics | ||
139 | { | ||
140 | // Range request | ||
141 | int start, end; | ||
142 | if (TryParseRange(range, out start, out end)) | ||
143 | { | ||
144 | // Before clamping start make sure we can satisfy it in order to avoid | ||
145 | // sending back the last byte instead of an error status | ||
146 | if (start >= mesh.Data.Length) | ||
147 | { | ||
148 | responsedata["int_response_code"] = 404; //501; //410; //404; | ||
149 | responsedata["content_type"] = "text/plain"; | ||
150 | responsedata["keepalive"] = false; | ||
151 | responsedata["str_response_string"] = "This range doesnt exist."; | ||
152 | return responsedata; | ||
153 | } | ||
154 | else | ||
155 | { | ||
156 | end = Utils.Clamp(end, 0, mesh.Data.Length - 1); | ||
157 | start = Utils.Clamp(start, 0, end); | ||
158 | int len = end - start + 1; | ||
159 | |||
160 | //m_log.Debug("Serving " + start + " to " + end + " of " + texture.Data.Length + " bytes for texture " + texture.ID); | ||
161 | |||
162 | |||
163 | |||
164 | if (start == 0 && len == mesh.Data.Length) // well redudante maybe | ||
165 | { | ||
166 | responsedata["int_response_code"] = (int) System.Net.HttpStatusCode.OK; | ||
167 | responsedata["bin_response_data"] = mesh.Data; | ||
168 | responsedata["int_bytes"] = mesh.Data.Length; | ||
169 | } | ||
170 | else | ||
171 | { | ||
172 | responsedata["int_response_code"] = | ||
173 | (int) System.Net.HttpStatusCode.PartialContent; | ||
174 | headers["Content-Range"] = String.Format("bytes {0}-{1}/{2}", start, end, | ||
175 | mesh.Data.Length); | ||
176 | |||
177 | byte[] d = new byte[len]; | ||
178 | Array.Copy(mesh.Data, start, d, 0, len); | ||
179 | responsedata["bin_response_data"] = d; | ||
180 | responsedata["int_bytes"] = len; | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | else | ||
185 | { | ||
186 | m_log.Warn("[GETMESH]: Failed to parse a range from GetMesh request, sending full asset: " + (string)request["uri"]); | ||
187 | responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data); | ||
188 | responsedata["content_type"] = "application/vnd.ll.mesh"; | ||
189 | responsedata["int_response_code"] = 200; | ||
190 | } | ||
191 | } | ||
192 | else | ||
193 | { | ||
194 | responsedata["str_response_string"] = Convert.ToBase64String(mesh.Data); | ||
195 | responsedata["content_type"] = "application/vnd.ll.mesh"; | ||
196 | responsedata["int_response_code"] = 200; | ||
197 | } | ||
92 | } | 198 | } |
93 | // Optionally add additional mesh types here | 199 | // Optionally add additional mesh types here |
94 | else | 200 | else |
@@ -112,5 +218,20 @@ namespace OpenSim.Capabilities.Handlers | |||
112 | 218 | ||
113 | return responsedata; | 219 | return responsedata; |
114 | } | 220 | } |
221 | private bool TryParseRange(string header, out int start, out int end) | ||
222 | { | ||
223 | if (header.StartsWith("bytes=")) | ||
224 | { | ||
225 | string[] rangeValues = header.Substring(6).Split('-'); | ||
226 | if (rangeValues.Length == 2) | ||
227 | { | ||
228 | if (Int32.TryParse(rangeValues[0], out start) && Int32.TryParse(rangeValues[1], out end)) | ||
229 | return true; | ||
230 | } | ||
231 | } | ||
232 | |||
233 | start = end = 0; | ||
234 | return false; | ||
235 | } | ||
115 | } | 236 | } |
116 | } \ No newline at end of file | 237 | } \ No newline at end of file |
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs index d0a37d0..c19ac32 100644 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs | |||
@@ -53,7 +53,8 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
53 | Normal = 0, | 53 | Normal = 0, |
54 | LslHttp = 1, | 54 | LslHttp = 1, |
55 | Inventory = 2, | 55 | Inventory = 2, |
56 | Texture = 3 | 56 | Texture = 3, |
57 | Mesh = 4 | ||
57 | } | 58 | } |
58 | 59 | ||
59 | public PollServiceEventArgs( | 60 | public PollServiceEventArgs( |
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 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | 29 | using System.Collections; |
30 | using System.Collections.Generic; | ||
30 | using System.Collections.Specialized; | 31 | using System.Collections.Specialized; |
31 | using System.Reflection; | 32 | using System.Reflection; |
32 | using System.IO; | 33 | using System.IO; |
34 | using System.Threading; | ||
33 | using System.Web; | 35 | using System.Web; |
34 | using Mono.Addins; | 36 | using Mono.Addins; |
37 | using OpenSim.Framework.Monitoring; | ||
35 | using log4net; | 38 | using log4net; |
36 | using Nini.Config; | 39 | using Nini.Config; |
37 | using OpenMetaverse; | 40 | using 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 | } |
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs index 8cba6c8..c8c709a 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs | |||
@@ -364,80 +364,81 @@ namespace OpenSim.Region.ClientStack.Linden | |||
364 | poolreq.thepoll.Process(poolreq); | 364 | poolreq.thepoll.Process(poolreq); |
365 | } | 365 | } |
366 | } | 366 | } |
367 | } | 367 | internal sealed class CapsDataThrottler |
368 | |||
369 | internal sealed class CapsDataThrottler | ||
370 | { | ||
371 | |||
372 | private volatile int currenttime = 0; | ||
373 | private volatile int lastTimeElapsed = 0; | ||
374 | private volatile int BytesSent = 0; | ||
375 | private int oversizedImages = 0; | ||
376 | public CapsDataThrottler(int pBytes, int max, int min) | ||
377 | { | ||
378 | ThrottleBytes = pBytes; | ||
379 | lastTimeElapsed = Util.EnvironmentTickCount(); | ||
380 | } | ||
381 | public bool hasEvents(UUID key, Dictionary<UUID, GetTextureModule.aPollResponse> responses) | ||
382 | { | 368 | { |
383 | PassTime(); | 369 | |
384 | // Note, this is called IN LOCK | 370 | private volatile int currenttime = 0; |
385 | bool haskey = responses.ContainsKey(key); | 371 | private volatile int lastTimeElapsed = 0; |
386 | if (!haskey) | 372 | private volatile int BytesSent = 0; |
373 | private int oversizedImages = 0; | ||
374 | public CapsDataThrottler(int pBytes, int max, int min) | ||
387 | { | 375 | { |
388 | return false; | 376 | ThrottleBytes = pBytes; |
377 | lastTimeElapsed = Util.EnvironmentTickCount(); | ||
389 | } | 378 | } |
390 | GetTextureModule.aPollResponse response; | 379 | public bool hasEvents(UUID key, Dictionary<UUID, GetTextureModule.aPollResponse> responses) |
391 | if (responses.TryGetValue(key,out response)) | ||
392 | { | 380 | { |
393 | 381 | PassTime(); | |
394 | // Normal | 382 | // Note, this is called IN LOCK |
395 | if (BytesSent + response.bytes <= ThrottleBytes) | 383 | bool haskey = responses.ContainsKey(key); |
384 | if (!haskey) | ||
396 | { | 385 | { |
397 | BytesSent += response.bytes; | 386 | return false; |
398 | //TimeBasedAction timeBasedAction = new TimeBasedAction { byteRemoval = response.bytes, requestId = key, timeMS = currenttime + 1000, unlockyn = false }; | ||
399 | //m_actions.Add(timeBasedAction); | ||
400 | return true; | ||
401 | } | ||
402 | // Big textures | ||
403 | else if (response.bytes > ThrottleBytes && oversizedImages <= ((ThrottleBytes%50000) + 1)) | ||
404 | { | ||
405 | Interlocked.Increment(ref oversizedImages); | ||
406 | BytesSent += response.bytes; | ||
407 | //TimeBasedAction timeBasedAction = new TimeBasedAction { byteRemoval = response.bytes, requestId = key, timeMS = currenttime + (((response.bytes % ThrottleBytes)+1)*1000) , unlockyn = false }; | ||
408 | //m_actions.Add(timeBasedAction); | ||
409 | return true; | ||
410 | } | 387 | } |
411 | else | 388 | GetTextureModule.aPollResponse response; |
389 | if (responses.TryGetValue(key, out response)) | ||
412 | { | 390 | { |
413 | return false; | 391 | |
392 | // Normal | ||
393 | if (BytesSent + response.bytes <= ThrottleBytes) | ||
394 | { | ||
395 | BytesSent += response.bytes; | ||
396 | //TimeBasedAction timeBasedAction = new TimeBasedAction { byteRemoval = response.bytes, requestId = key, timeMS = currenttime + 1000, unlockyn = false }; | ||
397 | //m_actions.Add(timeBasedAction); | ||
398 | return true; | ||
399 | } | ||
400 | // Big textures | ||
401 | else if (response.bytes > ThrottleBytes && oversizedImages <= ((ThrottleBytes % 50000) + 1)) | ||
402 | { | ||
403 | Interlocked.Increment(ref oversizedImages); | ||
404 | BytesSent += response.bytes; | ||
405 | //TimeBasedAction timeBasedAction = new TimeBasedAction { byteRemoval = response.bytes, requestId = key, timeMS = currenttime + (((response.bytes % ThrottleBytes)+1)*1000) , unlockyn = false }; | ||
406 | //m_actions.Add(timeBasedAction); | ||
407 | return true; | ||
408 | } | ||
409 | else | ||
410 | { | ||
411 | return false; | ||
412 | } | ||
414 | } | 413 | } |
414 | |||
415 | return haskey; | ||
416 | } | ||
417 | public void ProcessTime() | ||
418 | { | ||
419 | PassTime(); | ||
415 | } | 420 | } |
416 | 421 | ||
417 | return haskey; | 422 | |
418 | } | 423 | private void PassTime() |
419 | public void ProcessTime() | ||
420 | { | ||
421 | PassTime(); | ||
422 | } | ||
423 | |||
424 | |||
425 | private void PassTime() | ||
426 | { | ||
427 | currenttime = Util.EnvironmentTickCount(); | ||
428 | int timeElapsed = Util.EnvironmentTickCountSubtract(currenttime, lastTimeElapsed); | ||
429 | //processTimeBasedActions(responses); | ||
430 | if (Util.EnvironmentTickCountSubtract(currenttime, timeElapsed) >= 1000) | ||
431 | { | 424 | { |
432 | lastTimeElapsed = Util.EnvironmentTickCount(); | 425 | currenttime = Util.EnvironmentTickCount(); |
433 | BytesSent -= ThrottleBytes; | 426 | int timeElapsed = Util.EnvironmentTickCountSubtract(currenttime, lastTimeElapsed); |
434 | if (BytesSent < 0) BytesSent = 0; | 427 | //processTimeBasedActions(responses); |
435 | if (BytesSent < ThrottleBytes) | 428 | if (Util.EnvironmentTickCountSubtract(currenttime, timeElapsed) >= 1000) |
436 | { | 429 | { |
437 | oversizedImages = 0; | 430 | lastTimeElapsed = Util.EnvironmentTickCount(); |
431 | BytesSent -= ThrottleBytes; | ||
432 | if (BytesSent < 0) BytesSent = 0; | ||
433 | if (BytesSent < ThrottleBytes) | ||
434 | { | ||
435 | oversizedImages = 0; | ||
436 | } | ||
438 | } | 437 | } |
439 | } | 438 | } |
439 | public int ThrottleBytes; | ||
440 | } | 440 | } |
441 | public int ThrottleBytes; | ||
442 | } | 441 | } |
442 | |||
443 | |||
443 | } | 444 | } |