diff --git a/OpenSim/Capabilities/Caps.cs b/OpenSim/Capabilities/Caps.cs index 95fc61b051..30a323e348 100644 --- a/OpenSim/Capabilities/Caps.cs +++ b/OpenSim/Capabilities/Caps.cs @@ -64,7 +64,11 @@ namespace OpenSim.Framework.Capabilities public string CapsObjectPath { get { return m_capsObjectPath; } } private CapsHandlers m_capsHandlers; - private Dictionary m_externalCapsHandlers; + + private Dictionary m_pollServiceHandlers + = new Dictionary(); + + private Dictionary m_externalCapsHandlers = new Dictionary(); private IHttpServer m_httpListener; private UUID m_agentID; @@ -134,7 +138,6 @@ namespace OpenSim.Framework.Capabilities m_agentID = agent; m_capsHandlers = new CapsHandlers(httpServer, httpListen, httpPort, (httpServer == null) ? false : httpServer.UseSSL); - m_externalCapsHandlers = new Dictionary(); m_regionName = regionName; } @@ -149,6 +152,27 @@ namespace OpenSim.Framework.Capabilities m_capsHandlers[capName] = handler; } + public void RegisterPollHandler(string capName, PollServiceEventArgs pollServiceHandler) + { + m_pollServiceHandlers.Add(capName, pollServiceHandler); + + m_httpListener.AddPollServiceHTTPHandler(pollServiceHandler.Url, pollServiceHandler); + +// uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port; +// string protocol = "http"; +// string hostName = m_httpListenerHostName; +// +// if (MainServer.Instance.UseSSL) +// { +// hostName = MainServer.Instance.SSLCommonName; +// port = MainServer.Instance.SSLPort; +// protocol = "https"; +// } + +// RegisterHandler( +// capName, String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, pollServiceHandler.Url)); + } + /// /// Register an external handler. The service for this capability is somewhere else /// given by the URL. @@ -165,13 +189,70 @@ namespace OpenSim.Framework.Capabilities /// public void DeregisterHandlers() { - if (m_capsHandlers != null) + foreach (string capsName in m_capsHandlers.Caps) { - foreach (string capsName in m_capsHandlers.Caps) + m_capsHandlers.Remove(capsName); + } + + foreach (PollServiceEventArgs handler in m_pollServiceHandlers.Values) + { + m_httpListener.RemovePollServiceHTTPHandler("", handler.Url); + } + } + + public bool TryGetPollHandler(string name, out PollServiceEventArgs pollHandler) + { + return m_pollServiceHandlers.TryGetValue(name, out pollHandler); + } + + public Dictionary GetPollHandlers() + { + return new Dictionary(m_pollServiceHandlers); + } + + /// + /// Return an LLSD-serializable Hashtable describing the + /// capabilities and their handler details. + /// + /// If true, then exclude the seed cap. + public Hashtable GetCapsDetails(bool excludeSeed, List requestedCaps) + { + Hashtable caps = CapsHandlers.GetCapsDetails(excludeSeed, requestedCaps); + + lock (m_pollServiceHandlers) + { + foreach (KeyValuePair kvp in m_pollServiceHandlers) { - m_capsHandlers.Remove(capsName); + if (!requestedCaps.Contains(kvp.Key)) + continue; + + string hostName = m_httpListenerHostName; + 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("FetchInventoryDescendents2", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl)); + + caps[kvp.Key] = string.Format("{0}://{1}:{2}{3}", protocol, hostName, port, kvp.Value.Url); } } + + // Add the external too + foreach (KeyValuePair kvp in ExternalCapsHandlers) + { + if (!requestedCaps.Contains(kvp.Key)) + continue; + + caps[kvp.Key] = kvp.Value; + } + + return caps; } public void Activate() @@ -185,4 +266,4 @@ namespace OpenSim.Framework.Capabilities return m_capsActive.WaitOne(30000); } } -} +} \ No newline at end of file diff --git a/OpenSim/Data/Null/NullSimulationData.cs b/OpenSim/Data/Null/NullSimulationData.cs index e7e5c41e00..15824a9ebe 100644 --- a/OpenSim/Data/Null/NullSimulationData.cs +++ b/OpenSim/Data/Null/NullSimulationData.cs @@ -77,20 +77,34 @@ namespace OpenSim.Data.Null } #region Environment Settings + + private Dictionary EnvironmentSettings = new Dictionary(); + public string LoadRegionEnvironmentSettings(UUID regionUUID) { - //This connector doesn't support the Environment module yet + lock (EnvironmentSettings) + { + if (EnvironmentSettings.ContainsKey(regionUUID)) + return EnvironmentSettings[regionUUID]; + } return string.Empty; } public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings) { - //This connector doesn't support the Environment module yet + lock (EnvironmentSettings) + { + EnvironmentSettings[regionUUID] = settings; + } } public void RemoveRegionEnvironmentSettings(UUID regionUUID) { - //This connector doesn't support the Environment module yet + lock (EnvironmentSettings) + { + if (EnvironmentSettings.ContainsKey(regionUUID)) + EnvironmentSettings.Remove(regionUUID); + } } #endregion diff --git a/OpenSim/Data/SQLite/Resources/HGTravelStore.migrations b/OpenSim/Data/SQLite/Resources/HGTravelStore.migrations index 2e73caa9d2..02612ce13a 100644 --- a/OpenSim/Data/SQLite/Resources/HGTravelStore.migrations +++ b/OpenSim/Data/SQLite/Resources/HGTravelStore.migrations @@ -1,18 +1,18 @@ -:VERSION 1 # -------------------------- +:VERSION 2 # -------------------------- BEGIN; -CREATE TABLE hg_traveling_data ( +CREATE TABLE hg_traveling_data( SessionID VARCHAR(36) NOT NULL, UserID VARCHAR(36) NOT NULL, - GridExternalName VARCHAR(255) NOT NULL DEFAULT '', - ServiceToken VARCHAR(255) NOT NULL DEFAULT '', - ClientIPAddress VARCHAR(16) NOT NULL DEFAULT '', - MyIPAddress VARCHAR(16) NOT NULL DEFAULT '', - TMStamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - PRIMARY KEY (`SessionID`), - KEY (`UserID`) -) ENGINE=InnoDB; + GridExternalName VARCHAR(255) NOT NULL DEFAULT "", + ServiceToken VARCHAR(255) NOT NULL DEFAULT "", + ClientIPAddress VARCHAR(16) NOT NULL DEFAULT "", + MyIPAddress VARCHAR(16) NOT NULL DEFAULT "", + TMStamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY(SessionID), + UNIQUE(UserID) +); COMMIT; diff --git a/OpenSim/Framework/BlockingQueue.cs b/OpenSim/Framework/BlockingQueue.cs index 365816109b..3e90fac569 100644 --- a/OpenSim/Framework/BlockingQueue.cs +++ b/OpenSim/Framework/BlockingQueue.cs @@ -58,7 +58,7 @@ namespace OpenSim.Framework { lock (m_queueSync) { - if (m_queue.Count < 1 && m_pqueue.Count < 1) + while (m_queue.Count < 1 && m_pqueue.Count < 1) { Monitor.Wait(m_queueSync); } @@ -76,9 +76,10 @@ namespace OpenSim.Framework { lock (m_queueSync) { - if (m_queue.Count < 1 && m_pqueue.Count < 1) + bool success = true; + while (m_queue.Count < 1 && m_pqueue.Count < 1 && success) { - Monitor.Wait(m_queueSync, msTimeout); + success = Monitor.Wait(m_queueSync, msTimeout); } if (m_pqueue.Count > 0) @@ -89,8 +90,17 @@ namespace OpenSim.Framework } } + /// + /// Indicate whether this queue contains the given item. + /// + /// + /// This method is not thread-safe. Do not rely on the result without consistent external locking. + /// public bool Contains(T item) { + if (m_queue.Count < 1 && m_pqueue.Count < 1) + return false; + lock (m_queueSync) { if (m_pqueue.Contains(item)) @@ -99,16 +109,28 @@ namespace OpenSim.Framework } } + /// + /// Return a count of the number of requests on this queue. + /// + /// + /// This method is not thread-safe. Do not rely on the result without consistent external locking. + /// public int Count() { - lock (m_queueSync) - { - return m_queue.Count+m_pqueue.Count; - } + return m_queue.Count + m_pqueue.Count; } + /// + /// Return the array of items on this queue. + /// + /// + /// This method is not thread-safe. Do not rely on the result without consistent external locking. + /// public T[] GetQueueArray() { + if (m_queue.Count < 1 && m_pqueue.Count < 1) + return new T[0]; + lock (m_queueSync) { return m_queue.ToArray(); diff --git a/OpenSim/Framework/Console/RemoteConsole.cs b/OpenSim/Framework/Console/RemoteConsole.cs index 3e3c2b3758..8ad7b0daa9 100644 --- a/OpenSim/Framework/Console/RemoteConsole.cs +++ b/OpenSim/Framework/Console/RemoteConsole.cs @@ -234,7 +234,7 @@ namespace OpenSim.Framework.Console string uri = "/ReadResponses/" + sessionID.ToString() + "/"; m_Server.AddPollServiceHTTPHandler( - uri, new PollServiceEventArgs(null, HasEvents, GetEvents, NoEvents, sessionID,25000)); // 25 secs timeout + uri, new PollServiceEventArgs(null, uri, HasEvents, GetEvents, NoEvents, sessionID,25000)); // 25 secs timeout XmlDocument xmldoc = new XmlDocument(); XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index 0fde42c5fc..f4b4156e39 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -387,6 +387,8 @@ namespace OpenSim.Framework.Servers.HttpServer if (TryGetPollServiceHTTPHandler(request.UriPath.ToString(), out psEvArgs)) { + psEvArgs.RequestsReceived++; + PollServiceHttpRequest psreq = new PollServiceHttpRequest(psEvArgs, context, request); if (psEvArgs.Request != null) diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs index c19ac320da..3fd3bf703c 100644 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs @@ -50,25 +50,39 @@ namespace OpenSim.Framework.Servers.HttpServer public enum EventType : int { - Normal = 0, + LongPoll = 0, LslHttp = 1, Inventory = 2, Texture = 3, Mesh = 4 } + public string Url { get; set; } + + /// + /// Number of requests received for this poll service. + /// + public int RequestsReceived { get; set; } + + /// + /// Number of requests handled by this poll service. + /// + public int RequestsHandled { get; set; } + public PollServiceEventArgs( RequestMethod pRequest, + string pUrl, HasEventsMethod pHasEvents, GetEventsMethod pGetEvents, NoEventsMethod pNoEvents, UUID pId, int pTimeOutms) { Request = pRequest; + Url = pUrl; HasEvents = pHasEvents; GetEvents = pGetEvents; NoEvents = pNoEvents; Id = pId; TimeOutms = pTimeOutms; - Type = EventType.Normal; + Type = EventType.LongPoll; } } } diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs index 723530ae15..6aa94794ee 100644 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs @@ -26,13 +26,19 @@ */ using System; +using System.Collections; +using System.Reflection; +using System.Text; using HttpServer; +using log4net; using OpenMetaverse; namespace OpenSim.Framework.Servers.HttpServer { public class PollServiceHttpRequest { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + public readonly PollServiceEventArgs PollServiceArgs; public readonly IHttpClientContext HttpContext; public readonly IHttpRequest Request; @@ -48,5 +54,44 @@ namespace OpenSim.Framework.Servers.HttpServer RequestTime = System.Environment.TickCount; RequestID = UUID.Random(); } + + internal void DoHTTPGruntWork(BaseHttpServer server, Hashtable responsedata) + { + OSHttpResponse response + = new OSHttpResponse(new HttpResponse(HttpContext, Request), HttpContext); + + byte[] buffer = server.DoHTTPGruntWork(responsedata, response); + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + + try + { + response.OutputStream.Write(buffer, 0, buffer.Length); + } + catch (Exception ex) + { + m_log.Warn(string.Format("[POLL SERVICE WORKER THREAD]: Error ", ex)); + } + finally + { + //response.OutputStream.Close(); + try + { + response.OutputStream.Flush(); + response.Send(); + + //if (!response.KeepAlive && response.ReuseContext) + // response.FreeContext(); + } + catch (Exception e) + { + m_log.Warn(String.Format("[POLL SERVICE WORKER THREAD]: Error ", e)); + } + + PollServiceArgs.RequestsHandled++; + } + } } } \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs index 98789be12a..44f704537b 100644 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs @@ -105,7 +105,7 @@ namespace OpenSim.Framework.Servers.HttpServer { if (m_running) { - if (req.PollServiceArgs.Type != PollServiceEventArgs.EventType.Normal) + if (req.PollServiceArgs.Type != PollServiceEventArgs.EventType.LongPoll) { m_requests.Enqueue(req); } @@ -207,7 +207,7 @@ namespace OpenSim.Framework.Servers.HttpServer if (responsedata == null) continue; - if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.Normal) // This is the event queue + if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll) // This is the event queue { try { diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 6c7af128fa..19d40db61a 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -1791,10 +1791,12 @@ namespace OpenSim.Framework FireAndForget(callback, null); } - public static void InitThreadPool(int maxThreads) + public static void InitThreadPool(int minThreads, int maxThreads) { if (maxThreads < 2) throw new ArgumentOutOfRangeException("maxThreads", "maxThreads must be greater than 2"); + if (minThreads > maxThreads || minThreads < 2) + throw new ArgumentOutOfRangeException("minThreads", "minThreads must be greater than 2 and less than or equal to maxThreads"); if (m_ThreadPool != null) throw new InvalidOperationException("SmartThreadPool is already initialized"); @@ -1802,7 +1804,7 @@ namespace OpenSim.Framework startInfo.ThreadPoolName = "Util"; startInfo.IdleTimeout = 2000; startInfo.MaxWorkerThreads = maxThreads; - startInfo.MinWorkerThreads = 2; + startInfo.MinWorkerThreads = minThreads; m_ThreadPool = new SmartThreadPool(startInfo); } @@ -1877,7 +1879,7 @@ namespace OpenSim.Framework break; case FireAndForgetMethod.SmartThreadPool: if (m_ThreadPool == null) - InitThreadPool(15); + InitThreadPool(2, 15); m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj); break; case FireAndForgetMethod.Thread: @@ -2265,7 +2267,7 @@ namespace OpenSim.Framework { lock (m_syncRoot) { - m_lowQueue.Enqueue(data); + q.Enqueue(data); m_s.WaitOne(0); m_s.Release(); } @@ -2305,7 +2307,7 @@ namespace OpenSim.Framework { if (m_highQueue.Count > 0) res = m_highQueue.Dequeue(); - else + else if (m_lowQueue.Count > 0) res = m_lowQueue.Dequeue(); if (m_highQueue.Count == 0 && m_lowQueue.Count == 0) diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 87c279252b..6dd1f5b0b2 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -86,6 +86,7 @@ namespace OpenSim IConfig startupConfig = Config.Configs["Startup"]; IConfig networkConfig = Config.Configs["Network"]; + int stpMinThreads = 2; int stpMaxThreads = 15; if (startupConfig != null) @@ -112,12 +113,13 @@ namespace OpenSim if (!String.IsNullOrEmpty(asyncCallMethodStr) && Utils.EnumTryParse(asyncCallMethodStr, out asyncCallMethod)) Util.FireAndForgetMethod = asyncCallMethod; + stpMinThreads = startupConfig.GetInt("MinPoolThreads", 15); stpMaxThreads = startupConfig.GetInt("MaxPoolThreads", 15); m_consolePrompt = startupConfig.GetString("ConsolePrompt", @"Region (\R) "); } if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool) - Util.InitThreadPool(stpMaxThreads); + Util.InitThreadPool(stpMinThreads, stpMaxThreads); m_log.Info("[OPENSIM MAIN]: Using async_call_method " + Util.FireAndForgetMethod); } @@ -423,8 +425,8 @@ namespace OpenSim { RegionInfo regionInfo = presence.Scene.RegionInfo; - if (presence.Firstname.ToLower().Contains(mainParams[2].ToLower()) && - presence.Lastname.ToLower().Contains(mainParams[3].ToLower())) + if (presence.Firstname.ToLower().Equals(mainParams[2].ToLower()) && + presence.Lastname.ToLower().Equals(mainParams[3].ToLower())) { MainConsole.Instance.Output( String.Format( @@ -438,6 +440,7 @@ namespace OpenSim presence.ControllingClient.Kick("\nYou have been logged out by an administrator.\n"); presence.Scene.IncomingCloseAgent(presence.UUID, force); + break; } } diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs index 98ab433cb6..762e22a487 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs @@ -367,18 +367,7 @@ namespace OpenSim.Region.ClientStack.Linden foreach (OSD c in capsRequested) validCaps.Add(c.AsString()); - Hashtable caps = m_HostCapsObj.CapsHandlers.GetCapsDetails(true, validCaps); - - // Add the external too - foreach (KeyValuePair kvp in m_HostCapsObj.ExternalCapsHandlers) - { - if (!validCaps.Contains(kvp.Key)) - continue; - - caps[kvp.Key] = kvp.Value; - } - - string result = LLSDHelpers.SerialiseLLSDReply(caps); + string result = LLSDHelpers.SerialiseLLSDReply(m_HostCapsObj.GetCapsDetails(true, validCaps)); //m_log.DebugFormat("[CAPS] CapsRequest {0}", result); diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs index d6689d4d79..e98a4702c6 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs @@ -65,6 +65,13 @@ namespace OpenSim.Region.ClientStack.Linden /// public int DebugLevel { get; set; } + // Viewer post requests timeout in 60 secs + // https://bitbucket.org/lindenlab/viewer-release/src/421c20423df93d650cc305dc115922bb30040999/indra/llmessage/llhttpclient.cpp?at=default#cl-44 + // + private const int VIEWER_TIMEOUT = 60 * 1000; + // Just to be safe, we work on a 10 sec shorter cycle + private const int SERVER_EQ_TIME_NO_EVENTS = VIEWER_TIMEOUT - (10 * 1000); + protected Scene m_scene; private Dictionary m_ids = new Dictionary(); @@ -252,29 +259,32 @@ namespace OpenSim.Region.ClientStack.Linden List removeitems = new List(); lock (m_AvatarQueueUUIDMapping) - { - foreach (UUID ky in m_AvatarQueueUUIDMapping.Keys) - { -// m_log.DebugFormat("[EVENTQUEUE]: Found key {0} in m_AvatarQueueUUIDMapping while looking for {1}", ky, AgentID); - if (ky == agentID) - { - removeitems.Add(ky); - } - } + m_AvatarQueueUUIDMapping.Remove(agentID); - foreach (UUID ky in removeitems) - { - UUID eventQueueGetUuid = m_AvatarQueueUUIDMapping[ky]; - m_AvatarQueueUUIDMapping.Remove(ky); - - string eqgPath = GenerateEqgCapPath(eventQueueGetUuid); - MainServer.Instance.RemovePollServiceHTTPHandler("", eqgPath); - -// m_log.DebugFormat( -// "[EVENT QUEUE GET MODULE]: Removed EQG handler {0} for {1} in {2}", -// eqgPath, agentID, m_scene.RegionInfo.RegionName); - } - } +// lock (m_AvatarQueueUUIDMapping) +// { +// foreach (UUID ky in m_AvatarQueueUUIDMapping.Keys) +// { +//// m_log.DebugFormat("[EVENTQUEUE]: Found key {0} in m_AvatarQueueUUIDMapping while looking for {1}", ky, AgentID); +// if (ky == agentID) +// { +// removeitems.Add(ky); +// } +// } +// +// foreach (UUID ky in removeitems) +// { +// UUID eventQueueGetUuid = m_AvatarQueueUUIDMapping[ky]; +// m_AvatarQueueUUIDMapping.Remove(ky); +// +// string eqgPath = GenerateEqgCapPath(eventQueueGetUuid); +// MainServer.Instance.RemovePollServiceHTTPHandler("", eqgPath); +// +//// m_log.DebugFormat( +//// "[EVENT QUEUE GET MODULE]: Removed EQG handler {0} for {1} in {2}", +//// eqgPath, agentID, m_scene.RegionInfo.RegionName); +// } +// } UUID searchval = UUID.Zero; @@ -359,29 +369,9 @@ namespace OpenSim.Region.ClientStack.Linden m_AvatarQueueUUIDMapping.Add(agentID, eventQueueGetUUID); } - string eventQueueGetPath = GenerateEqgCapPath(eventQueueGetUUID); - - // Register this as a caps handler - // FIXME: Confusingly, we need to register separate as a capability so that the client is told about - // EventQueueGet when it receive capability information, but then we replace the rest handler immediately - // afterwards with the poll service. So for now, we'll pass a null instead to simplify code reading, but - // really it should be possible to directly register the poll handler as a capability. - caps.RegisterHandler( - "EventQueueGet", new RestHTTPHandler("POST", eventQueueGetPath, null, "EventQueueGet", null)); -// delegate(Hashtable m_dhttpMethod) -// { -// return ProcessQueue(m_dhttpMethod, agentID, caps); -// })); - - // This will persist this beyond the expiry of the caps handlers - // TODO: Add EventQueueGet name/description for diagnostics - MainServer.Instance.AddPollServiceHTTPHandler( - eventQueueGetPath, - new PollServiceEventArgs(null, HasEvents, GetEvents, NoEvents, agentID, 40000)); - -// m_log.DebugFormat( -// "[EVENT QUEUE GET MODULE]: Registered EQG handler {0} for {1} in {2}", -// eventQueueGetPath, agentID, m_scene.RegionInfo.RegionName); + caps.RegisterPollHandler( + "EventQueueGet", + new PollServiceEventArgs(null, GenerateEqgCapPath(eventQueueGetUUID), HasEvents, GetEvents, NoEvents, agentID, SERVER_EQ_TIME_NO_EVENTS)); Random rnd = new Random(Environment.TickCount); lock (m_ids) @@ -399,7 +389,10 @@ namespace OpenSim.Region.ClientStack.Linden Queue queue = GetQueue(agentID); if (queue != null) lock (queue) + { + //m_log.WarnFormat("POLLED FOR EVENTS BY {0} in {1} -- {2}", agentID, m_scene.RegionInfo.RegionName, queue.Count); return queue.Count > 0; + } return false; } @@ -422,7 +415,7 @@ namespace OpenSim.Region.ClientStack.Linden public Hashtable GetEvents(UUID requestID, UUID pAgentId) { if (DebugLevel >= 2) - m_log.DebugFormat("POLLED FOR EQ MESSAGES BY {0} in {1}", pAgentId, m_scene.RegionInfo.RegionName); + m_log.WarnFormat("POLLED FOR EQ MESSAGES BY {0} in {1}", pAgentId, m_scene.RegionInfo.RegionName); Queue queue = TryGetQueue(pAgentId); OSD element; diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs index 6ec111539c..7b1528490b 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetMeshModule.cs @@ -246,8 +246,8 @@ namespace OpenSim.Region.ClientStack.Linden private Scene m_scene; private MeshCapsDataThrottler m_throttler; - public PollServiceMeshEventArgs(UUID pId, Scene scene) : - base(null, null, null, null, pId, int.MaxValue) + 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, 1400000, 10000, scene, pId); @@ -361,7 +361,7 @@ namespace OpenSim.Region.ClientStack.Linden string capUrl = "/CAPS/" + UUID.Random() + "/"; // Register this as a poll service - PollServiceMeshEventArgs args = new PollServiceMeshEventArgs(agentID, m_scene); + PollServiceMeshEventArgs args = new PollServiceMeshEventArgs(capUrl, agentID, m_scene); args.Type = PollServiceEventArgs.EventType.Mesh; MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args); diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs index a42c96c981..c12b8d649b 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs @@ -210,7 +210,7 @@ namespace OpenSim.Region.ClientStack.Linden private Scene m_scene; private CapsDataThrottler m_throttler = new CapsDataThrottler(100000, 1400000,10000); public PollServiceTextureEventArgs(UUID pId, Scene scene) : - base(null, null, null, null, pId, int.MaxValue) + base(null, "", null, null, null, pId, int.MaxValue) { m_scene = scene; // x is request id, y is userid diff --git a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs index 27b09a634c..f816ad32ab 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs @@ -78,7 +78,6 @@ namespace OpenSim.Region.ClientStack.Linden private static WebFetchInvDescHandler m_webFetchHandler; - private Dictionary m_capsDict = new Dictionary(); private static Thread[] m_workerThreads = null; private static DoubleQueue m_queue = @@ -115,7 +114,6 @@ namespace OpenSim.Region.ClientStack.Linden return; m_scene.EventManager.OnRegisterCaps -= RegisterCaps; - m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps; foreach (Thread t in m_workerThreads) Watchdog.AbortThread(t.ManagedThreadId); @@ -135,7 +133,6 @@ namespace OpenSim.Region.ClientStack.Linden m_webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService); m_scene.EventManager.OnRegisterCaps += RegisterCaps; - m_scene.EventManager.OnDeregisterCaps += DeregisterCaps; if (m_workerThreads == null) { @@ -178,8 +175,8 @@ namespace OpenSim.Region.ClientStack.Linden private Scene m_scene; - public PollServiceInventoryEventArgs(Scene scene, UUID pId) : - base(null, null, null, null, pId, int.MaxValue) + public PollServiceInventoryEventArgs(Scene scene, string url, UUID pId) : + base(null, url, null, null, null, pId, int.MaxValue) { m_scene = scene; @@ -310,40 +307,39 @@ namespace OpenSim.Region.ClientStack.Linden if (m_fetchInventoryDescendents2Url == "") return; - string capUrl = "/CAPS/" + UUID.Random() + "/"; - // Register this as a poll service - PollServiceInventoryEventArgs args = new PollServiceInventoryEventArgs(m_scene, agentID); - + PollServiceInventoryEventArgs args + = new PollServiceInventoryEventArgs(m_scene, "/CAPS/" + UUID.Random() + "/", agentID); args.Type = PollServiceEventArgs.EventType.Inventory; - 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.RegisterPollHandler("FetchInventoryDescendents2", args); - caps.RegisterHandler("FetchInventoryDescendents2", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl)); - - m_capsDict[agentID] = capUrl; +// 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("FetchInventoryDescendents2", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl)); } - private void DeregisterCaps(UUID agentID, Caps caps) - { - string capUrl; - - if (m_capsDict.TryGetValue(agentID, out capUrl)) - { - MainServer.Instance.RemoveHTTPHandler("", capUrl); - m_capsDict.Remove(agentID); - } - } +// private void DeregisterCaps(UUID agentID, Caps caps) +// { +// string capUrl; +// +// if (m_capsDict.TryGetValue(agentID, out capUrl)) +// { +// MainServer.Instance.RemoveHTTPHandler("", capUrl); +// m_capsDict.Remove(agentID); +// } +// } private void DoInventoryRequests() { @@ -353,7 +349,8 @@ namespace OpenSim.Region.ClientStack.Linden aPollRequest poolreq = m_queue.Dequeue(); - poolreq.thepoll.Process(poolreq); + if (poolreq != null && poolreq.thepoll != null) + poolreq.thepoll.Process(poolreq); } } } diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index dfad485d41..ac5e77e4b0 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -3816,6 +3816,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP ResendPrimUpdate(update); } +// OpenSim.Framework.Lazy> objectUpdateBlocks = new OpenSim.Framework.Lazy>(); +// OpenSim.Framework.Lazy> compressedUpdateBlocks = new OpenSim.Framework.Lazy>(); +// OpenSim.Framework.Lazy> terseUpdateBlocks = new OpenSim.Framework.Lazy>(); +// OpenSim.Framework.Lazy> terseAgentUpdateBlocks = new OpenSim.Framework.Lazy>(); +// +// OpenSim.Framework.Lazy> objectUpdates = new OpenSim.Framework.Lazy>(); +// OpenSim.Framework.Lazy> compressedUpdates = new OpenSim.Framework.Lazy>(); +// OpenSim.Framework.Lazy> terseUpdates = new OpenSim.Framework.Lazy>(); +// OpenSim.Framework.Lazy> terseAgentUpdates = new OpenSim.Framework.Lazy>(); + + private void ProcessEntityUpdates(int maxUpdates) { OpenSim.Framework.Lazy> objectUpdateBlocks = new OpenSim.Framework.Lazy>(); @@ -3828,6 +3839,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP OpenSim.Framework.Lazy> terseUpdates = new OpenSim.Framework.Lazy>(); OpenSim.Framework.Lazy> terseAgentUpdates = new OpenSim.Framework.Lazy>(); +// objectUpdateBlocks.Value.Clear(); +// compressedUpdateBlocks.Value.Clear(); +// terseUpdateBlocks.Value.Clear(); +// terseAgentUpdateBlocks.Value.Clear(); +// objectUpdates.Value.Clear(); +// compressedUpdates.Value.Clear(); +// terseUpdates.Value.Clear(); +// terseAgentUpdates.Value.Clear(); + // Check to see if this is a flush if (maxUpdates <= 0) { diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 77b07ed7a3..b4ac021e85 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -809,8 +809,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP } PacketPool.Instance.ReturnPacket(packet); + m_dataPresentEvent.Set(); + } + private AutoResetEvent m_dataPresentEvent = new AutoResetEvent(false); + /// /// Start the process of sending a packet to the client. /// @@ -1730,6 +1734,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Action generic every round Action clientPacketHandler = ClientOutgoingPacketHandler; +// while (true) while (base.IsRunningOutbound) { m_scene.ThreadAlive(2); @@ -1791,8 +1796,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP // If nothing was sent, sleep for the minimum amount of time before a // token bucket could get more tokens - if (!m_packetSent) - Thread.Sleep((int)TickCountResolution); + //if (!m_packetSent) + // Thread.Sleep((int)TickCountResolution); + m_dataPresentEvent.WaitOne(100); Watchdog.UpdateThread(); } diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs index a60b314447..505bb5370e 100644 --- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs @@ -75,10 +75,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments public void AddRegion(Scene scene) { m_scene = scene; - m_scene.RegisterModuleInterface(this); - if (Enabled) { + // Only register module with scene if it is enabled. All callers check for a null attachments module. + // Ideally, there should be a null attachments module for when this core attachments module has been + // disabled. Registering only when enabled allows for other attachments module implementations. + m_scene.RegisterModuleInterface(this); m_scene.EventManager.OnNewClient += SubscribeToClientEvents; m_scene.EventManager.OnStartScript += (localID, itemID) => HandleScriptStateChange(localID, true); m_scene.EventManager.OnStopScript += (localID, itemID) => HandleScriptStateChange(localID, false); diff --git a/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs b/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs index 6ce6227a1f..0c759df3e8 100644 --- a/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Caps/CapabilitiesModule.cs @@ -80,7 +80,7 @@ namespace OpenSim.Region.CoreModules.Framework MainConsole.Instance.Commands.AddCommand( "Comms", false, "show caps stats by user", - "show caps stats [ ]", + "show caps stats by user [ ]", "Shows statistics on capabilities use by user.", "If a user name is given, then prints a detailed breakdown of caps use ordered by number of requests received.", HandleShowCapsStatsByUserCommand); @@ -296,27 +296,31 @@ namespace OpenSim.Region.CoreModules.Framework if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_scene) return; - StringBuilder caps = new StringBuilder(); - caps.AppendFormat("Region {0}:\n", m_scene.RegionInfo.RegionName); + StringBuilder capsReport = new StringBuilder(); + capsReport.AppendFormat("Region {0}:\n", m_scene.RegionInfo.RegionName); lock (m_capsObjects) { foreach (KeyValuePair kvp in m_capsObjects) { - caps.AppendFormat("** Circuit {0}:\n", kvp.Key); + capsReport.AppendFormat("** Circuit {0}:\n", kvp.Key); + Caps caps = kvp.Value; - for (IDictionaryEnumerator kvp2 = kvp.Value.CapsHandlers.GetCapsDetails(false, null).GetEnumerator(); kvp2.MoveNext(); ) + for (IDictionaryEnumerator kvp2 = caps.CapsHandlers.GetCapsDetails(false, null).GetEnumerator(); kvp2.MoveNext(); ) { Uri uri = new Uri(kvp2.Value.ToString()); - caps.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery); + capsReport.AppendFormat(m_showCapsCommandFormat, kvp2.Key, uri.PathAndQuery); } - foreach (KeyValuePair kvp3 in kvp.Value.ExternalCapsHandlers) - caps.AppendFormat(m_showCapsCommandFormat, kvp3.Key, kvp3.Value); + foreach (KeyValuePair kvp2 in caps.GetPollHandlers()) + capsReport.AppendFormat(m_showCapsCommandFormat, kvp2.Key, kvp2.Value.Url); + + foreach (KeyValuePair kvp3 in caps.ExternalCapsHandlers) + capsReport.AppendFormat(m_showCapsCommandFormat, kvp3.Key, kvp3.Value); } } - MainConsole.Instance.Output(caps.ToString()); + MainConsole.Instance.Output(capsReport.ToString()); } private void HandleShowCapsStatsByCapCommand(string module, string[] cmdParams) @@ -374,7 +378,16 @@ namespace OpenSim.Region.CoreModules.Framework { receivedStats[sp.Name] = reqHandler.RequestsReceived; handledStats[sp.Name] = reqHandler.RequestsHandled; - } + } + else + { + PollServiceEventArgs pollHandler = null; + if (caps.TryGetPollHandler(capName, out pollHandler)) + { + receivedStats[sp.Name] = pollHandler.RequestsReceived; + handledStats[sp.Name] = pollHandler.RequestsHandled; + } + } } ); @@ -405,11 +418,9 @@ namespace OpenSim.Region.CoreModules.Framework Caps caps = m_scene.CapsModule.GetCapsForUser(sp.UUID); if (caps == null) - return; + return; - Dictionary capsHandlers = caps.CapsHandlers.GetCapsHandlers(); - - foreach (IRequestHandler reqHandler in capsHandlers.Values) + foreach (IRequestHandler reqHandler in caps.CapsHandlers.GetCapsHandlers().Values) { string reqName = reqHandler.Name ?? ""; @@ -424,6 +435,23 @@ namespace OpenSim.Region.CoreModules.Framework handledStats[reqName] += reqHandler.RequestsHandled; } } + + foreach (KeyValuePair kvp in caps.GetPollHandlers()) + { + string name = kvp.Key; + PollServiceEventArgs pollHandler = kvp.Value; + + if (!receivedStats.ContainsKey(name)) + { + receivedStats[name] = pollHandler.RequestsReceived; + handledStats[name] = pollHandler.RequestsHandled; + } + else + { + receivedStats[name] += pollHandler.RequestsReceived; + handledStats[name] += pollHandler.RequestsHandled; + } + } } ); @@ -486,12 +514,16 @@ namespace OpenSim.Region.CoreModules.Framework if (caps == null) return; - Dictionary capsHandlers = caps.CapsHandlers.GetCapsHandlers(); + List capRows = new List(); - foreach (IRequestHandler reqHandler in capsHandlers.Values.OrderByDescending(rh => rh.RequestsReceived)) - { - cdt.AddRow(reqHandler.Name, reqHandler.RequestsReceived, reqHandler.RequestsHandled); - } + foreach (IRequestHandler reqHandler in caps.CapsHandlers.GetCapsHandlers().Values) + capRows.Add(new CapTableRow(reqHandler.Name, reqHandler.RequestsReceived, reqHandler.RequestsHandled)); + + foreach (KeyValuePair kvp in caps.GetPollHandlers()) + capRows.Add(new CapTableRow(kvp.Key, kvp.Value.RequestsReceived, kvp.Value.RequestsHandled)); + + foreach (CapTableRow ctr in capRows.OrderByDescending(ctr => ctr.RequestsReceived)) + cdt.AddRow(ctr.Name, ctr.RequestsReceived, ctr.RequestsHandled); sb.Append(cdt.ToString()); */ @@ -525,6 +557,14 @@ namespace OpenSim.Region.CoreModules.Framework totalRequestsReceived += reqHandler.RequestsReceived; totalRequestsHandled += reqHandler.RequestsHandled; } + + Dictionary capsPollHandlers = caps.GetPollHandlers(); + + foreach (PollServiceEventArgs handler in capsPollHandlers.Values) + { + totalRequestsReceived += handler.RequestsReceived; + totalRequestsHandled += handler.RequestsHandled; + } cdt.AddRow(sp.Name, sp.IsChildAgent ? "child" : "root", totalRequestsReceived, totalRequestsHandled); } @@ -533,5 +573,19 @@ namespace OpenSim.Region.CoreModules.Framework sb.Append(cdt.ToString()); */ } + + private class CapTableRow + { + public string Name { get; set; } + public int RequestsReceived { get; set; } + public int RequestsHandled { get; set; } + + public CapTableRow(string name, int requestsReceived, int requestsHandled) + { + Name = name; + RequestsReceived = requestsReceived; + RequestsHandled = requestsHandled; + } + } } } diff --git a/OpenSim/Region/CoreModules/Framework/GridServiceThrottle/GridServiceThrottleModule.cs b/OpenSim/Region/CoreModules/Framework/ServiceThrottle/ServiceThrottleModule.cs similarity index 51% rename from OpenSim/Region/CoreModules/Framework/GridServiceThrottle/GridServiceThrottleModule.cs rename to OpenSim/Region/CoreModules/Framework/ServiceThrottle/ServiceThrottleModule.cs index f1eb1ad3a9..a70261e938 100644 --- a/OpenSim/Region/CoreModules/Framework/GridServiceThrottle/GridServiceThrottleModule.cs +++ b/OpenSim/Region/CoreModules/Framework/ServiceThrottle/ServiceThrottleModule.cs @@ -42,23 +42,37 @@ using GridRegion = OpenSim.Services.Interfaces.GridRegion; namespace OpenSim.Region.CoreModules.Framework { [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GridServiceThrottleModule")] - public class GridServiceThrottleModule : ISharedRegionModule + public class ServiceThrottleModule : ISharedRegionModule, IServiceThrottleModule { private static readonly ILog m_log = LogManager.GetLogger( MethodBase.GetCurrentMethod().DeclaringType); private readonly List m_scenes = new List(); + private System.Timers.Timer m_timer = new System.Timers.Timer(); - private OpenSim.Framework.BlockingQueue m_RequestQueue = new OpenSim.Framework.BlockingQueue(); + private Queue m_RequestQueue = new Queue(); + private Dictionary> m_Pending = new Dictionary>(); + private int m_Interval; + + #region ISharedRegionModule public void Initialise(IConfigSource config) { - Watchdog.StartThread( - ProcessQueue, - "GridServiceRequestThread", - ThreadPriority.BelowNormal, - true, - false); + m_Interval = Util.GetConfigVarFromSections(config, "Interval", new string[] { "ServiceThrottle" }, 5000); + + m_timer = new System.Timers.Timer(); + m_timer.AutoReset = false; + m_timer.Enabled = true; + m_timer.Interval = 15000; // 15 secs at first + m_timer.Elapsed += ProcessQueue; + m_timer.Start(); + + //Watchdog.StartThread( + // ProcessQueue, + // "GridServiceRequestThread", + // ThreadPriority.BelowNormal, + // true, + // false); } public void AddRegion(Scene scene) @@ -66,7 +80,9 @@ namespace OpenSim.Region.CoreModules.Framework lock (m_scenes) { m_scenes.Add(scene); + scene.RegisterModuleInterface(this); scene.EventManager.OnNewClient += OnNewClient; + scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; } } @@ -83,11 +99,6 @@ namespace OpenSim.Region.CoreModules.Framework } } - void OnNewClient(IClientAPI client) - { - client.OnRegionHandleRequest += OnRegionHandleRequest; - } - public void PostInitialise() { } @@ -98,7 +109,7 @@ namespace OpenSim.Region.CoreModules.Framework public string Name { - get { return "GridServiceThrottleModule"; } + get { return "ServiceThrottleModule"; } } public Type ReplaceableInterface @@ -106,9 +117,31 @@ namespace OpenSim.Region.CoreModules.Framework get { return null; } } + #endregion ISharedRegionMOdule + + #region Events + + void OnNewClient(IClientAPI client) + { + client.OnRegionHandleRequest += OnRegionHandleRequest; + } + + void OnMakeRootAgent(ScenePresence obj) + { + lock (m_timer) + { + if (!m_timer.Enabled) + { + m_timer.Interval = m_Interval; + m_timer.Enabled = true; + m_timer.Start(); + } + } + } + public void OnRegionHandleRequest(IClientAPI client, UUID regionID) { - //m_log.DebugFormat("[GRIDSERVICE THROTTLE]: RegionHandleRequest {0}", regionID); + //m_log.DebugFormat("[SERVICE THROTTLE]: RegionHandleRequest {0}", regionID); ulong handle = 0; if (IsLocalRegionHandle(regionID, out handle)) { @@ -116,11 +149,83 @@ namespace OpenSim.Region.CoreModules.Framework return; } - GridRegionRequest request = new GridRegionRequest(client, regionID); - m_RequestQueue.Enqueue(request); + Action action = delegate + { + GridRegion r = m_scenes[0].GridService.GetRegionByUUID(UUID.Zero, regionID); + + if (r != null && r.RegionHandle != 0) + client.SendRegionHandle(regionID, r.RegionHandle); + }; + + Enqueue("region", regionID.ToString(), action); + } + + #endregion Events + + #region IServiceThrottleModule + + public void Enqueue(string category, string itemid, Action continuation) + { + lock (m_RequestQueue) + { + if (m_Pending.ContainsKey(category)) + { + if (m_Pending[category].Contains(itemid)) + // Don't enqueue, it's already pending + return; + } + else + m_Pending.Add(category, new List()); + + m_Pending[category].Add(itemid); + + m_RequestQueue.Enqueue(delegate + { + lock (m_RequestQueue) + m_Pending[category].Remove(itemid); + + continuation(); + }); + } + } + + #endregion IServiceThrottleModule + + #region Process Continuation Queue + + private void ProcessQueue(object sender, System.Timers.ElapsedEventArgs e) + { + //m_log.DebugFormat("[YYY]: Process queue with {0} continuations", m_RequestQueue.Count); + + while (m_RequestQueue.Count > 0) + { + Action continuation = null; + lock (m_RequestQueue) + continuation = m_RequestQueue.Dequeue(); + + if (continuation != null) + continuation(); + } + + if (AreThereRootAgents()) + { + lock (m_timer) + { + m_timer.Interval = 1000; // 1 sec + m_timer.Enabled = true; + m_timer.Start(); + } + } + else + lock (m_timer) + m_timer.Enabled = false; } + #endregion Process Continuation Queue + + #region Misc + private bool IsLocalRegionHandle(UUID regionID, out ulong regionHandle) { regionHandle = 0; @@ -133,31 +238,19 @@ namespace OpenSim.Region.CoreModules.Framework return false; } - private void ProcessQueue() + private bool AreThereRootAgents() { - while (true) + foreach (Scene s in m_scenes) { - Watchdog.UpdateThread(); - - GridRegionRequest request = m_RequestQueue.Dequeue(); - GridRegion r = m_scenes[0].GridService.GetRegionByUUID(UUID.Zero, request.regionID); - - if (r != null && r.RegionHandle != 0) - request.client.SendRegionHandle(request.regionID, r.RegionHandle); - + foreach (ScenePresence sp in s.GetScenePresences()) + if (!sp.IsChildAgent) + return true; } + + return false; } + + #endregion Misc } - class GridRegionRequest - { - public IClientAPI client; - public UUID regionID; - - public GridRegionRequest(IClientAPI c, UUID r) - { - client = c; - regionID = r; - } - } } diff --git a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs index a5280939fc..a91adfab13 100644 --- a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs +++ b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs @@ -56,13 +56,10 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement protected bool m_Enabled; protected List m_Scenes = new List(); + protected IServiceThrottleModule m_ServiceThrottle; // The cache protected Dictionary m_UserCache = new Dictionary(); - // Throttle the name requests - private OpenSim.Framework.BlockingQueue m_RequestQueue = new OpenSim.Framework.BlockingQueue(); - - #region ISharedRegionModule public void Initialise(IConfigSource config) @@ -115,6 +112,8 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement public void RegionLoaded(Scene s) { + if (m_Enabled && m_ServiceThrottle == null) + m_ServiceThrottle = s.RequestModuleInterface(); } public void PostInitialise() @@ -154,7 +153,7 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement client.OnAvatarPickerRequest -= new AvatarPickerRequest(HandleAvatarPickerRequest); } - void HandleUUIDNameRequest(UUID uuid, IClientAPI remote_client) + void HandleUUIDNameRequest(UUID uuid, IClientAPI client) { // m_log.DebugFormat( // "[USER MANAGEMENT MODULE]: Handling request for name binding of UUID {0} from {1}", @@ -162,12 +161,31 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement if (m_Scenes[0].LibraryService != null && (m_Scenes[0].LibraryService.LibraryRootFolder.Owner == uuid)) { - remote_client.SendNameReply(uuid, "Mr", "OpenSim"); + client.SendNameReply(uuid, "Mr", "OpenSim"); } else { - NameRequest request = new NameRequest(remote_client, uuid); - m_RequestQueue.Enqueue(request); + string[] names = new string[2]; + if (TryGetUserNamesFromCache(uuid, names)) + { + client.SendNameReply(uuid, names[0], names[1]); + return; + } + + // Not found in cache, queue continuation + m_ServiceThrottle.Enqueue("name", uuid.ToString(), delegate + { + //m_log.DebugFormat("[YYY]: Name request {0}", uuid); + bool foundRealName = TryGetUserNames(uuid, names); + + if (names.Length == 2) + { + if (!foundRealName) + m_log.DebugFormat("[USER MANAGEMENT MODULE]: Sending {0} {1} for {2} to {3} since no bound name found", names[0], names[1], uuid, client.Name); + + client.SendNameReply(uuid, names[0], names[1]); + } + }); } } @@ -283,15 +301,27 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement } /// - /// Try to get the names bound to the given uuid. + /// /// - /// True if the name was found, false if not. - /// - /// The array of names if found. If not found, then names[0] = "Unknown" and names[1] = "User" - private bool TryGetUserNames(UUID uuid, out string[] names) + /// + /// Caller please provide a properly instantiated array for names, string[2] + /// + private bool TryGetUserNames(UUID uuid, string[] names) { - names = new string[2]; + if (names == null) + names = new string[2]; + if (TryGetUserNamesFromCache(uuid, names)) + return true; + + if (TryGetUserNamesFromServices(uuid, names)) + return true; + + return false; + } + + private bool TryGetUserNamesFromCache(UUID uuid, string[] names) + { lock (m_UserCache) { if (m_UserCache.ContainsKey(uuid)) @@ -303,6 +333,17 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement } } + return false; + } + + /// + /// Try to get the names bound to the given uuid, from the services. + /// + /// True if the name was found, false if not. + /// + /// The array of names if found. If not found, then names[0] = "Unknown" and names[1] = "User" + private bool TryGetUserNamesFromServices(UUID uuid, string[] names) + { UserAccount account = m_Scenes[0].UserAccountService.GetUserAccount(UUID.Zero, uuid); if (account != null) @@ -387,18 +428,11 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement public string GetUserName(UUID uuid) { - string[] names; - TryGetUserNames(uuid, out names); + string[] names = new string[2]; + TryGetUserNames(uuid, names); - if (names.Length == 2) - { - string firstname = names[0]; - string lastname = names[1]; + return names[0] + " " + names[1]; - return firstname + " " + lastname; - } - - return "(hippos)"; } public string GetUserHomeURL(UUID userID) @@ -598,13 +632,6 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement protected void Init() { RegisterConsoleCmds(); - Watchdog.StartThread( - ProcessQueue, - "NameRequestThread", - ThreadPriority.BelowNormal, - true, - false); - } protected void RegisterConsoleCmds() @@ -674,39 +701,6 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement MainConsole.Instance.Output(cdt.ToString()); } - private void ProcessQueue() - { - while (true) - { - Watchdog.UpdateThread(); - - NameRequest request = m_RequestQueue.Dequeue(); - string[] names; - bool foundRealName = TryGetUserNames(request.uuid, out names); - - if (names.Length == 2) - { - if (!foundRealName) - m_log.DebugFormat("[USER MANAGEMENT MODULE]: Sending {0} {1} for {2} to {3} since no bound name found", names[0], names[1], request.uuid, request.client.Name); - - request.client.SendNameReply(request.uuid, names[0], names[1]); - } - - } - } - - } - - class NameRequest - { - public IClientAPI client; - public UUID uuid; - - public NameRequest(IClientAPI c, UUID n) - { - client = c; - uuid = n; - } } } \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs index 2a4d4404e6..1983fedaa3 100644 --- a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs +++ b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs @@ -219,7 +219,8 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp string uri = "/lslhttp/" + urlcode.ToString(); - PollServiceEventArgs args = new PollServiceEventArgs(HttpRequestHandler, HasEvents, GetEvents, NoEvents, urlcode, 25000); + PollServiceEventArgs args + = new PollServiceEventArgs(HttpRequestHandler, uri, HasEvents, GetEvents, NoEvents, urlcode, 25000); args.Type = PollServiceEventArgs.EventType.LslHttp; m_HttpServer.AddPollServiceHTTPHandler(uri, args); @@ -266,7 +267,8 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp string uri = "/lslhttps/" + urlcode.ToString(); - PollServiceEventArgs args = new PollServiceEventArgs(HttpRequestHandler, HasEvents, GetEvents, NoEvents, urlcode, 25000); + PollServiceEventArgs args + = new PollServiceEventArgs(HttpRequestHandler, uri, HasEvents, GetEvents, NoEvents, urlcode, 25000); args.Type = PollServiceEventArgs.EventType.LslHttp; m_HttpsServer.AddPollServiceHTTPHandler(uri, args); diff --git a/OpenSim/Region/Framework/Interfaces/IServiceThrottleModule.cs b/OpenSim/Region/Framework/Interfaces/IServiceThrottleModule.cs new file mode 100644 index 0000000000..198256fdde --- /dev/null +++ b/OpenSim/Region/Framework/Interfaces/IServiceThrottleModule.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface IServiceThrottleModule + { + /// + /// Enqueue a continuation meant to get a resource from elsewhere. + /// As usual with CPS, caller beware: if that continuation is a never-ending computation, + /// the whole thread will be blocked, and no requests are processed + /// + /// Category of the resource (e.g. name, region) + /// The resource identifier + /// The continuation to be executed + void Enqueue(string category, string itemid, Action continuation); + } + +} diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs index 8c9c006aac..b0a29c048e 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCModule.cs @@ -116,7 +116,8 @@ namespace OpenSim.Region.OptionalModules.World.NPC return false; // Delete existing npc attachments - scene.AttachmentsModule.DeleteAttachmentsFromScene(npc, false); + if(scene.AttachmentsModule != null) + scene.AttachmentsModule.DeleteAttachmentsFromScene(npc, false); // XXX: We can't just use IAvatarFactoryModule.SetAppearance() yet // since it doesn't transfer attachments @@ -125,7 +126,8 @@ namespace OpenSim.Region.OptionalModules.World.NPC npc.Appearance = npcAppearance; // Rez needed npc attachments - scene.AttachmentsModule.RezAttachments(npc); + if (scene.AttachmentsModule != null) + scene.AttachmentsModule.RezAttachments(npc); IAvatarFactoryModule module = scene.RequestModuleInterface(); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs b/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs index 928b350645..0f11c4a158 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSActorAvatarMove.cs @@ -130,6 +130,7 @@ public class BSActorAvatarMove : BSActor SetVelocityAndTarget(m_controllingPrim.RawVelocity, m_controllingPrim.TargetVelocity, true /* inTaintTime */); m_physicsScene.BeforeStep += Mover; + m_controllingPrim.OnPreUpdateProperty += Process_OnPreUpdateProperty; m_walkingUpStairs = 0; } @@ -139,6 +140,7 @@ public class BSActorAvatarMove : BSActor { if (m_velocityMotor != null) { + m_controllingPrim.OnPreUpdateProperty -= Process_OnPreUpdateProperty; m_physicsScene.BeforeStep -= Mover; m_velocityMotor = null; } @@ -197,7 +199,7 @@ public class BSActorAvatarMove : BSActor { if (m_controllingPrim.Flying) { - // Flying and not collising and velocity nearly zero. + // Flying and not colliding and velocity nearly zero. m_controllingPrim.ZeroMotion(true /* inTaintTime */); } } @@ -266,6 +268,19 @@ public class BSActorAvatarMove : BSActor } } + // Called just as the property update is received from the physics engine. + // Do any mode necessary for avatar movement. + private void Process_OnPreUpdateProperty(ref EntityProperties entprop) + { + // Don't change position if standing on a stationary object. + if (m_controllingPrim.IsStationary) + { + entprop.Position = m_controllingPrim.RawPosition; + m_physicsScene.PE.SetTranslation(m_controllingPrim.PhysBody, entprop.Position, entprop.Rotation); + } + + } + // Decide if the character is colliding with a low object and compute a force to pop the // avatar up so it can walk up and over the low objects. private OMV.Vector3 WalkUpStairs() diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index c9e3ca0ead..59e7f5ffbe 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -709,10 +709,10 @@ public sealed class BSCharacter : BSPhysObject // the world that things have changed. public override void UpdateProperties(EntityProperties entprop) { - // Don't change position if standing on a stationary object. - if (!IsStationary) - RawPosition = entprop.Position; + // Let anyone (like the actors) modify the updated properties before they are pushed into the object and the simulator. + TriggerPreUpdatePropertyAction(ref entprop); + RawPosition = entprop.Position; RawOrientation = entprop.Rotation; // Smooth velocity. OpenSimulator is VERY sensitive to changes in velocity of the avatar @@ -740,7 +740,7 @@ public sealed class BSCharacter : BSPhysObject // Linkset.UpdateProperties(UpdatedProperties.EntPropUpdates, this); // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. - // base.RequestPhysicsterseUpdate(); + // PhysScene.PostUpdate(this); DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", LocalID, RawPosition, RawOrientation, RawVelocity, _acceleration, _rotationalVelocity); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index a41eaf8617..fc4545f4e3 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -103,9 +103,10 @@ public abstract class BSPhysObject : PhysicsActor CollisionsLastTickStep = -1; SubscribedEventsMs = 0; - CollidingStep = 0; - CollidingGroundStep = 0; - CollisionAccumulation = 0; + // Crazy values that will never be true + CollidingStep = BSScene.NotASimulationStep; + CollidingGroundStep = BSScene.NotASimulationStep; + CollisionAccumulation = BSScene.NotASimulationStep; ColliderIsMoving = false; CollisionScore = 0; @@ -349,7 +350,7 @@ public abstract class BSPhysObject : PhysicsActor if (value) CollidingStep = PhysScene.SimulationStep; else - CollidingStep = 0; + CollidingStep = BSScene.NotASimulationStep; } } public override bool CollidingGround { @@ -359,7 +360,7 @@ public abstract class BSPhysObject : PhysicsActor if (value) CollidingGroundStep = PhysScene.SimulationStep; else - CollidingGroundStep = 0; + CollidingGroundStep = BSScene.NotASimulationStep; } } public override bool CollidingObj { @@ -368,7 +369,7 @@ public abstract class BSPhysObject : PhysicsActor if (value) CollidingObjectStep = PhysScene.SimulationStep; else - CollidingObjectStep = 0; + CollidingObjectStep = BSScene.NotASimulationStep; } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 214271b247..41aca3b2a7 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -97,6 +97,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters internal long m_simulationStep = 0; // The current simulation step. public long SimulationStep { get { return m_simulationStep; } } + // A number to use for SimulationStep that is probably not any step value + // Used by the collision code (which remembers the step when a collision happens) to remember not any simulation step. + public static long NotASimulationStep = -1234; internal float LastTimeStep { get; private set; } // The simulation time from the last invocation of Simulate() diff --git a/OpenSim/Region/RegionCombinerModule/RegionCombinerIndividualEventForwarder.cs b/OpenSim/Region/RegionCombinerModule/RegionCombinerIndividualEventForwarder.cs index f424e7f42c..83732e205d 100644 --- a/OpenSim/Region/RegionCombinerModule/RegionCombinerIndividualEventForwarder.cs +++ b/OpenSim/Region/RegionCombinerModule/RegionCombinerIndividualEventForwarder.cs @@ -51,7 +51,8 @@ namespace OpenSim.Region.RegionCombinerModule m_virtScene.UnSubscribeToClientPrimEvents(client); m_virtScene.UnSubscribeToClientPrimRezEvents(client); m_virtScene.UnSubscribeToClientInventoryEvents(client); - ((AttachmentsModule)m_virtScene.AttachmentsModule).UnsubscribeFromClientEvents(client); + if(m_virtScene.AttachmentsModule != null) + ((AttachmentsModule)m_virtScene.AttachmentsModule).UnsubscribeFromClientEvents(client); //m_virtScene.UnSubscribeToClientTeleportEvents(client); m_virtScene.UnSubscribeToClientScriptEvents(client); @@ -66,7 +67,8 @@ namespace OpenSim.Region.RegionCombinerModule client.OnRezObject += LocalRezObject; m_rootScene.SubscribeToClientInventoryEvents(client); - ((AttachmentsModule)m_rootScene.AttachmentsModule).SubscribeToClientEvents(client); + if (m_rootScene.AttachmentsModule != null) + ((AttachmentsModule)m_rootScene.AttachmentsModule).SubscribeToClientEvents(client); //m_rootScene.SubscribeToClientTeleportEvents(client); m_rootScene.SubscribeToClientScriptEvents(client); diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index f28dd3eb35..8055886ba5 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -1723,5 +1723,9 @@ MaxStringSpace = 0 ;; {MaxDistance} {} {Cut-off distance at which sounds will not be sent to users} {100.0} MaxDistance = 100.0 +[ServiceThrottle] + ;; Default time interval (in ms) for the throttle service thread to wake up + Interval = 5000 + [Modules] Include-modules = "addon-modules/*/config/*.ini"