From 494a1e922dd5c86ef54d7a83347ec8ba255e0e83 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 9 Oct 2009 02:10:53 -0700 Subject: Calling .Close() on AutoResetEvent and ManualResetEvent (those classes contain an unmanaged resource that will not automatically be disposed when they are GCed), and commenting out some ManualResetEvents that are not in use yet --- OpenSim/Framework/Communications/GenericAsyncResult.cs | 1 + OpenSim/Framework/Communications/RestClient.cs | 6 +++--- OpenSim/Framework/Parallel.cs | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) (limited to 'OpenSim/Framework') diff --git a/OpenSim/Framework/Communications/GenericAsyncResult.cs b/OpenSim/Framework/Communications/GenericAsyncResult.cs index efd2f43..8e3f62b 100644 --- a/OpenSim/Framework/Communications/GenericAsyncResult.cs +++ b/OpenSim/Framework/Communications/GenericAsyncResult.cs @@ -146,6 +146,7 @@ namespace OpenSim.Framework.Communications // If the operation isn't done, wait for it AsyncWaitHandle.WaitOne(); AsyncWaitHandle.Close(); + m_waitHandle.Close(); m_waitHandle = null; // Allow early GC } diff --git a/OpenSim/Framework/Communications/RestClient.cs b/OpenSim/Framework/Communications/RestClient.cs index d98f47d..a74169e 100644 --- a/OpenSim/Framework/Communications/RestClient.cs +++ b/OpenSim/Framework/Communications/RestClient.cs @@ -105,7 +105,7 @@ namespace OpenSim.Framework.Communications /// /// This flag will help block the main synchroneous method, in case we run in synchroneous mode /// - public static ManualResetEvent _allDone = new ManualResetEvent(false); + //public static ManualResetEvent _allDone = new ManualResetEvent(false); /// /// Default time out period @@ -282,12 +282,12 @@ namespace OpenSim.Framework.Communications else { s.Close(); - _allDone.Set(); + //_allDone.Set(); } } catch (Exception e) { - _allDone.Set(); + //_allDone.Set(); _asyncException = e; } } diff --git a/OpenSim/Framework/Parallel.cs b/OpenSim/Framework/Parallel.cs index 74537ba..6efdad0 100644 --- a/OpenSim/Framework/Parallel.cs +++ b/OpenSim/Framework/Parallel.cs @@ -89,6 +89,7 @@ namespace OpenSim.Framework } threadFinishEvent.WaitOne(); + threadFinishEvent.Close(); if (exception != null) throw new Exception(exception.Message, exception); @@ -148,6 +149,7 @@ namespace OpenSim.Framework } threadFinishEvent.WaitOne(); + threadFinishEvent.Close(); if (exception != null) throw new Exception(exception.Message, exception); @@ -199,6 +201,7 @@ namespace OpenSim.Framework } threadFinishEvent.WaitOne(); + threadFinishEvent.Close(); if (exception != null) throw new Exception(exception.Message, exception); -- cgit v1.1 From c893761319f7e61d13b2d2301180d0f227fde1a9 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 12:50:59 -0700 Subject: * Unregister event handlers in LLUDPServer when a client logs out and disconnects * Move ViewerEffect handling to Scene.PacketHandlers * Removing the unused CloseAllAgents function * Trimming ClientManager down. This class needs to be reworked to keep LLUDP circuit codes from intruding into the abstract OpenSim core code --- OpenSim/Framework/ClientManager.cs | 163 +++---------------------------------- OpenSim/Framework/IScene.cs | 1 - 2 files changed, 13 insertions(+), 151 deletions(-) (limited to 'OpenSim/Framework') diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index 094a3ff..5ebbbc1 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -34,120 +34,33 @@ using OpenMetaverse.Packets; namespace OpenSim.Framework { - public delegate void ForEachClientDelegate(IClientAPI client); - public class ClientManager { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private Dictionary m_clients; - - public ClientManager() - { - m_clients = new Dictionary(); - } + private Dictionary m_clients = new Dictionary(); - public void ForEachClient(ForEachClientDelegate whatToDo) + public void Add(uint circuitCode, IClientAPI client) { - IClientAPI[] LocalClients; lock (m_clients) - { - LocalClients = new IClientAPI[m_clients.Count]; - m_clients.Values.CopyTo(LocalClients, 0); - } - - for (int i = 0; i < LocalClients.Length; i++) - { - try - { - whatToDo(LocalClients[i]); - } - catch (Exception e) - { - m_log.Warn("[CLIENT]: Unable to do ForEachClient for one of the clients" + "\n Reason: " + e.ToString()); - } - } + m_clients.Add(circuitCode, client); } - public void Remove(uint id) + public bool Remove(uint circuitCode) { lock (m_clients) - { - m_clients.Remove(id); - } - } - - public void Add(uint id, IClientAPI client) - { - lock (m_clients) - { - m_clients.Add(id, client); - } - } - - /// - /// Pass incoming packet to client. - /// - /// uint identifying the connection/client. - /// object containing the packet. - public void InPacket(uint circuitCode, object packet) - { - IClientAPI client; - bool tryGetRet = false; - - lock (m_clients) - tryGetRet = m_clients.TryGetValue(circuitCode, out client); - - if (tryGetRet) - { - client.InPacket(packet); - } + return m_clients.Remove(circuitCode); } - public void CloseAllAgents(uint circuitCode) + public bool TryGetClient(uint circuitCode, out IClientAPI user) { - IClientAPI client; - bool tryGetRet = false; lock (m_clients) - tryGetRet = m_clients.TryGetValue(circuitCode, out client); - if (tryGetRet) - { - CloseAllCircuits(client.AgentId); - } - } - - public void CloseAllCircuits(UUID agentId) - { - uint[] circuits = GetAllCircuits(agentId); - // We're using a for loop here so changes to the circuits don't cause it to completely fail. - - for (int i = 0; i < circuits.Length; i++) - { - IClientAPI client; - try - { - bool tryGetRet = false; - lock (m_clients) - tryGetRet = m_clients.TryGetValue(circuits[i], out client); - if (tryGetRet) - { - Remove(client.CircuitCode); - client.Close(false); - } - } - catch (Exception e) - { - m_log.Error(string.Format("[CLIENT]: Unable to shutdown circuit for: {0}\n Reason: {1}", agentId, e)); - } - } + return m_clients.TryGetValue(circuitCode, out user); } - // [Obsolete("Using Obsolete to drive development is invalid. Obsolete presumes that something new has already been created to replace this.")] - public uint[] GetAllCircuits(UUID agentId) + public void ForEachClient(Action action) { - List circuits = new List(); - // Wasteful, I know - IClientAPI[] LocalClients = new IClientAPI[0]; + IClientAPI[] LocalClients; lock (m_clients) { LocalClients = new IClientAPI[m_clients.Count]; @@ -156,65 +69,15 @@ namespace OpenSim.Framework for (int i = 0; i < LocalClients.Length; i++) { - if (LocalClients[i].AgentId == agentId) + try { - circuits.Add(LocalClients[i].CircuitCode); + action(LocalClients[i]); } - } - return circuits.ToArray(); - } - - public List GetAllCircuitCodes() - { - List circuits; - - lock (m_clients) - { - circuits = new List(m_clients.Keys); - } - - return circuits; - } - - public void ViewerEffectHandler(IClientAPI sender, List args) - { - // TODO: don't create new blocks if recycling an old packet - List effectBlock = new List(); - for (int i = 0; i < args.Count; i++) - { - ViewerEffectPacket.EffectBlock effect = new ViewerEffectPacket.EffectBlock(); - effect.AgentID = args[i].AgentID; - effect.Color = args[i].Color; - effect.Duration = args[i].Duration; - effect.ID = args[i].ID; - effect.Type = args[i].Type; - effect.TypeData = args[i].TypeData; - effectBlock.Add(effect); - } - ViewerEffectPacket.EffectBlock[] effectBlockArray = effectBlock.ToArray(); - - IClientAPI[] LocalClients; - lock (m_clients) - { - LocalClients = new IClientAPI[m_clients.Count]; - m_clients.Values.CopyTo(LocalClients, 0); - } - - for (int i = 0; i < LocalClients.Length; i++) - { - if (LocalClients[i].AgentId != sender.AgentId) + catch (Exception e) { - LocalClients[i].SendViewerEffect(effectBlockArray); + m_log.Warn("[CLIENT]: Unable to do ForEachClient for one of the clients" + "\n Reason: " + e.ToString()); } } } - - public bool TryGetClient(uint circuitId, out IClientAPI user) - { - lock (m_clients) - { - return m_clients.TryGetValue(circuitId, out user); - } - } } } diff --git a/OpenSim/Framework/IScene.cs b/OpenSim/Framework/IScene.cs index 489653f..f34027d 100644 --- a/OpenSim/Framework/IScene.cs +++ b/OpenSim/Framework/IScene.cs @@ -71,7 +71,6 @@ namespace OpenSim.Framework void AddNewClient(IClientAPI client); void RemoveClient(UUID agentID); - void CloseAllAgents(uint circuitcode); void Restart(int seconds); //RegionInfo OtherRegionUp(RegionInfo thisRegion); -- cgit v1.1 From 23a334b9f54a1ef5df3b503c165e7b76b746a2b1 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 14:50:03 -0700 Subject: * Rewrote ClientManager to remove Lindenisms from OpenSim core, improve performance by removing locks, and replace LLUDPClientCollection * Removed the confusing (and LL-specific) shutdowncircuit parameter from IClientAPI.Close() * Updated the LLUDP code to only use ClientManager instead of trying to synchronize ClientManager and m_clients * Remove clients asynchronously since it is a very slow operation (including a 2000ms sleep) --- OpenSim/Framework/ClientManager.cs | 170 +++++++++++++++++++++++++++++++------ OpenSim/Framework/IClientAPI.cs | 2 +- 2 files changed, 145 insertions(+), 27 deletions(-) (limited to 'OpenSim/Framework') diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index 5ebbbc1..4edfabe 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -28,56 +28,174 @@ using System; using System.Collections.Generic; using System.Reflection; -using log4net; +using System.Net; +using BclExtras.Collections; using OpenMetaverse; using OpenMetaverse.Packets; namespace OpenSim.Framework { + /// + /// Maps from client AgentID and RemoteEndPoint values to IClientAPI + /// references for all of the connected clients + /// public class ClientManager { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + #region IComparers - private Dictionary m_clients = new Dictionary(); - - public void Add(uint circuitCode, IClientAPI client) + private sealed class UUIDComparer : IComparer { - lock (m_clients) - m_clients.Add(circuitCode, client); + public int Compare(UUID x, UUID y) + { + return x.CompareTo(y); + } } - public bool Remove(uint circuitCode) + private sealed class IPEndPointComparer : IComparer { - lock (m_clients) - return m_clients.Remove(circuitCode); + 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; + } } - public bool TryGetClient(uint circuitCode, out IClientAPI user) + #endregion IComparers + + /// An immutable dictionary mapping from + /// to references + private ImmutableMap m_dict; + /// An immutable dictionary mapping from + /// to references + private ImmutableMap m_dict2; + /// Immutability grants thread safety for concurrent reads and + /// read-writes, but not concurrent writes + private object m_writeLock = new object(); + + /// Number of clients in the collection + public int Count { get { return m_dict.Count; } } + + /// + /// Default constructor + /// + public ClientManager() { - lock (m_clients) - return m_clients.TryGetValue(circuitCode, out user); + m_dict = new ImmutableMap(new UUIDComparer()); + m_dict2 = new ImmutableMap(new IPEndPointComparer()); } - public void ForEachClient(Action action) + /// + /// Add a client reference to the collection if it does not already + /// exist + /// + /// UUID of the client + /// Remote endpoint of the client + /// Reference to the client object + /// True if the client reference was successfully added, + /// otherwise false if the given key already existed in the collection + public bool Add(UUID key, IPEndPoint key2, IClientAPI value) { - IClientAPI[] LocalClients; - lock (m_clients) + lock (m_writeLock) { - LocalClients = new IClientAPI[m_clients.Count]; - m_clients.Values.CopyTo(LocalClients, 0); - } - - for (int i = 0; i < LocalClients.Length; i++) - { - try + if (!m_dict.ContainsKey(key) && !m_dict2.ContainsKey(key2)) { - action(LocalClients[i]); + m_dict = m_dict.Add(key, value); + m_dict2 = m_dict2.Add(key2, value); + + return true; } - catch (Exception e) + else { - m_log.Warn("[CLIENT]: Unable to do ForEachClient for one of the clients" + "\n Reason: " + e.ToString()); + return false; } } } + + /// + /// Remove a client from the collection + /// + /// UUID of the client + /// Remote endpoint of the client + public void Remove(UUID key, IPEndPoint key2) + { + lock (m_writeLock) + { + m_dict = m_dict.Delete(key); + m_dict2 = m_dict2.Delete(key2); + } + } + + /// + /// Resets the client collection + /// + public void Clear() + { + lock (m_writeLock) + { + m_dict = new ImmutableMap(new UUIDComparer()); + m_dict2 = new ImmutableMap(new IPEndPointComparer()); + } + } + + /// + /// Checks if a UUID is in the collection + /// + /// UUID to check for + /// True if the UUID was found in the collection, otherwise false + public bool ContainsKey(UUID key) + { + return m_dict.ContainsKey(key); + } + + /// + /// Checks if an endpoint is in the collection + /// + /// Endpoint to check for + /// True if the endpoint was found in the collection, otherwise false + public bool ContainsKey(IPEndPoint key) + { + return m_dict2.ContainsKey(key); + } + + /// + /// Attempts to fetch a value out of the collection + /// + /// UUID of the client to retrieve + /// Retrieved client, or null on lookup failure + /// True if the lookup succeeded, otherwise false + public bool TryGetValue(UUID key, out IClientAPI value) + { + return m_dict.TryGetValue(key, out value); + } + + /// + /// Attempts to fetch a value out of the collection + /// + /// Endpoint of the client to retrieve + /// Retrieved client, or null on lookup failure + /// True if the lookup succeeded, otherwise false + public bool TryGetValue(IPEndPoint key, out IClientAPI value) + { + return m_dict2.TryGetValue(key, out value); + } + + /// + /// Performs a given task in parallel for each of the elements in the + /// collection + /// + /// Action to perform on each element + public void ForEach(Action action) + { + Parallel.ForEach(m_dict.Values, action); + } } } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index d3bd9e7..99ea0d5 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -802,7 +802,7 @@ namespace OpenSim.Framework void InPacket(object NewPack); void ProcessInPacket(Packet NewPack); - void Close(bool ShutdownCircuit); + void Close(); void Kick(string message); /// -- cgit v1.1 From 395a8680c3633ca131e7481f765517311ef51710 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 16:53:19 -0700 Subject: * Fixed a bug where clients were being added to ClientManager twice * Changed the ClientManager interface to reduce potential errors with duplicate or mismatched keys * Added IClientAPI.RemoteEndPoint, which can (hopefully) eventually replace IClientAPI.CircuitCode * Changed the order of operations during client shutdown --- OpenSim/Framework/ClientManager.cs | 22 +++++++++++----------- OpenSim/Framework/IClientAPI.cs | 2 ++ 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'OpenSim/Framework') diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index 4edfabe..aefe425 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -97,19 +97,17 @@ namespace OpenSim.Framework /// Add a client reference to the collection if it does not already /// exist /// - /// UUID of the client - /// Remote endpoint of the client /// Reference to the client object /// True if the client reference was successfully added, /// otherwise false if the given key already existed in the collection - public bool Add(UUID key, IPEndPoint key2, IClientAPI value) + public bool Add(IClientAPI value) { lock (m_writeLock) { - if (!m_dict.ContainsKey(key) && !m_dict2.ContainsKey(key2)) + if (!m_dict.ContainsKey(value.AgentId) && !m_dict2.ContainsKey(value.RemoteEndPoint)) { - m_dict = m_dict.Add(key, value); - m_dict2 = m_dict2.Add(key2, value); + m_dict = m_dict.Add(value.AgentId, value); + m_dict2 = m_dict2.Add(value.RemoteEndPoint, value); return true; } @@ -123,14 +121,16 @@ namespace OpenSim.Framework /// /// Remove a client from the collection /// - /// UUID of the client - /// Remote endpoint of the client - public void Remove(UUID key, IPEndPoint key2) + /// Reference to the client object + public void Remove(IClientAPI value) { lock (m_writeLock) { - m_dict = m_dict.Delete(key); - m_dict2 = m_dict2.Delete(key2); + if (m_dict.ContainsKey(value.AgentId)) + m_dict = m_dict.Delete(value.AgentId); + + if (m_dict2.ContainsKey(value.RemoteEndPoint)) + m_dict2 = m_dict2.Delete(value.RemoteEndPoint); } } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 99ea0d5..29846f5 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -561,6 +561,8 @@ namespace OpenSim.Framework // [Obsolete("LLClientView Specific - Circuits are unique to LLClientView")] uint CircuitCode { get; } + IPEndPoint RemoteEndPoint { get; } + event GenericMessage OnGenericMessage; // [Obsolete("LLClientView Specific - Replace with more bare-bones arguments.")] -- cgit v1.1 From dc11643c007adf7a640ec4fbabe25995352aaa18 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 17:33:45 -0700 Subject: * Consolidated adding / removing ClientManager IClientAPIs to two places in Scene * Added some missing implementations of IClientAPI.RemoteEndPoint * Added a ClientManager.Remove(UUID) overload * Removed a reference to a missing project from prebuild.xml --- OpenSim/Framework/ClientManager.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'OpenSim/Framework') diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index aefe425..fd5f87f 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -134,6 +134,20 @@ namespace OpenSim.Framework } } + public void Remove(UUID key) + { + lock (m_writeLock) + { + IClientAPI client; + + if (m_dict.TryGetValue(key, out client)) + { + m_dict = m_dict.Delete(key); + m_dict2 = m_dict2.Delete(client.RemoteEndPoint); + } + } + } + /// /// Resets the client collection /// -- cgit v1.1 From e8c1e69a0dbab1a7db894eeff6b052bbd350a8f5 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 18:56:54 -0700 Subject: * Copied LocklessQueue.cs into OpenSim.Framework and added the .Count property and .Clear() method * Changed the way the QueueEmpty callback is fired. It will be fired asynchronously as soon as an empty queue is detected (this can happen immediately following a dequeue), and will not be fired again until at least one packet is dequeued from that queue. This will give callbacks advanced notice of an empty queue and prevent callbacks from stacking up while the queue is empty * Added LLUDPClient.IsConnected checks in several places to prevent unwanted network activity after a client disconnects * Prevent LLClientView.Close() from being called twice every disconnect * Removed the packet resend limit and improved the client timeout check --- OpenSim/Framework/ClientManager.cs | 23 +++---- OpenSim/Framework/LocklessQueue.cs | 130 +++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 14 deletions(-) create mode 100644 OpenSim/Framework/LocklessQueue.cs (limited to 'OpenSim/Framework') diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index fd5f87f..367bc6a 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -121,20 +121,10 @@ namespace OpenSim.Framework /// /// Remove a client from the collection /// - /// Reference to the client object - public void Remove(IClientAPI value) - { - lock (m_writeLock) - { - if (m_dict.ContainsKey(value.AgentId)) - m_dict = m_dict.Delete(value.AgentId); - - if (m_dict2.ContainsKey(value.RemoteEndPoint)) - m_dict2 = m_dict2.Delete(value.RemoteEndPoint); - } - } - - public void Remove(UUID key) + /// UUID of the client to remove + /// True if a client was removed, or false if the given UUID + /// was not present in the collection + public bool Remove(UUID key) { lock (m_writeLock) { @@ -144,6 +134,11 @@ namespace OpenSim.Framework { m_dict = m_dict.Delete(key); m_dict2 = m_dict2.Delete(client.RemoteEndPoint); + return true; + } + else + { + return false; } } } diff --git a/OpenSim/Framework/LocklessQueue.cs b/OpenSim/Framework/LocklessQueue.cs new file mode 100644 index 0000000..dd3d201 --- /dev/null +++ b/OpenSim/Framework/LocklessQueue.cs @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2009, openmetaverse.org + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.org nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Threading; + +namespace OpenSim.Framework +{ + public sealed class LocklessQueue + { + private sealed class SingleLinkNode + { + public SingleLinkNode Next; + public T Item; + } + + SingleLinkNode head; + SingleLinkNode tail; + int count; + + public int Count { get { return count; } } + + public LocklessQueue() + { + Init(); + } + + public void Enqueue(T item) + { + SingleLinkNode oldTail = null; + SingleLinkNode oldTailNext; + + SingleLinkNode newNode = new SingleLinkNode(); + newNode.Item = item; + + bool newNodeWasAdded = false; + + while (!newNodeWasAdded) + { + oldTail = tail; + oldTailNext = oldTail.Next; + + if (tail == oldTail) + { + if (oldTailNext == null) + newNodeWasAdded = CAS(ref tail.Next, null, newNode); + else + CAS(ref tail, oldTail, oldTailNext); + } + } + + CAS(ref tail, oldTail, newNode); + Interlocked.Increment(ref count); + } + + public bool Dequeue(out T item) + { + item = default(T); + SingleLinkNode oldHead = null; + bool haveAdvancedHead = false; + + while (!haveAdvancedHead) + { + oldHead = head; + SingleLinkNode oldTail = tail; + SingleLinkNode oldHeadNext = oldHead.Next; + + if (oldHead == head) + { + if (oldHead == oldTail) + { + if (oldHeadNext == null) + return false; + + CAS(ref tail, oldTail, oldHeadNext); + } + else + { + item = oldHeadNext.Item; + haveAdvancedHead = CAS(ref head, oldHead, oldHeadNext); + } + } + } + + Interlocked.Decrement(ref count); + return true; + } + + public void Clear() + { + Init(); + } + + private void Init() + { + count = 0; + head = tail = new SingleLinkNode(); + } + + private static bool CAS(ref SingleLinkNode location, SingleLinkNode comparand, SingleLinkNode newValue) + { + return + (object)comparand == + (object)Interlocked.CompareExchange(ref location, newValue, comparand); + } + } +} \ No newline at end of file -- cgit v1.1 From 4135b0c4dcd142fe43b4c1b020d41a72d9df63dd Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 19:45:38 -0700 Subject: * Split Task category into Task and State * Crude prioritization hack --- OpenSim/Framework/ThrottleOutPacketType.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'OpenSim/Framework') diff --git a/OpenSim/Framework/ThrottleOutPacketType.cs b/OpenSim/Framework/ThrottleOutPacketType.cs index fd490a5..eb42fea 100644 --- a/OpenSim/Framework/ThrottleOutPacketType.cs +++ b/OpenSim/Framework/ThrottleOutPacketType.cs @@ -31,13 +31,23 @@ namespace OpenSim.Framework { public enum ThrottleOutPacketType : int { - Unknown = -1, // Also doubles as 'do not throttle' + /// Unthrottled packets + Unknown = -1, + /// Packets that are being resent Resend = 0, + /// Terrain data Land = 1, + /// Wind data Wind = 2, + /// Cloud data Cloud = 3, - Task = 4, - Texture = 5, - Asset = 6, + /// Texture assets + Texture = 4, + /// Non-texture assets + Asset = 5, + /// Avatar and primitive data + State = 6, + /// Any packets that do not fit into the other throttles + Task = 7, } } -- cgit v1.1 From 1e9e9df0b3c2c6fad5e94db96c799bb31c193af1 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Oct 2009 14:25:58 -0700 Subject: * 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" --- OpenSim/Framework/ClientManager.cs | 150 ++++++++++++++++++++----------------- 1 file changed, 81 insertions(+), 69 deletions(-) (limited to 'OpenSim/Framework') diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index 367bc6a..61b59e7 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -29,7 +29,6 @@ using System; using System.Collections.Generic; using System.Reflection; using System.Net; -using BclExtras.Collections; using OpenMetaverse; using OpenMetaverse.Packets; @@ -41,56 +40,29 @@ namespace OpenSim.Framework /// public class ClientManager { - #region IComparers - - private sealed class UUIDComparer : IComparer - { - public int Compare(UUID x, UUID y) - { - return x.CompareTo(y); - } - } - - private sealed class IPEndPointComparer : IComparer - { - 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 - - /// An immutable dictionary mapping from + /// A dictionary mapping from /// to references - private ImmutableMap m_dict; - /// An immutable dictionary mapping from + private Dictionary m_dict1; + /// A dictionary mapping from /// to references - private ImmutableMap m_dict2; - /// Immutability grants thread safety for concurrent reads and - /// read-writes, but not concurrent writes - private object m_writeLock = new object(); + private Dictionary m_dict2; + /// An immutable collection of + /// references + private IClientAPI[] m_array; + /// Synchronization object for writing to the collections + private object m_syncRoot = new object(); /// Number of clients in the collection - public int Count { get { return m_dict.Count; } } + public int Count { get { return m_dict1.Count; } } /// /// Default constructor /// public ClientManager() { - m_dict = new ImmutableMap(new UUIDComparer()); - m_dict2 = new ImmutableMap(new IPEndPointComparer()); + m_dict1 = new Dictionary(); + m_dict2 = new Dictionary(); + m_array = new IClientAPI[0]; } /// @@ -102,20 +74,26 @@ namespace OpenSim.Framework /// otherwise false if the given key already existed in the collection public bool Add(IClientAPI value) { - lock (m_writeLock) + lock (m_syncRoot) { - if (!m_dict.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 - { + if (m_dict1.ContainsKey(value.AgentId) || m_dict2.ContainsKey(value.RemoteEndPoint)) 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; } /// @@ -126,21 +104,31 @@ namespace OpenSim.Framework /// was not present in the collection public bool Remove(UUID key) { - lock (m_writeLock) + lock (m_syncRoot) { - IClientAPI client; - - if (m_dict.TryGetValue(key, out client)) + IClientAPI value; + if (m_dict1.TryGetValue(key, out value)) { - m_dict = m_dict.Delete(key); - m_dict2 = m_dict2.Delete(client.RemoteEndPoint); + m_dict1.Remove(key); + 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; } - else - { - return false; - } } + + return false; } /// @@ -148,10 +136,11 @@ namespace OpenSim.Framework /// public void Clear() { - lock (m_writeLock) + lock (m_syncRoot) { - m_dict = new ImmutableMap(new UUIDComparer()); - m_dict2 = new ImmutableMap(new IPEndPointComparer()); + m_dict1.Clear(); + m_dict2.Clear(); + m_array = new IClientAPI[0]; } } @@ -162,7 +151,7 @@ namespace OpenSim.Framework /// True if the UUID was found in the collection, otherwise false public bool ContainsKey(UUID key) { - return m_dict.ContainsKey(key); + return m_dict1.ContainsKey(key); } /// @@ -183,7 +172,12 @@ namespace OpenSim.Framework /// True if the lookup succeeded, otherwise false 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; + } } /// @@ -194,7 +188,12 @@ namespace OpenSim.Framework /// True if the lookup succeeded, otherwise false 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; + } } /// @@ -204,7 +203,20 @@ namespace OpenSim.Framework /// Action to perform on each element public void ForEach(Action action) { - Parallel.ForEach(m_dict.Values, action); + IClientAPI[] localArray = m_array; + Parallel.ForEach(localArray, action); + } + + /// + /// Performs a given task synchronously for each of the elements in + /// the collection + /// + /// Action to perform on each element + public void ForEachSync(Action action) + { + IClientAPI[] localArray = m_array; + for (int i = 0; i < localArray.Length; i++) + action(localArray[i]); } } } -- cgit v1.1 From 82012ec4e3c441021795c66112a66e002d459e73 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Oct 2009 16:21:48 -0700 Subject: * Clean up the SetThrottle() code and add a maxBurstRate parameter to allow more tweaking in the future --- OpenSim/Framework/ThrottleOutPacketType.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'OpenSim/Framework') diff --git a/OpenSim/Framework/ThrottleOutPacketType.cs b/OpenSim/Framework/ThrottleOutPacketType.cs index eb42fea..e21ff32 100644 --- a/OpenSim/Framework/ThrottleOutPacketType.cs +++ b/OpenSim/Framework/ThrottleOutPacketType.cs @@ -41,13 +41,14 @@ namespace OpenSim.Framework Wind = 2, /// Cloud data Cloud = 3, + /// Any packets that do not fit into the other throttles + Task = 4, /// Texture assets - Texture = 4, + Texture = 5, /// Non-texture assets - Asset = 5, + Asset = 6, /// Avatar and primitive data - State = 6, - /// Any packets that do not fit into the other throttles - Task = 7, + /// This is a sub-category of Task + State = 7, } } -- cgit v1.1