Prevent race conditions between two threads that call LLClientView.Close() simultaneously (e.g. ack timeout and an attempt to reconnect)

0.7.3-extended
Justin Clark-Casey (justincc) 2012-07-19 22:32:27 +01:00
parent 4deb25da87
commit 47e2922a40
3 changed files with 60 additions and 33 deletions

View File

@ -348,8 +348,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private int m_animationSequenceNumber = 1; private int m_animationSequenceNumber = 1;
private bool m_SendLogoutPacketWhenClosing = true; private bool m_SendLogoutPacketWhenClosing = true;
private AgentUpdateArgs lastarg; private AgentUpdateArgs lastarg;
private bool m_IsActive = true;
private bool m_IsLoggingOut = false;
protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>(); protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>();
protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers
@ -413,16 +411,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public uint CircuitCode { get { return m_circuitCode; } } public uint CircuitCode { get { return m_circuitCode; } }
public int MoneyBalance { get { return m_moneyBalance; } } public int MoneyBalance { get { return m_moneyBalance; } }
public int NextAnimationSequenceNumber { get { return m_animationSequenceNumber++; } } public int NextAnimationSequenceNumber { get { return m_animationSequenceNumber++; } }
public bool IsActive
{ /// <summary>
get { return m_IsActive; } /// As well as it's function in IClientAPI, in LLClientView we are locking on this property in order to
set { m_IsActive = value; } /// prevent race conditions by different threads calling Close().
} /// </summary>
public bool IsLoggingOut public bool IsActive { get; set; }
{
get { return m_IsLoggingOut; } /// <summary>
set { m_IsLoggingOut = value; } /// Used to synchronise threads when client is being closed.
} /// </summary>
public Object CloseSyncLock { get; private set; }
public bool IsLoggingOut { get; set; }
public bool DisableFacelights public bool DisableFacelights
{ {
@ -447,6 +448,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
// DebugPacketLevel = 1; // DebugPacketLevel = 1;
CloseSyncLock = new Object();
RegisterInterface<IClientIM>(this); RegisterInterface<IClientIM>(this);
RegisterInterface<IClientInventory>(this); RegisterInterface<IClientInventory>(this);
RegisterInterface<IClientChat>(this); RegisterInterface<IClientChat>(this);
@ -482,17 +485,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_prioritizer = new Prioritizer(m_scene); m_prioritizer = new Prioritizer(m_scene);
RegisterLocalPacketHandlers(); RegisterLocalPacketHandlers();
IsActive = true;
} }
#region Client Methods #region Client Methods
/// <summary> /// <summary>
/// Shut down the client view /// Close down the client view
/// </summary> /// </summary>
public void Close() public void Close()
{ {
IsActive = false; // We lock here to prevent race conditions between two threads calling close simultaneously (e.g.
// a simultaneous relog just as a client is being closed out due to no packet ack from the old connection.
lock (CloseSyncLock)
{
if (!IsActive)
return;
IsActive = false;
CloseWithoutChecks();
}
}
/// <summary>
/// Closes down the client view without first checking whether it is active.
/// </summary>
/// <remarks>
/// This exists because LLUDPServer has to set IsActive = false in earlier synchronous code before calling
/// CloseWithoutIsActiveCheck asynchronously.
///
/// Callers must lock ClosingSyncLock before calling.
/// </remarks>
public void CloseWithoutChecks()
{
m_log.DebugFormat( m_log.DebugFormat(
"[CLIENT]: Close has been called for {0} attached to scene {1}", "[CLIENT]: Close has been called for {0} attached to scene {1}",
Name, m_scene.RegionInfo.RegionName); Name, m_scene.RegionInfo.RegionName);
@ -3570,7 +3596,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public void SendCoarseLocationUpdate(List<UUID> users, List<Vector3> CoarseLocations) public void SendCoarseLocationUpdate(List<UUID> users, List<Vector3> CoarseLocations)
{ {
if (!IsActive) return; // We don't need to update inactive clients. // We don't need to update inactive clients.
if (!IsActive)
return;
CoarseLocationUpdatePacket loc = (CoarseLocationUpdatePacket)PacketPool.Instance.GetPacket(PacketType.CoarseLocationUpdate); CoarseLocationUpdatePacket loc = (CoarseLocationUpdatePacket)PacketPool.Instance.GetPacket(PacketType.CoarseLocationUpdate);
loc.Header.Reliable = false; loc.Header.Reliable = false;

View File

@ -1123,22 +1123,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
/// regular client pings. /// regular client pings.
/// </remarks> /// </remarks>
/// <param name='client'></param> /// <param name='client'></param>
private void DeactivateClientDueToTimeout(IClientAPI client) private void DeactivateClientDueToTimeout(LLClientView client)
{ {
// We must set IsActive synchronously so that we can stop the packet loop reinvoking this method, even lock (client.CloseSyncLock)
// though it's set later on by LLClientView.Close() {
client.IsActive = false; m_log.WarnFormat(
"[LLUDPSERVER]: Ack timeout, disconnecting {0} agent for {1} in {2}",
m_log.WarnFormat( client.SceneAgent.IsChildAgent ? "child" : "root", client.Name, m_scene.RegionInfo.RegionName);
"[LLUDPSERVER]: Ack timeout, disconnecting {0} agent for {1} in {2}",
client.SceneAgent.IsChildAgent ? "child" : "root", client.Name, m_scene.RegionInfo.RegionName); StatsManager.SimExtraStats.AddAbnormalClientThreadTermination();
StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); if (!client.SceneAgent.IsChildAgent)
client.Kick("Simulator logged you out due to connection timeout");
if (!client.SceneAgent.IsChildAgent)
client.Kick("Simulator logged you out due to connection timeout"); client.CloseWithoutChecks();
}
client.Close();
} }
private void IncomingPacketHandler() private void IncomingPacketHandler()

View File

@ -3420,8 +3420,8 @@ namespace OpenSim.Region.Framework.Scenes
// We have a zombie from a crashed session. // We have a zombie from a crashed session.
// Or the same user is trying to be root twice here, won't work. // Or the same user is trying to be root twice here, won't work.
// Kill it. // Kill it.
m_log.DebugFormat( m_log.WarnFormat(
"[SCENE]: Zombie scene presence detected for {0} {1} in {2}", "[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.",
sp.Name, sp.UUID, RegionInfo.RegionName); sp.Name, sp.UUID, RegionInfo.RegionName);
sp.ControllingClient.Close(); sp.ControllingClient.Close();
@ -4377,7 +4377,7 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// This method will return both root and child scene presences. /// This method will return both root and child scene presences.
/// ///
/// Consider using ForEachScenePresence() or ForEachRootScenePresence() if possible since these will not /// Consider using ForEachScenePresence() or ForEachRootScenePresence() if possible since these will not
/// involving creating a new List object. /// involving creating a new List object.
/// </remarks> /// </remarks>