diff options
author | Justin Clark-Casey (justincc) | 2014-01-16 20:23:31 +0000 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2014-01-17 00:11:48 +0000 |
commit | bf86addf3d55aba81985c0f68aea9ee7063da403 (patch) | |
tree | 8e68ba029f3a2a6570cef935821d98c3642a5477 | |
parent | Merge branch '0.7.6-post-fixes' of ssh://opensimulator.org/var/git/opensim in... (diff) | |
download | opensim-SC_OLD-bf86addf3d55aba81985c0f68aea9ee7063da403.zip opensim-SC_OLD-bf86addf3d55aba81985c0f68aea9ee7063da403.tar.gz opensim-SC_OLD-bf86addf3d55aba81985c0f68aea9ee7063da403.tar.bz2 opensim-SC_OLD-bf86addf3d55aba81985c0f68aea9ee7063da403.tar.xz |
Prevent duplicate invocations or race dontision in SP.CompleteMovement()
This can happen under poor network conditions if a viewer repeats the message send
If this happens, physics actors can get orphaned, which unecessarily raises physics frame times
-rw-r--r-- | OpenSim/Region/Framework/Scenes/ScenePresence.cs | 29 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs | 92 |
2 files changed, 65 insertions, 56 deletions
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 7243db1..d7511d3 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs | |||
@@ -108,6 +108,16 @@ namespace OpenSim.Region.Framework.Scenes | |||
108 | } | 108 | } |
109 | } | 109 | } |
110 | 110 | ||
111 | /// <summary> | ||
112 | /// This exists to prevent race conditions between two CompleteMovement threads if the simulator is slow and | ||
113 | /// the viewer fires these in quick succession. | ||
114 | /// </summary> | ||
115 | /// <remarks> | ||
116 | /// TODO: The child -> agent transition should be folded into LifecycleState and the CompleteMovement | ||
117 | /// regulation done there. | ||
118 | /// </remarks> | ||
119 | private object m_completeMovementLock = new object(); | ||
120 | |||
111 | // private static readonly byte[] DEFAULT_TEXTURE = AvatarAppearance.GetDefaultTexture().GetBytes(); | 121 | // private static readonly byte[] DEFAULT_TEXTURE = AvatarAppearance.GetDefaultTexture().GetBytes(); |
112 | private static readonly Array DIR_CONTROL_FLAGS = Enum.GetValues(typeof(Dir_ControlFlags)); | 122 | private static readonly Array DIR_CONTROL_FLAGS = Enum.GetValues(typeof(Dir_ControlFlags)); |
113 | private static readonly Vector3 HEAD_ADJUSTMENT = new Vector3(0f, 0f, 0.3f); | 123 | private static readonly Vector3 HEAD_ADJUSTMENT = new Vector3(0f, 0f, 0.3f); |
@@ -904,6 +914,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
904 | /// <summary> | 914 | /// <summary> |
905 | /// Turns a child agent into a root agent. | 915 | /// Turns a child agent into a root agent. |
906 | /// </summary> | 916 | /// </summary> |
917 | /// <remarks> | ||
907 | /// Child agents are logged into neighbouring sims largely to observe changes. Root agents exist when the | 918 | /// Child agents are logged into neighbouring sims largely to observe changes. Root agents exist when the |
908 | /// avatar is actual in the sim. They can perform all actions. | 919 | /// avatar is actual in the sim. They can perform all actions. |
909 | /// This change is made whenever an avatar enters a region, whether by crossing over from a neighbouring sim, | 920 | /// This change is made whenever an avatar enters a region, whether by crossing over from a neighbouring sim, |
@@ -911,8 +922,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
911 | /// | 922 | /// |
912 | /// This method is on the critical path for transferring an avatar from one region to another. Delay here | 923 | /// This method is on the critical path for transferring an avatar from one region to another. Delay here |
913 | /// delays that crossing. | 924 | /// delays that crossing. |
914 | /// </summary> | 925 | /// </remarks> |
915 | private void MakeRootAgent(Vector3 pos, bool isFlying) | 926 | private bool MakeRootAgent(Vector3 pos, bool isFlying) |
916 | { | 927 | { |
917 | // m_log.InfoFormat( | 928 | // m_log.InfoFormat( |
918 | // "[SCENE]: Upgrading child to root agent for {0} in {1}", | 929 | // "[SCENE]: Upgrading child to root agent for {0} in {1}", |
@@ -920,6 +931,10 @@ namespace OpenSim.Region.Framework.Scenes | |||
920 | 931 | ||
921 | //m_log.DebugFormat("[SCENE]: known regions in {0}: {1}", Scene.RegionInfo.RegionName, KnownChildRegionHandles.Count); | 932 | //m_log.DebugFormat("[SCENE]: known regions in {0}: {1}", Scene.RegionInfo.RegionName, KnownChildRegionHandles.Count); |
922 | 933 | ||
934 | lock (m_completeMovementLock) | ||
935 | if (!IsChildAgent) | ||
936 | return false; | ||
937 | |||
923 | IsChildAgent = false; | 938 | IsChildAgent = false; |
924 | 939 | ||
925 | // Must reset this here so that a teleport to a region next to an existing region does not keep the flag | 940 | // Must reset this here so that a teleport to a region next to an existing region does not keep the flag |
@@ -1069,6 +1084,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
1069 | 1084 | ||
1070 | m_scene.EventManager.TriggerOnMakeRootAgent(this); | 1085 | m_scene.EventManager.TriggerOnMakeRootAgent(this); |
1071 | 1086 | ||
1087 | return true; | ||
1072 | } | 1088 | } |
1073 | 1089 | ||
1074 | public int GetStateSource() | 1090 | public int GetStateSource() |
@@ -1442,7 +1458,14 @@ namespace OpenSim.Region.Framework.Scenes | |||
1442 | } | 1458 | } |
1443 | 1459 | ||
1444 | bool flying = ((m_AgentControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0); | 1460 | bool flying = ((m_AgentControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) != 0); |
1445 | MakeRootAgent(AbsolutePosition, flying); | 1461 | if (!MakeRootAgent(AbsolutePosition, flying)) |
1462 | { | ||
1463 | m_log.DebugFormat( | ||
1464 | "[SCENE PRESENCE]: Aborting CompleteMovement call for {0} in {1} as they are already root", | ||
1465 | Name, Scene.Name); | ||
1466 | |||
1467 | return; | ||
1468 | } | ||
1446 | 1469 | ||
1447 | // Tell the client that we're totally ready | 1470 | // Tell the client that we're totally ready |
1448 | ControllingClient.MoveAgentIntoRegion(m_scene.RegionInfo, AbsolutePosition, look); | 1471 | ControllingClient.MoveAgentIntoRegion(m_scene.RegionInfo, AbsolutePosition, look); |
diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs index d1aeaee..1ff1329 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs | |||
@@ -111,6 +111,45 @@ namespace OpenSim.Region.Framework.Scenes.Tests | |||
111 | Assert.That(scene.GetScenePresences().Count, Is.EqualTo(1)); | 111 | Assert.That(scene.GetScenePresences().Count, Is.EqualTo(1)); |
112 | } | 112 | } |
113 | 113 | ||
114 | /// <summary> | ||
115 | /// Test that duplicate complete movement calls are ignored. | ||
116 | /// </summary> | ||
117 | /// <remarks> | ||
118 | /// If duplicate calls are not ignored then there is a risk of race conditions or other unexpected effects. | ||
119 | /// </remarks> | ||
120 | [Test] | ||
121 | public void TestDupeCompleteMovementCalls() | ||
122 | { | ||
123 | TestHelpers.InMethod(); | ||
124 | // TestHelpers.EnableLogging(); | ||
125 | |||
126 | UUID spUuid = TestHelpers.ParseTail(0x1); | ||
127 | |||
128 | TestScene scene = new SceneHelpers().SetupScene(); | ||
129 | |||
130 | int makeRootAgentEvents = 0; | ||
131 | scene.EventManager.OnMakeRootAgent += spi => makeRootAgentEvents++; | ||
132 | |||
133 | ScenePresence sp = SceneHelpers.AddScenePresence(scene, spUuid); | ||
134 | |||
135 | Assert.That(makeRootAgentEvents, Is.EqualTo(1)); | ||
136 | |||
137 | // Normally these would be invoked by a CompleteMovement message coming in to the UDP stack. But for | ||
138 | // convenience, here we will invoke it manually. | ||
139 | sp.CompleteMovement(sp.ControllingClient, true); | ||
140 | |||
141 | Assert.That(makeRootAgentEvents, Is.EqualTo(1)); | ||
142 | |||
143 | // Check rest of exepcted parameters. | ||
144 | Assert.That(scene.AuthenticateHandler.GetAgentCircuitData(spUuid), Is.Not.Null); | ||
145 | Assert.That(scene.AuthenticateHandler.GetAgentCircuits().Count, Is.EqualTo(1)); | ||
146 | |||
147 | Assert.That(sp.IsChildAgent, Is.False); | ||
148 | Assert.That(sp.UUID, Is.EqualTo(spUuid)); | ||
149 | |||
150 | Assert.That(scene.GetScenePresences().Count, Is.EqualTo(1)); | ||
151 | } | ||
152 | |||
114 | [Test] | 153 | [Test] |
115 | public void TestCreateDuplicateRootScenePresence() | 154 | public void TestCreateDuplicateRootScenePresence() |
116 | { | 155 | { |
@@ -249,58 +288,5 @@ namespace OpenSim.Region.Framework.Scenes.Tests | |||
249 | // Assert.That(childPresence, Is.Not.Null); | 288 | // Assert.That(childPresence, Is.Not.Null); |
250 | // Assert.That(childPresence.IsChildAgent, Is.True); | 289 | // Assert.That(childPresence.IsChildAgent, Is.True); |
251 | } | 290 | } |
252 | |||
253 | // /// <summary> | ||
254 | // /// Test adding a root agent to a scene. Doesn't yet actually complete crossing the agent into the scene. | ||
255 | // /// </summary> | ||
256 | // [Test] | ||
257 | // public void T010_TestAddRootAgent() | ||
258 | // { | ||
259 | // TestHelpers.InMethod(); | ||
260 | // | ||
261 | // string firstName = "testfirstname"; | ||
262 | // | ||
263 | // AgentCircuitData agent = new AgentCircuitData(); | ||
264 | // agent.AgentID = agent1; | ||
265 | // agent.firstname = firstName; | ||
266 | // agent.lastname = "testlastname"; | ||
267 | // agent.SessionID = UUID.Random(); | ||
268 | // agent.SecureSessionID = UUID.Random(); | ||
269 | // agent.circuitcode = 123; | ||
270 | // agent.BaseFolder = UUID.Zero; | ||
271 | // agent.InventoryFolder = UUID.Zero; | ||
272 | // agent.startpos = Vector3.Zero; | ||
273 | // agent.CapsPath = GetRandomCapsObjectPath(); | ||
274 | // agent.ChildrenCapSeeds = new Dictionary<ulong, string>(); | ||
275 | // agent.child = true; | ||
276 | // | ||
277 | // scene.PresenceService.LoginAgent(agent.AgentID.ToString(), agent.SessionID, agent.SecureSessionID); | ||
278 | // | ||
279 | // string reason; | ||
280 | // scene.NewUserConnection(agent, (uint)TeleportFlags.ViaLogin, out reason); | ||
281 | // testclient = new TestClient(agent, scene); | ||
282 | // scene.AddNewAgent(testclient); | ||
283 | // | ||
284 | // ScenePresence presence = scene.GetScenePresence(agent1); | ||
285 | // | ||
286 | // Assert.That(presence, Is.Not.Null, "presence is null"); | ||
287 | // Assert.That(presence.Firstname, Is.EqualTo(firstName), "First name not same"); | ||
288 | // acd1 = agent; | ||
289 | // } | ||
290 | // | ||
291 | // /// <summary> | ||
292 | // /// Test removing an uncrossed root agent from a scene. | ||
293 | // /// </summary> | ||
294 | // [Test] | ||
295 | // public void T011_TestRemoveRootAgent() | ||
296 | // { | ||
297 | // TestHelpers.InMethod(); | ||
298 | // | ||
299 | // scene.RemoveClient(agent1); | ||
300 | // | ||
301 | // ScenePresence presence = scene.GetScenePresence(agent1); | ||
302 | // | ||
303 | // Assert.That(presence, Is.Null, "presence is not null"); | ||
304 | // } | ||
305 | } | 291 | } |
306 | } \ No newline at end of file | 292 | } \ No newline at end of file |