From 5e4d6cab00cb29cd088ab7b62ab13aff103b64cb Mon Sep 17 00:00:00 2001 From: onefang Date: Sun, 19 May 2019 21:24:15 +1000 Subject: Dump OpenSim 0.9.0.1 into it's own branch. --- .../ClientStack/Linden/Caps/GetMeshModule.cs | 329 +++++++++++++++++++-- 1 file changed, 308 insertions(+), 21 deletions(-) (limited to 'OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs') diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs index f57d857..ba917e39 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs @@ -27,11 +27,14 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Collections.Specialized; using System.Reflection; using System.IO; +using System.Threading; using System.Web; using Mono.Addins; +using OpenSim.Framework.Monitoring; using log4net; using Nini.Config; using OpenMetaverse; @@ -52,15 +55,46 @@ namespace OpenSim.Region.ClientStack.Linden { // private static readonly ILog m_log = // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - + private Scene m_scene; private IAssetService m_AssetService; private bool m_Enabled = true; private string m_URL; + private string m_URL2; private string m_RedirectURL = null; private string m_RedirectURL2 = null; + struct aPollRequest + { + public PollServiceMeshEventArgs thepoll; + public UUID reqID; + public Hashtable request; + } + + public class aPollResponse + { + public Hashtable response; + public int bytes; + public int lod; + } + + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static GetMeshHandler m_getMeshHandler; + + private IAssetService m_assetService = null; + + private Dictionary m_capsDict = new Dictionary(); + private static Thread[] m_workerThreads = null; + private static int m_NumberScenes = 0; + private static OpenSim.Framework.BlockingQueue m_queue = + new OpenSim.Framework.BlockingQueue(); + + private Dictionary m_pollservices = new Dictionary(); + + #region Region Module interfaceBase Members public Type ReplaceableInterface @@ -87,6 +121,7 @@ namespace OpenSim.Region.ClientStack.Linden if (m_URL2 != string.Empty) { m_Enabled = true; + m_RedirectURL2 = config.GetString("GetMesh2RedirectURL"); } } @@ -97,6 +132,8 @@ namespace OpenSim.Region.ClientStack.Linden return; m_scene = pScene; + + m_assetService = pScene.AssetService; } public void RemoveRegion(Scene scene) @@ -105,6 +142,9 @@ namespace OpenSim.Region.ClientStack.Linden return; m_scene.EventManager.OnRegisterCaps -= RegisterCaps; + m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps; + m_scene.EventManager.OnThrottleUpdate -= ThrottleUpdate; + m_NumberScenes--; m_scene = null; } @@ -115,55 +155,302 @@ namespace OpenSim.Region.ClientStack.Linden m_AssetService = m_scene.RequestModuleInterface(); m_scene.EventManager.OnRegisterCaps += RegisterCaps; - } + // We'll reuse the same handler for all requests. + m_getMeshHandler = new GetMeshHandler(m_assetService); + m_scene.EventManager.OnDeregisterCaps += DeregisterCaps; + m_scene.EventManager.OnThrottleUpdate += ThrottleUpdate; + m_NumberScenes++; + + if (m_workerThreads == null) + { + m_workerThreads = new Thread[2]; + + for (uint i = 0; i < 2; i++) + { + m_workerThreads[i] = WorkManager.StartThread(DoMeshRequests, + String.Format("GetMeshWorker{0}", i), + ThreadPriority.Normal, + true, + false, + null, + int.MaxValue); + } + } + } - public void Close() { } + public void Close() + { + if(m_NumberScenes <= 0 && m_workerThreads != null) + { + m_log.DebugFormat("[GetMeshModule] Closing"); + foreach (Thread t in m_workerThreads) + Watchdog.AbortThread(t.ManagedThreadId); + // This will fail on region shutdown. Its harmless. + // Prevent red ink. + try + { + m_queue.Clear(); + } + catch {} + } + } public string Name { get { return "GetMeshModule"; } } #endregion + private static void DoMeshRequests() + { + while(true) + { + aPollRequest poolreq = m_queue.Dequeue(4500); + Watchdog.UpdateThread(); + if(m_NumberScenes <= 0) + return; + if(poolreq.reqID != UUID.Zero) + poolreq.thepoll.Process(poolreq); + } + } - public void RegisterCaps(UUID agentID, Caps caps) + // 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. + public void ThrottleUpdate(ScenePresence p) { - UUID capID = UUID.Random(); - bool getMeshRegistered = false; + UUID user = p.UUID; + int imagethrottle = p.ControllingClient.GetAgentThrottleSilent((int)ThrottleOutPacketType.Asset); + PollServiceMeshEventArgs args; + if (m_pollservices.TryGetValue(user, out args)) + { + args.UpdateThrottle(imagethrottle); + } + } + + private class PollServiceMeshEventArgs : PollServiceEventArgs + { + private List requests = + new List(); + private Dictionary responses = + new Dictionary(); + + private Scene m_scene; + private MeshCapsDataThrottler m_throttler; + public PollServiceMeshEventArgs(string uri, UUID pId, Scene scene) : + base(null, uri, null, null, null, pId, int.MaxValue) + { + m_scene = scene; + m_throttler = new MeshCapsDataThrottler(100000); + // x is request id, y is userid + HasEvents = (x, y) => + { + lock (responses) + { + bool ret = m_throttler.hasEvents(x, responses); + return ret; + + } + }; + GetEvents = (x, y) => + { + lock (responses) + { + try + { + return responses[x].response; + } + finally + { + responses.Remove(x); + m_throttler.PassTime(); + } + } + }; + // x is request id, y is request data hashtable + Request = (x, y) => + { + aPollRequest reqinfo = new aPollRequest(); + reqinfo.thepoll = this; + reqinfo.reqID = x; + reqinfo.request = y; + + m_queue.Enqueue(reqinfo); + m_throttler.PassTime(); + }; - if (m_URL == string.Empty) + // this should never happen except possible on shutdown + NoEvents = (x, y) => + { + /* + lock (requests) + { + Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString()); + requests.Remove(request); + } + */ + Hashtable response = new Hashtable(); + + response["int_response_code"] = 500; + response["str_response_string"] = "Script timeout"; + response["content_type"] = "text/plain"; + response["keepalive"] = false; + response["reusecontext"] = false; + + return response; + }; + } + + public void Process(aPollRequest requestinfo) { + Hashtable response; + + UUID requestID = requestinfo.reqID; + + if(m_scene.ShuttingDown) + return; + + // If the avatar is gone, don't bother to get the texture + if (m_scene.GetScenePresence(Id) == null) + { + response = new Hashtable(); + + response["int_response_code"] = 500; + response["str_response_string"] = "Script timeout"; + response["content_type"] = "text/plain"; + response["keepalive"] = false; + response["reusecontext"] = false; + + lock (responses) + responses[requestID] = new aPollResponse() { bytes = 0, response = response, lod = 0 }; + + return; + } + + response = m_getMeshHandler.Handle(requestinfo.request); + lock (responses) + { + responses[requestID] = new aPollResponse() + { + bytes = (int)response["int_bytes"], + lod = (int)response["int_lod"], + response = response + }; + + } + m_throttler.PassTime(); + } + internal void UpdateThrottle(int pthrottle) + { + int tmp = 2 * pthrottle; + if(tmp < 10000) + tmp = 10000; + m_throttler.ThrottleBytes = tmp; } - else if (m_URL == "localhost") + } + + public void RegisterCaps(UUID agentID, Caps caps) + { +// UUID capID = UUID.Random(); + if (m_URL == "localhost") { - getMeshRegistered = true; - caps.RegisterHandler( - "GetMesh", - new GetMeshHandler("/CAPS/" + capID + "/", m_AssetService, "GetMesh", agentID.ToString(), m_RedirectURL)); + string capUrl = "/CAPS/" + UUID.Random() + "/"; + + // Register this as a poll service + PollServiceMeshEventArgs args = new PollServiceMeshEventArgs(capUrl, agentID, m_scene); + + args.Type = PollServiceEventArgs.EventType.Mesh; + MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args); + + string hostName = m_scene.RegionInfo.ExternalHostName; + uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port; + string protocol = "http"; + + if (MainServer.Instance.UseSSL) + { + hostName = MainServer.Instance.SSLCommonName; + port = MainServer.Instance.SSLPort; + protocol = "https"; + } + caps.RegisterHandler("GetMesh", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl)); + m_pollservices[agentID] = args; + m_capsDict[agentID] = capUrl; } else { caps.RegisterHandler("GetMesh", m_URL); } + } - if(m_URL2 == string.Empty) + private void DeregisterCaps(UUID agentID, Caps caps) + { + string capUrl; + PollServiceMeshEventArgs args; + if (m_capsDict.TryGetValue(agentID, out capUrl)) + { + MainServer.Instance.RemoveHTTPHandler("", capUrl); + m_capsDict.Remove(agentID); + } + if (m_pollservices.TryGetValue(agentID, out args)) { + m_pollservices.Remove(agentID); + } + } + + internal sealed class MeshCapsDataThrottler + { + private double lastTimeElapsed = 0; + private double BytesSent = 0; + public MeshCapsDataThrottler(int pBytes) + { + if(pBytes < 10000) + pBytes = 10000; + ThrottleBytes = pBytes; + lastTimeElapsed = Util.GetTimeStampMS(); } - else if (m_URL2 == "localhost") + + public bool hasEvents(UUID key, Dictionary responses) { - if (!getMeshRegistered) + PassTime(); + // Note, this is called IN LOCK + bool haskey = responses.ContainsKey(key); + + if (!haskey) + { + return false; + } + aPollResponse response; + if (responses.TryGetValue(key, out response)) { - caps.RegisterHandler( - "GetMesh2", - new GetMeshHandler("/CAPS/" + capID + "/", m_AssetService, "GetMesh2", agentID.ToString(), m_RedirectURL2)); + // Normal + if (BytesSent <= ThrottleBytes) + { + BytesSent += response.bytes; + return true; + } + else + { + return false; + } } + return haskey; } - else + + public void PassTime() { - caps.RegisterHandler("GetMesh2", m_URL2); + double currenttime = Util.GetTimeStampMS(); + double timeElapsed = currenttime - lastTimeElapsed; + if(timeElapsed < 50.0) + return; + int add = (int)(ThrottleBytes * timeElapsed * 0.001); + if (add >= 1000) + { + lastTimeElapsed = currenttime; + BytesSent -= add; + if (BytesSent < 0) BytesSent = 0; + } } - } + public int ThrottleBytes; + } } } -- cgit v1.1