Prevent race conditions between two threads that call LLClientView.Close() simultaneously (e.g. ack timeout and an attempt to reconnect)
parent
4deb25da87
commit
47e2922a40
|
@ -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;
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue