aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs
diff options
context:
space:
mode:
authorJustin Clark-Casey (justincc)2012-05-28 23:06:00 +0100
committerJustin Clark-Casey (justincc)2012-05-28 23:06:00 +0100
commit79f3ce2e9f3563165ee640f917f944ec4b370ac7 (patch)
treebab16f99077a5145ca87d16e753d973b14527361 /OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs
parentIf handling the failure of teleport, move agent state to CleaningUp when we s... (diff)
downloadopensim-SC_OLD-79f3ce2e9f3563165ee640f917f944ec4b370ac7.zip
opensim-SC_OLD-79f3ce2e9f3563165ee640f917f944ec4b370ac7.tar.gz
opensim-SC_OLD-79f3ce2e9f3563165ee640f917f944ec4b370ac7.tar.bz2
opensim-SC_OLD-79f3ce2e9f3563165ee640f917f944ec4b370ac7.tar.xz
refactor: factor out entity transfer state machine into a separate class to make code more analyzable
Diffstat (limited to 'OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs')
-rw-r--r--OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs269
1 files changed, 269 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs
new file mode 100644
index 0000000..d0cab49
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferStateMachine.cs
@@ -0,0 +1,269 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Net;
31using System.Reflection;
32using System.Threading;
33using OpenMetaverse;
34using log4net;
35using Nini.Config;
36using OpenSim.Framework;
37using OpenSim.Framework.Capabilities;
38using OpenSim.Framework.Client;
39using OpenSim.Region.Framework.Interfaces;
40using OpenSim.Region.Framework.Scenes;
41using OpenSim.Region.Physics.Manager;
42using OpenSim.Services.Interfaces;
43using GridRegion = OpenSim.Services.Interfaces.GridRegion;
44
45namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
46{
47 /// <summary>
48 /// The possible states that an agent can be in when its being transferred between regions.
49 /// </summary>
50 /// <remarks>
51 /// This is a state machine.
52 ///
53 /// [Entry] => Preparing
54 /// Preparing => { Transferring || CleaningUp || [Exit] }
55 /// Transferring => { ReceivedAtDestination || CleaningUp }
56 /// ReceivedAtDestination => CleaningUp
57 /// CleaningUp => [Exit]
58 ///
59 /// In other words, agents normally travel throwing Preparing => Transferring => ReceivedAtDestination => CleaningUp
60 /// However, any state can transition to CleaningUp if the teleport has failed.
61 /// </remarks>
62 enum AgentTransferState
63 {
64 Preparing, // The agent is being prepared for transfer
65 Transferring, // The agent is in the process of being transferred to a destination
66 ReceivedAtDestination, // The destination has notified us that the agent has been successfully received
67 CleaningUp // The agent is being changed to child/removed after a transfer
68 }
69
70 /// <summary>
71 /// Records the state of entities when they are in transfer within or between regions (cross or teleport).
72 /// </summary>
73 public class EntityTransferStateMachine
74 {
75 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
76
77 /// <summary>
78 /// If true then on a teleport, the source region waits for a callback from the destination region. If
79 /// a callback fails to arrive within a set time then the user is pulled back into the source region.
80 /// </summary>
81 public bool EnableWaitForAgentArrivedAtDestination { get; set; }
82
83 private EntityTransferModule m_mod;
84
85 private Dictionary<UUID, AgentTransferState> m_agentsInTransit = new Dictionary<UUID, AgentTransferState>();
86
87 public EntityTransferStateMachine(EntityTransferModule module)
88 {
89 m_mod = module;
90 }
91
92 /// <summary>
93 /// Set that an agent is in transit.
94 /// </summary>
95 /// <param name='id'>The ID of the agent being teleported</param>
96 /// <returns>true if the agent was not already in transit, false if it was</returns>
97 internal bool SetInTransit(UUID id)
98 {
99 lock (m_agentsInTransit)
100 {
101 if (!m_agentsInTransit.ContainsKey(id))
102 {
103 m_agentsInTransit[id] = AgentTransferState.Preparing;
104 return true;
105 }
106 }
107
108 return false;
109 }
110
111 /// <summary>
112 /// Updates the state of an agent that is already in transit.
113 /// </summary>
114 /// <param name='id'></param>
115 /// <param name='newState'></param>
116 /// <returns></returns>
117 /// <exception cref='Exception'>Illegal transitions will throw an Exception</exception>
118 internal void UpdateInTransit(UUID id, AgentTransferState newState)
119 {
120 lock (m_agentsInTransit)
121 {
122 // Illegal to try and update an agent that's not actually in transit.
123 if (!m_agentsInTransit.ContainsKey(id))
124 throw new Exception(
125 string.Format(
126 "Agent with ID {0} is not registered as in transit in {1}",
127 id, m_mod.Scene.RegionInfo.RegionName));
128
129 AgentTransferState oldState = m_agentsInTransit[id];
130
131 bool transitionOkay = false;
132
133 if (newState == AgentTransferState.CleaningUp && oldState != AgentTransferState.CleaningUp)
134 transitionOkay = true;
135 else if (newState == AgentTransferState.Transferring && oldState == AgentTransferState.Preparing)
136 transitionOkay = true;
137 else if (newState == AgentTransferState.ReceivedAtDestination && oldState == AgentTransferState.Transferring)
138 transitionOkay = true;
139
140 if (transitionOkay)
141 m_agentsInTransit[id] = newState;
142 else
143 throw new Exception(
144 string.Format(
145 "Agent with ID {0} is not allowed to move from old transit state {1} to new state {2} in {3}",
146 id, oldState, newState, m_mod.Scene.RegionInfo.RegionName));
147 }
148 }
149
150 internal bool IsInTransit(UUID id)
151 {
152 lock (m_agentsInTransit)
153 return m_agentsInTransit.ContainsKey(id);
154 }
155
156 /// <summary>
157 /// Removes an agent from the transit state machine.
158 /// </summary>
159 /// <param name='id'></param>
160 /// <returns>true if the agent was flagged as being teleported when this method was called, false otherwise</returns>
161 internal bool ResetFromTransit(UUID id)
162 {
163 lock (m_agentsInTransit)
164 {
165 if (m_agentsInTransit.ContainsKey(id))
166 {
167 AgentTransferState state = m_agentsInTransit[id];
168
169 if (state == AgentTransferState.Transferring || state == AgentTransferState.ReceivedAtDestination)
170 {
171 // FIXME: For now, we allow exit from any state since a thrown exception in teleport is now guranteed
172 // to be handled properly - ResetFromTransit() could be invoked at any step along the process
173 m_log.WarnFormat(
174 "[ENTITY TRANSFER STATE MACHINE]: Agent with ID {0} should not exit directly from state {1}, should go to {2} state first in {3}",
175 id, state, AgentTransferState.CleaningUp, m_mod.Scene.RegionInfo.RegionName);
176
177// throw new Exception(
178// "Agent with ID {0} cannot exit directly from state {1}, it must go to {2} state first",
179// state, AgentTransferState.CleaningUp);
180 }
181
182 m_agentsInTransit.Remove(id);
183
184 m_log.DebugFormat(
185 "[ENTITY TRANSFER STATE MACHINE]: Agent {0} cleared from transit in {1}",
186 id, m_mod.Scene.RegionInfo.RegionName);
187
188 return true;
189 }
190 }
191
192 m_log.WarnFormat(
193 "[ENTITY TRANSFER STATE MACHINE]: Agent {0} requested to clear from transit in {1} but was already cleared",
194 id, m_mod.Scene.RegionInfo.RegionName);
195
196 return false;
197 }
198
199 internal bool WaitForAgentArrivedAtDestination(UUID id)
200 {
201 if (!m_mod.WaitForAgentArrivedAtDestination)
202 return true;
203
204 lock (m_agentsInTransit)
205 {
206 if (!IsInTransit(id))
207 throw new Exception(
208 string.Format(
209 "Asked to wait for destination callback for agent with ID {0} in {1} but agent is not in transit",
210 id, m_mod.Scene.RegionInfo.RegionName));
211
212 AgentTransferState currentState = m_agentsInTransit[id];
213
214 if (currentState != AgentTransferState.Transferring && currentState != AgentTransferState.ReceivedAtDestination)
215 throw new Exception(
216 string.Format(
217 "Asked to wait for destination callback for agent with ID {0} in {1} but agent is in state {2}",
218 id, m_mod.Scene.RegionInfo.RegionName, currentState));
219 }
220
221 int count = 200;
222
223 // There should be no race condition here since no other code should be removing the agent transfer or
224 // changing the state to another other than Transferring => ReceivedAtDestination.
225 while (m_agentsInTransit[id] != AgentTransferState.ReceivedAtDestination && count-- > 0)
226 {
227// m_log.Debug(" >>> Waiting... " + count);
228 Thread.Sleep(100);
229 }
230
231 return count > 0;
232 }
233
234 internal void SetAgentArrivedAtDestination(UUID id)
235 {
236 lock (m_agentsInTransit)
237 {
238 if (!m_agentsInTransit.ContainsKey(id))
239 {
240 m_log.WarnFormat(
241 "[ENTITY TRANSFER STATE MACHINE]: Region {0} received notification of arrival in destination of agent {1} but no teleport request is active",
242 m_mod.Scene.RegionInfo.RegionName, id);
243
244 return;
245 }
246
247 AgentTransferState currentState = m_agentsInTransit[id];
248
249 if (currentState == AgentTransferState.ReceivedAtDestination)
250 {
251 // An anomoly but don't make this an outright failure - destination region could be overzealous in sending notification.
252 m_log.WarnFormat(
253 "[ENTITY TRANSFER STATE MACHINE]: Region {0} received notification of arrival in destination of agent {1} but notification has already previously been received",
254 m_mod.Scene.RegionInfo.RegionName, id);
255 }
256 else if (currentState != AgentTransferState.Transferring)
257 {
258 m_log.ErrorFormat(
259 "[ENTITY TRANSFER STATE MACHINE]: Region {0} received notification of arrival in destination of agent {1} but agent is in state {2}",
260 m_mod.Scene.RegionInfo.RegionName, id, currentState);
261
262 return;
263 }
264
265 m_agentsInTransit[id] = AgentTransferState.ReceivedAtDestination;
266 }
267 }
268 }
269} \ No newline at end of file