* Switched to a plain lock for the ClientManager collections and protected the TryGetValues with try/catch instead of a lock

* Added ClientManager.ForEachSync() for operations that need to run synchronously, such as "show connections"
prioritization
John Hurliman 2009-10-14 14:25:58 -07:00
parent 25878d6828
commit 1e9e9df0b3
3 changed files with 85 additions and 71 deletions

View File

@ -29,7 +29,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Reflection; using System.Reflection;
using System.Net; using System.Net;
using BclExtras.Collections;
using OpenMetaverse; using OpenMetaverse;
using OpenMetaverse.Packets; using OpenMetaverse.Packets;
@ -41,56 +40,29 @@ namespace OpenSim.Framework
/// </summary> /// </summary>
public class ClientManager public class ClientManager
{ {
#region IComparers /// <summary>A dictionary mapping from <seealso cref="UUID"/>
private sealed class UUIDComparer : IComparer<UUID>
{
public int Compare(UUID x, UUID y)
{
return x.CompareTo(y);
}
}
private sealed class IPEndPointComparer : IComparer<IPEndPoint>
{
public int Compare(IPEndPoint x, IPEndPoint y)
{
if (x == null && y == null)
return 0;
else if (x == null)
return -1;
else if (y == null)
return 1;
int result = x.Address.Address.CompareTo(y.Address.Address);
if (result == 0) result = x.Port.CompareTo(y.Port);
return result;
}
}
#endregion IComparers
/// <summary>An immutable dictionary mapping from <seealso cref="UUID"/>
/// to <seealso cref="IClientAPI"/> references</summary> /// to <seealso cref="IClientAPI"/> references</summary>
private ImmutableMap<UUID, IClientAPI> m_dict; private Dictionary<UUID, IClientAPI> m_dict1;
/// <summary>An immutable dictionary mapping from <seealso cref="IPEndPoint"/> /// <summary>A dictionary mapping from <seealso cref="IPEndPoint"/>
/// to <seealso cref="IClientAPI"/> references</summary> /// to <seealso cref="IClientAPI"/> references</summary>
private ImmutableMap<IPEndPoint, IClientAPI> m_dict2; private Dictionary<IPEndPoint, IClientAPI> m_dict2;
/// <summary>Immutability grants thread safety for concurrent reads and /// <summary>An immutable collection of <seealso cref="IClientAPI"/>
/// read-writes, but not concurrent writes</summary> /// references</summary>
private object m_writeLock = new object(); private IClientAPI[] m_array;
/// <summary>Synchronization object for writing to the collections</summary>
private object m_syncRoot = new object();
/// <summary>Number of clients in the collection</summary> /// <summary>Number of clients in the collection</summary>
public int Count { get { return m_dict.Count; } } public int Count { get { return m_dict1.Count; } }
/// <summary> /// <summary>
/// Default constructor /// Default constructor
/// </summary> /// </summary>
public ClientManager() public ClientManager()
{ {
m_dict = new ImmutableMap<UUID, IClientAPI>(new UUIDComparer()); m_dict1 = new Dictionary<UUID, IClientAPI>();
m_dict2 = new ImmutableMap<IPEndPoint, IClientAPI>(new IPEndPointComparer()); m_dict2 = new Dictionary<IPEndPoint, IClientAPI>();
m_array = new IClientAPI[0];
} }
/// <summary> /// <summary>
@ -102,20 +74,26 @@ namespace OpenSim.Framework
/// otherwise false if the given key already existed in the collection</returns> /// otherwise false if the given key already existed in the collection</returns>
public bool Add(IClientAPI value) public bool Add(IClientAPI value)
{ {
lock (m_writeLock) lock (m_syncRoot)
{ {
if (!m_dict.ContainsKey(value.AgentId) && !m_dict2.ContainsKey(value.RemoteEndPoint)) if (m_dict1.ContainsKey(value.AgentId) || m_dict2.ContainsKey(value.RemoteEndPoint))
{
m_dict = m_dict.Add(value.AgentId, value);
m_dict2 = m_dict2.Add(value.RemoteEndPoint, value);
return true;
}
else
{
return false; return false;
}
m_dict1[value.AgentId] = value;
m_dict2[value.RemoteEndPoint] = value;
IClientAPI[] oldArray = m_array;
int oldLength = oldArray.Length;
IClientAPI[] newArray = new IClientAPI[oldLength + 1];
for (int i = 0; i < oldLength; i++)
newArray[i] = oldArray[i];
newArray[oldLength] = value;
m_array = newArray;
} }
return true;
} }
/// <summary> /// <summary>
@ -126,21 +104,31 @@ namespace OpenSim.Framework
/// was not present in the collection</returns> /// was not present in the collection</returns>
public bool Remove(UUID key) public bool Remove(UUID key)
{ {
lock (m_writeLock) lock (m_syncRoot)
{ {
IClientAPI client; IClientAPI value;
if (m_dict1.TryGetValue(key, out value))
if (m_dict.TryGetValue(key, out client))
{ {
m_dict = m_dict.Delete(key); m_dict1.Remove(key);
m_dict2 = m_dict2.Delete(client.RemoteEndPoint); m_dict2.Remove(value.RemoteEndPoint);
IClientAPI[] oldArray = m_array;
int oldLength = oldArray.Length;
IClientAPI[] newArray = new IClientAPI[oldLength - 1];
int j = 0;
for (int i = 0; i < oldLength; i++)
{
if (oldArray[i] != value)
newArray[j++] = oldArray[i];
}
m_array = newArray;
return true; return true;
} }
else
{
return false;
}
} }
return false;
} }
/// <summary> /// <summary>
@ -148,10 +136,11 @@ namespace OpenSim.Framework
/// </summary> /// </summary>
public void Clear() public void Clear()
{ {
lock (m_writeLock) lock (m_syncRoot)
{ {
m_dict = new ImmutableMap<UUID, IClientAPI>(new UUIDComparer()); m_dict1.Clear();
m_dict2 = new ImmutableMap<IPEndPoint, IClientAPI>(new IPEndPointComparer()); m_dict2.Clear();
m_array = new IClientAPI[0];
} }
} }
@ -162,7 +151,7 @@ namespace OpenSim.Framework
/// <returns>True if the UUID was found in the collection, otherwise false</returns> /// <returns>True if the UUID was found in the collection, otherwise false</returns>
public bool ContainsKey(UUID key) public bool ContainsKey(UUID key)
{ {
return m_dict.ContainsKey(key); return m_dict1.ContainsKey(key);
} }
/// <summary> /// <summary>
@ -183,7 +172,12 @@ namespace OpenSim.Framework
/// <returns>True if the lookup succeeded, otherwise false</returns> /// <returns>True if the lookup succeeded, otherwise false</returns>
public bool TryGetValue(UUID key, out IClientAPI value) public bool TryGetValue(UUID key, out IClientAPI value)
{ {
return m_dict.TryGetValue(key, out value); try { return m_dict1.TryGetValue(key, out value); }
catch (Exception)
{
value = null;
return false;
}
} }
/// <summary> /// <summary>
@ -194,7 +188,12 @@ namespace OpenSim.Framework
/// <returns>True if the lookup succeeded, otherwise false</returns> /// <returns>True if the lookup succeeded, otherwise false</returns>
public bool TryGetValue(IPEndPoint key, out IClientAPI value) public bool TryGetValue(IPEndPoint key, out IClientAPI value)
{ {
return m_dict2.TryGetValue(key, out value); try { return m_dict2.TryGetValue(key, out value); }
catch (Exception)
{
value = null;
return false;
}
} }
/// <summary> /// <summary>
@ -204,7 +203,20 @@ namespace OpenSim.Framework
/// <param name="action">Action to perform on each element</param> /// <param name="action">Action to perform on each element</param>
public void ForEach(Action<IClientAPI> action) public void ForEach(Action<IClientAPI> action)
{ {
Parallel.ForEach<IClientAPI>(m_dict.Values, action); IClientAPI[] localArray = m_array;
Parallel.ForEach<IClientAPI>(localArray, action);
}
/// <summary>
/// Performs a given task synchronously for each of the elements in
/// the collection
/// </summary>
/// <param name="action">Action to perform on each element</param>
public void ForEachSync(Action<IClientAPI> action)
{
IClientAPI[] localArray = m_array;
for (int i = 0; i < localArray.Length; i++)
action(localArray[i]);
} }
} }
} }

View File

@ -933,7 +933,7 @@ namespace OpenSim
m_sceneManager.ForEachScene( m_sceneManager.ForEachScene(
delegate(Scene scene) delegate(Scene scene)
{ {
scene.ClientManager.ForEach( scene.ClientManager.ForEachSync(
delegate(IClientAPI client) delegate(IClientAPI client)
{ {
connections.AppendFormat("{0}: {1} ({2}) from {3} on circuit {4}\n", connections.AppendFormat("{0}: {1} ({2}) from {3} on circuit {4}\n",

View File

@ -152,7 +152,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_throttleRates = new ThrottleRates(configSource); m_throttleRates = new ThrottleRates(configSource);
} }
public new void Start() public void Start()
{ {
if (m_scene == null) if (m_scene == null)
throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference");
@ -817,6 +817,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void LogoutHandler(IClientAPI client) private void LogoutHandler(IClientAPI client)
{ {
client.SendLogoutPacket(); client.SendLogoutPacket();
if (client.IsActive)
RemoveClient(((LLClientView)client).UDPClient);
} }
} }
} }