From 8c7149063bce41cac6543757c7b917583f21ea90 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 12 Jun 2012 01:23:40 +0100 Subject: [PATCH 1/5] In PresenceDetector.OnConnectionClose(), use the IsChildAgent check already available on IClientAPI.SceneAgent rather than retrieving it again by scanning all scenes. --- OpenSim/Framework/IClientAPI.cs | 22 ++++++++++++++----- .../ClientStack/Linden/UDP/LLClientView.cs | 4 ++-- .../Presence/PresenceDetector.cs | 13 +---------- OpenSim/Region/Framework/Scenes/Scene.cs | 7 +++--- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 869b069f90..f8b6a8463a 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -740,14 +740,24 @@ namespace OpenSim.Framework /// string Name { get; } - /// - /// Determines whether the client thread is doing anything or not. - /// + /// + /// True if the client is active (sending and receiving new UDP messages). False if the client is closing. + /// bool IsActive { get; set; } - /// - /// Determines whether the client is or has been removed from a given scene - /// + /// + /// Set if the client is closing due to a logout request or because of too much time since last ack. + /// + /// + /// Do not use this flag if you want to know if the client is closing, since it will not be set in other + /// circumstances (e.g. if a child agent is closed or the agent is kicked off the simulator). Use IsActive + /// instead. + /// + /// Only set for root agents. + /// + /// TODO: Too much time since last ack should probably be a separate property, or possibly part of a state + /// machine. + /// bool IsLoggingOut { get; set; } bool SendLogoutPacketWhenClosing { set; } diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 74b9c6d773..c4f167e2a1 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -490,12 +490,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public void Close() { + IsActive = false; + m_log.DebugFormat( "[CLIENT]: Close has been called for {0} attached to scene {1}", Name, m_scene.RegionInfo.RegionName); - IsActive = false; - // Shutdown the image manager ImageManager.Close(); diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/PresenceDetector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/PresenceDetector.cs index ccfbf78a29..b43745c846 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/PresenceDetector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/PresenceDetector.cs @@ -64,7 +64,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence scene.EventManager.OnNewClient -= OnNewClient; m_PresenceService.LogoutRegionAgents(scene.RegionInfo.RegionID); - } public void OnMakeRootAgent(ScenePresence sp) @@ -80,18 +79,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence public void OnConnectionClose(IClientAPI client) { - if (client.IsLoggingOut) + if (client.IsLoggingOut && !client.SceneAgent.IsChildAgent) { - object sp = null; - if (client.Scene.TryGetScenePresence(client.AgentId, out sp)) - { - if (sp is ScenePresence) - { - if (((ScenePresence)sp).IsChildAgent) - return; - } - } - // m_log.DebugFormat("[PRESENCE DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName); m_PresenceService.LogoutAgent(client.SessionId); } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 7afde38c23..1305d83018 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -3463,10 +3463,9 @@ namespace OpenSim.Region.Framework.Scenes // Or the same user is trying to be root twice here, won't work. // Kill it. m_log.DebugFormat( - "[SCENE]: Zombie scene presence detected for {0} in {1}", - agent.AgentID, - RegionInfo.RegionName - ); + "[SCENE]: Zombie scene presence detected for {0} {1} in {2}", + sp.Name, sp.UUID, RegionInfo.RegionName); + sp.ControllingClient.Close(); sp = null; } From c89db34fc4372be7ff94d92472131b2c33de8605 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 12 Jun 2012 02:03:31 +0100 Subject: [PATCH 2/5] If the simulator closes a root agent due to ack timeout, then send the client a kick message with that reason, in case it is somehow still listening. --- .../ClientStack/Linden/UDP/LLUDPServer.cs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 7f86491f4c..2036f61f22 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -558,9 +558,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (!client.IsLoggingOut && (Environment.TickCount & Int32.MaxValue) - udpClient.TickLastPacketReceived > timeoutTicks) { - m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); + m_log.WarnFormat( + "[LLUDPSERVER]: Ack timeout for {0} {1}, disconnecting", + client.Name, client.AgentId); + StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); - RemoveClient(client); + LogoutClientDueToTimeout(client); return; } @@ -1121,6 +1124,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP Util.FireAndForget(o => client.Close()); } + private void LogoutClientDueToTimeout(IClientAPI client) + { + // We must set IsLoggingOut synchronously so that we can stop the packet loop reinvoking this method. + client.IsLoggingOut = true; + + // Fire this out on a different thread so that we don't hold up outgoing packet processing for + // everybody else if this is being called due to an ack timeout. + // This is the same as processing as the async process of a logout request. + Util.FireAndForget( + o => + { if (!client.SceneAgent.IsChildAgent) + client.Kick("Simulator logged you out due to connection timeout"); + client.Close(); }); + } + private void IncomingPacketHandler() { // Set this culture for the thread that incoming packets are received From b099f26376a7d671eeb9113dd7611cfcb0e57de0 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 12 Jun 2012 02:16:36 +0100 Subject: [PATCH 3/5] Set IClientAPI.IsActive = false early on client removal due to ack timeout rather than using IsLoggingOut flag. IsActive is more appropriate since unack timeout is not due to voluntary logout. This is in line with operations such as manual kick that do not set the IsLoggingOut flag. It's also slightly better race-wise since it reduces the chance of this operation clashing with another reason for client deactivation (e.g. manual kick). --- OpenSim/Framework/IClientAPI.cs | 9 +-- .../ClientStack/Linden/UDP/LLUDPServer.cs | 62 ++++++++++--------- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index f8b6a8463a..2ea6de547a 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -741,22 +741,19 @@ namespace OpenSim.Framework string Name { get; } /// - /// True if the client is active (sending and receiving new UDP messages). False if the client is closing. + /// True if the client is active (sending and receiving new UDP messages). False if the client is being closed. /// bool IsActive { get; set; } /// - /// Set if the client is closing due to a logout request or because of too much time since last ack. + /// Set if the client is closing due to a logout request /// /// /// Do not use this flag if you want to know if the client is closing, since it will not be set in other /// circumstances (e.g. if a child agent is closed or the agent is kicked off the simulator). Use IsActive - /// instead. + /// instead with a IClientAPI.SceneAgent.IsChildAgent check if necessary. /// /// Only set for root agents. - /// - /// TODO: Too much time since last ack should probably be a separate property, or possibly part of a state - /// machine. /// bool IsLoggingOut { get; set; } diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 2036f61f22..37d29439a6 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -555,15 +555,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (udpClient.IsPaused) timeoutTicks = m_pausedAckTimeout; - if (!client.IsLoggingOut && + if (client.IsActive && (Environment.TickCount & Int32.MaxValue) - udpClient.TickLastPacketReceived > timeoutTicks) { - m_log.WarnFormat( - "[LLUDPSERVER]: Ack timeout for {0} {1}, disconnecting", - client.Name, client.AgentId); + // We must set IsActive synchronously so that we can stop the packet loop reinvoking this method, even + // though it's set later on by LLClientView.Close() + client.IsActive = false; - StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); - LogoutClientDueToTimeout(client); + // Fire this out on a different thread so that we don't hold up outgoing packet processing for + // everybody else if this is being called due to an ack timeout. + // This is the same as processing as the async process of a logout request. + Util.FireAndForget(o => DeactivateClientDueToTimeout(client)); return; } @@ -792,7 +794,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP Interlocked.Increment(ref udpClient.PacketsReceived); int now = Environment.TickCount & Int32.MaxValue; - udpClient.TickLastPacketReceived = now; +// udpClient.TickLastPacketReceived = now; #region ACK Receiving @@ -1113,30 +1115,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP return client; } - private void RemoveClient(IClientAPI client) + /// + /// Deactivates the client if we don't receive any packets within a certain amount of time (default 60 seconds). + /// + /// + /// If a connection is active then we will always receive packets even if nothing else is happening, due to + /// regular client pings. + /// + /// + private void DeactivateClientDueToTimeout(IClientAPI client) { - // We must set IsLoggingOut synchronously so that we can stop the packet loop reinvoking this method. - client.IsLoggingOut = true; + // We must set IsActive synchronously so that we can stop the packet loop reinvoking this method, even + // though it's set later on by LLClientView.Close() + client.IsActive = false; - // Fire this out on a different thread so that we don't hold up outgoing packet processing for - // everybody else if this is being called due to an ack timeout. - // This is the same as processing as the async process of a logout request. - Util.FireAndForget(o => client.Close()); - } + m_log.WarnFormat( + "[LLUDPSERVER]: Ack timeout, disconnecting {0} agent for {1} in {2}", + client.SceneAgent.IsChildAgent ? "child" : "root", client.Name, m_scene.RegionInfo.RegionName); - private void LogoutClientDueToTimeout(IClientAPI client) - { - // We must set IsLoggingOut synchronously so that we can stop the packet loop reinvoking this method. - client.IsLoggingOut = true; + StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); - // Fire this out on a different thread so that we don't hold up outgoing packet processing for - // everybody else if this is being called due to an ack timeout. - // This is the same as processing as the async process of a logout request. - Util.FireAndForget( - o => - { if (!client.SceneAgent.IsChildAgent) - client.Kick("Simulator logged you out due to connection timeout"); - client.Close(); }); + if (!client.SceneAgent.IsChildAgent) + client.Kick("Simulator logged you out due to connection timeout"); + + client.Close(); } private void IncomingPacketHandler() @@ -1450,8 +1452,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected void LogoutHandler(IClientAPI client) { client.SendLogoutPacket(); + if (!client.IsLoggingOut) - RemoveClient(client); + { + client.IsLoggingOut = true; + client.Close(); + } } } } \ No newline at end of file From 1b1f0a2d77fb4d2ac572f16c71a7560860c8c040 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 12 Jun 2012 02:43:33 +0100 Subject: [PATCH 4/5] OnConnectionClosed listeners, retrieve data from IClientAPI.SceneAgent rather than scanning all scene for the presence with the right id Stop checking IsLoggingOut on these listeners, if called with a root agent then we always want to perform these actions. This covers cases where the client is closed due to manual kick, simulator shutdown, etc. --- OpenSim/Framework/ISceneAgent.cs | 7 +++ .../EntityTransfer/HGEntityTransferModule.cs | 46 +++++++++---------- .../GridUser/ActivityDetector.cs | 28 +++-------- .../Presence/PresenceDetector.cs | 2 +- .../Framework/Scenes/AsyncInventorySender.cs | 2 +- .../SimianGrid/SimianActivityDetector.cs | 28 +++-------- 6 files changed, 43 insertions(+), 70 deletions(-) diff --git a/OpenSim/Framework/ISceneAgent.cs b/OpenSim/Framework/ISceneAgent.cs index 824172d58a..563d906b7f 100644 --- a/OpenSim/Framework/ISceneAgent.cs +++ b/OpenSim/Framework/ISceneAgent.cs @@ -26,6 +26,7 @@ */ using System; +using OpenMetaverse; namespace OpenSim.Framework { @@ -71,5 +72,11 @@ namespace OpenSim.Framework /// This includes scene object data and the appearance data of other avatars. /// void SendInitialDataToMe(); + + /// + /// Direction in which the scene presence is looking. + /// + /// Will be Vector3.Zero for a child agent. + Vector3 Lookat { get; } } } \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs index 43a72e279d..08863c2d05 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs @@ -325,34 +325,30 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer void OnConnectionClosed(IClientAPI obj) { - if (obj.IsLoggingOut) + if (obj.SceneAgent.IsChildAgent) + return; + + // Let's find out if this is a foreign user or a local user + IUserManagement uMan = Scene.RequestModuleInterface(); +// UserAccount account = Scene.UserAccountService.GetUserAccount(Scene.RegionInfo.ScopeID, obj.AgentId); + + if (uMan != null && uMan.IsLocalGridUser(obj.AgentId)) { - object sp = null; - if (obj.Scene.TryGetScenePresence(obj.AgentId, out sp)) - { - if (((ScenePresence)sp).IsChildAgent) - return; - } + // local grid user + return; + } - // Let's find out if this is a foreign user or a local user - IUserManagement uMan = Scene.RequestModuleInterface(); -// UserAccount account = Scene.UserAccountService.GetUserAccount(Scene.RegionInfo.ScopeID, obj.AgentId); - if (uMan != null && uMan.IsLocalGridUser(obj.AgentId)) - { - // local grid user - return; - } + AgentCircuitData aCircuit = ((Scene)(obj.Scene)).AuthenticateHandler.GetAgentCircuitData(obj.CircuitCode); - AgentCircuitData aCircuit = ((Scene)(obj.Scene)).AuthenticateHandler.GetAgentCircuitData(obj.CircuitCode); - - if (aCircuit.ServiceURLs.ContainsKey("HomeURI")) - { - string url = aCircuit.ServiceURLs["HomeURI"].ToString(); - IUserAgentService security = new UserAgentServiceConnector(url); - security.LogoutAgent(obj.AgentId, obj.SessionId); - //m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Sent logout call to UserAgentService @ {0}", url); - } - else + if (aCircuit.ServiceURLs.ContainsKey("HomeURI")) + { + string url = aCircuit.ServiceURLs["HomeURI"].ToString(); + IUserAgentService security = new UserAgentServiceConnector(url); + security.LogoutAgent(obj.AgentId, obj.SessionId); + //m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Sent logout call to UserAgentService @ {0}", url); + } + else + { m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: HomeURI not found for agent {0} logout", obj.AgentId); } } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs index 4cf62eca54..b0edce7a91 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/GridUser/ActivityDetector.cs @@ -79,29 +79,13 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.GridUser public void OnConnectionClose(IClientAPI client) { - if (client.IsLoggingOut) - { - object sp = null; - Vector3 position = new Vector3(128, 128, 0); - Vector3 lookat = new Vector3(0, 1, 0); - - if (client.Scene.TryGetScenePresence(client.AgentId, out sp)) - { - if (sp is ScenePresence) - { - if (((ScenePresence)sp).IsChildAgent) - return; - - position = ((ScenePresence)sp).AbsolutePosition; - lookat = ((ScenePresence)sp).Lookat; - } - } - -// m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName); - m_GridUserService.LoggedOut(client.AgentId.ToString(), client.SessionId, client.Scene.RegionInfo.RegionID, position, lookat); - } + if (client.SceneAgent.IsChildAgent) + return; +// m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName); + m_GridUserService.LoggedOut( + client.AgentId.ToString(), client.SessionId, client.Scene.RegionInfo.RegionID, + client.SceneAgent.AbsolutePosition, client.SceneAgent.Lookat); } - } } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/PresenceDetector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/PresenceDetector.cs index b43745c846..172bea10e3 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/PresenceDetector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/PresenceDetector.cs @@ -79,7 +79,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence public void OnConnectionClose(IClientAPI client) { - if (client.IsLoggingOut && !client.SceneAgent.IsChildAgent) + if (!client.SceneAgent.IsChildAgent) { // m_log.DebugFormat("[PRESENCE DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName); m_PresenceService.LogoutAgent(client.SessionId); diff --git a/OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs b/OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs index 9cb567430b..d9d2e64d87 100644 --- a/OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs +++ b/OpenSim/Region/Framework/Scenes/AsyncInventorySender.cs @@ -137,7 +137,7 @@ namespace OpenSim.Region.Framework.Scenes } } - if (fh.Client.IsLoggingOut) + if (!fh.Client.IsActive) continue; // m_log.DebugFormat( diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs index 2267325bdc..95e4bab134 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs @@ -79,27 +79,13 @@ namespace OpenSim.Services.Connectors.SimianGrid public void OnConnectionClose(IClientAPI client) { - if (client.IsLoggingOut) - { - object sp = null; - Vector3 position = new Vector3(128, 128, 0); - Vector3 lookat = new Vector3(0, 1, 0); + if (client.SceneAgent.IsChildAgent) + return; - if (client.Scene.TryGetScenePresence(client.AgentId, out sp)) - { - if (sp is ScenePresence) - { - if (((ScenePresence)sp).IsChildAgent) - return; - - position = ((ScenePresence)sp).AbsolutePosition; - lookat = ((ScenePresence)sp).Lookat; - } - } - -// m_log.DebugFormat("[SIMIAN ACTIVITY DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName); - m_GridUserService.LoggedOut(client.AgentId.ToString(), client.SessionId, client.Scene.RegionInfo.RegionID, position, lookat); - } +// m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName); + m_GridUserService.LoggedOut( + client.AgentId.ToString(), client.SessionId, client.Scene.RegionInfo.RegionID, + client.SceneAgent.AbsolutePosition, client.SceneAgent.Lookat); } void OnEnteringNewParcel(ScenePresence sp, int localLandID, UUID regionID) @@ -111,4 +97,4 @@ namespace OpenSim.Services.Connectors.SimianGrid }); } } -} +} \ No newline at end of file From 2ca31a9841991f748e972c96a3eba96e611f1dea Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 12 Jun 2012 02:46:14 +0100 Subject: [PATCH 5/5] Remove accidental timeout left in during earlier debugging. Has been in since two commits ago (b099f26) --- OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 37d29439a6..5126d845ad 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -794,7 +794,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP Interlocked.Increment(ref udpClient.PacketsReceived); int now = Environment.TickCount & Int32.MaxValue; -// udpClient.TickLastPacketReceived = now; + udpClient.TickLastPacketReceived = now; #region ACK Receiving