From 3a04d706c9ebd13214e62fb944b4a8da6860bb91 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 8 Oct 2009 17:34:51 -0700 Subject: Fear the lockless LLUDP implementation! --- .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 252 +++++---------------- 1 file changed, 60 insertions(+), 192 deletions(-) (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index dbb9861..06fa3e2 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -30,6 +30,7 @@ using System.Collections.Generic; using System.Net; using OpenSim.Framework; using OpenMetaverse; +using BclExtras.Collections; using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim; @@ -37,246 +38,113 @@ namespace OpenSim.Region.ClientStack.LindenUDP { public sealed class UDPClientCollection { - Dictionary Dictionary1; - Dictionary Dictionary2; - LLUDPClient[] Array; - ReaderWriterLockImpl rwLock = new ReaderWriterLockImpl(); - object m_sync = new object(); + #region IComparers - public UDPClientCollection() - { - Dictionary1 = new Dictionary(); - Dictionary2 = new Dictionary(); - Array = new LLUDPClient[0]; - } - - public UDPClientCollection(int capacity) + private sealed class UUIDComparer : IComparer { - Dictionary1 = new Dictionary(capacity); - Dictionary2 = new Dictionary(capacity); - Array = new LLUDPClient[0]; + public int Compare(UUID x, UUID y) + { + return x.Guid.CompareTo(y.Guid); + } } - public void Add(UUID key1, IPEndPoint key2, LLUDPClient value) + private sealed class IPEndPointComparer : IComparer { - //rwLock.EnterWriteLock(); - - //try - //{ - // if (Dictionary1.ContainsKey(key1)) - // { - // if (!Dictionary2.ContainsKey(key2)) - // throw new ArgumentException("key1 exists in the dictionary but not key2"); - // } - // else if (Dictionary2.ContainsKey(key2)) - // { - // if (!Dictionary1.ContainsKey(key1)) - // throw new ArgumentException("key2 exists in the dictionary but not key1"); - // } - - // Dictionary1[key1] = value; - // Dictionary2[key2] = value; - - // LLUDPClient[] oldArray = Array; - // int oldLength = oldArray.Length; - - // LLUDPClient[] newArray = new LLUDPClient[oldLength + 1]; - // for (int i = 0; i < oldLength; i++) - // newArray[i] = oldArray[i]; - // newArray[oldLength] = value; - - // Array = newArray; - //} - //finally { rwLock.ExitWriteLock(); } - - lock (m_sync) + public int Compare(IPEndPoint x, IPEndPoint y) { - if (Dictionary1.ContainsKey(key1)) - { - if (!Dictionary2.ContainsKey(key2)) - throw new ArgumentException("key1 exists in the dictionary but not key2"); - } - else if (Dictionary2.ContainsKey(key2)) - { - if (!Dictionary1.ContainsKey(key1)) - throw new ArgumentException("key2 exists in the dictionary but not key1"); - } - - Dictionary1[key1] = value; - Dictionary2[key2] = value; - - LLUDPClient[] oldArray = Array; - int oldLength = oldArray.Length; + int result = x.Address.Address.CompareTo(y.Address.Address); + if (result == 0) result = x.Port.CompareTo(y.Port); + return result; + } + } - LLUDPClient[] newArray = new LLUDPClient[oldLength + 1]; - for (int i = 0; i < oldLength; i++) - newArray[i] = oldArray[i]; - newArray[oldLength] = value; + #endregion IComparers - Array = newArray; - } + private ImmutableMap m_dict1; + private ImmutableMap m_dict2; + private LLUDPClient[] m_array; + public UDPClientCollection() + { + m_dict1 = new ImmutableMap(new UUIDComparer()); + m_dict2 = new ImmutableMap(new IPEndPointComparer()); + m_array = new LLUDPClient[0]; } - public bool Remove(UUID key1, IPEndPoint key2) + public void Add(UUID key1, IPEndPoint key2, LLUDPClient value) { - //rwLock.EnterWriteLock(); - - //try - //{ - // LLUDPClient value; - // if (Dictionary1.TryGetValue(key1, out value)) - // { - // Dictionary1.Remove(key1); - // Dictionary2.Remove(key2); + m_dict1 = m_dict1.Add(key1, value); + m_dict2 = m_dict2.Add(key2, value); + + // Copy the array by hand + LLUDPClient[] oldArray = m_array; + int oldLength = oldArray.Length; + LLUDPClient[] newArray = new LLUDPClient[oldLength + 1]; + + for (int i = 0; i < oldLength; i++) + newArray[i] = oldArray[i]; + newArray[oldLength] = value; + + m_array = newArray; + } - // LLUDPClient[] oldArray = Array; - // int oldLength = oldArray.Length; + public void Remove(UUID key1, IPEndPoint key2) + { + m_dict1 = m_dict1.Delete(key1); + m_dict2 = m_dict2.Delete(key2); - // LLUDPClient[] newArray = new LLUDPClient[oldLength - 1]; - // int j = 0; - // for (int i = 0; i < oldLength; i++) - // { - // if (oldArray[i] != value) - // newArray[j++] = oldArray[i]; - // } + LLUDPClient[] oldArray = m_array; + int oldLength = oldArray.Length; - // Array = newArray; - // return true; - // } - //} - //finally { rwLock.ExitWriteLock(); } + // Copy the array by hand - //return false; + LLUDPClient[] newArray = new LLUDPClient[oldLength - 1]; + int j = 0; - lock (m_sync) + for (int i = 0; i < oldLength; i++) { - LLUDPClient value; - if (Dictionary1.TryGetValue(key1, out value)) - { - Dictionary1.Remove(key1); - Dictionary2.Remove(key2); - - LLUDPClient[] oldArray = Array; - int oldLength = oldArray.Length; - - LLUDPClient[] newArray = new LLUDPClient[oldLength - 1]; - int j = 0; - for (int i = 0; i < oldLength; i++) - { - if (oldArray[i] != value) - newArray[j++] = oldArray[i]; - } - - Array = newArray; - return true; - } + if (oldArray[i].AgentID != key1) + newArray[j++] = oldArray[i]; } - return false; - + m_array = newArray; } public void Clear() { - //rwLock.EnterWriteLock(); - - //try - //{ - // Dictionary1.Clear(); - // Dictionary2.Clear(); - // Array = new LLUDPClient[0]; - //} - //finally { rwLock.ExitWriteLock(); } - - lock (m_sync) - { - Dictionary1.Clear(); - Dictionary2.Clear(); - Array = new LLUDPClient[0]; - } - + m_dict1 = new ImmutableMap(new UUIDComparer()); + m_dict2 = new ImmutableMap(new IPEndPointComparer()); + m_array = new LLUDPClient[0]; } public int Count { - get { return Array.Length; } + get { return m_array.Length; } } public bool ContainsKey(UUID key) { - return Dictionary1.ContainsKey(key); + return m_dict1.ContainsKey(key); } public bool ContainsKey(IPEndPoint key) { - return Dictionary2.ContainsKey(key); + return m_dict2.ContainsKey(key); } public bool TryGetValue(UUID key, out LLUDPClient value) { - ////bool success; - ////bool doLock = !rwLock.IsUpgradeableReadLockHeld; - ////if (doLock) rwLock.EnterReadLock(); - - ////try { success = Dictionary1.TryGetValue(key, out value); } - ////finally { if (doLock) rwLock.ExitReadLock(); } - - ////return success; - - lock (m_sync) - return Dictionary1.TryGetValue(key, out value); - - //try - //{ - // return Dictionary1.TryGetValue(key, out value); - //} - //catch { } - //value = null; - //return false; + return m_dict1.TryGetValue(key, out value); } public bool TryGetValue(IPEndPoint key, out LLUDPClient value) { - ////bool success; - ////bool doLock = !rwLock.IsUpgradeableReadLockHeld; - ////if (doLock) rwLock.EnterReadLock(); - - ////try { success = Dictionary2.TryGetValue(key, out value); } - ////finally { if (doLock) rwLock.ExitReadLock(); } - - ////return success; - - lock (m_sync) - return Dictionary2.TryGetValue(key, out value); - - //try - //{ - // return Dictionary2.TryGetValue(key, out value); - //} - //catch { } - //value = null; - //return false; - + return m_dict2.TryGetValue(key, out value); } public void ForEach(Action action) { - //bool doLock = !rwLock.IsUpgradeableReadLockHeld; - //if (doLock) rwLock.EnterUpgradeableReadLock(); - - //try { Parallel.ForEach(Array, action); } - //finally { if (doLock) rwLock.ExitUpgradeableReadLock(); } - - LLUDPClient[] localArray = null; - lock (m_sync) - { - localArray = new LLUDPClient[Array.Length]; - Array.CopyTo(localArray, 0); - } - - Parallel.ForEach(localArray, action); - + Parallel.ForEach(m_array, action); } } } -- cgit v1.1 From 56a27c37d3e84495988e423be7b52007cea595cc Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 8 Oct 2009 21:51:53 -0700 Subject: Simplified LLUDPClientCollection from three collections down to one. This will prevent any potential problems from inconsistency between the internal collections --- .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 75 ++++------------------ 1 file changed, 11 insertions(+), 64 deletions(-) (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index 06fa3e2..abf3882 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -40,14 +40,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP { #region IComparers - private sealed class UUIDComparer : IComparer - { - public int Compare(UUID x, UUID y) - { - return x.Guid.CompareTo(y.Guid); - } - } - private sealed class IPEndPointComparer : IComparer { public int Compare(IPEndPoint x, IPEndPoint y) @@ -60,91 +52,46 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion IComparers - private ImmutableMap m_dict1; - private ImmutableMap m_dict2; - private LLUDPClient[] m_array; + private ImmutableMap m_dict; public UDPClientCollection() { - m_dict1 = new ImmutableMap(new UUIDComparer()); - m_dict2 = new ImmutableMap(new IPEndPointComparer()); - m_array = new LLUDPClient[0]; + m_dict = new ImmutableMap(new IPEndPointComparer()); } - public void Add(UUID key1, IPEndPoint key2, LLUDPClient value) + public void Add(IPEndPoint key, LLUDPClient value) { - m_dict1 = m_dict1.Add(key1, value); - m_dict2 = m_dict2.Add(key2, value); - - // Copy the array by hand - LLUDPClient[] oldArray = m_array; - int oldLength = oldArray.Length; - LLUDPClient[] newArray = new LLUDPClient[oldLength + 1]; - - for (int i = 0; i < oldLength; i++) - newArray[i] = oldArray[i]; - newArray[oldLength] = value; - - m_array = newArray; + m_dict = m_dict.Add(key, value); } - public void Remove(UUID key1, IPEndPoint key2) + public void Remove(IPEndPoint key) { - m_dict1 = m_dict1.Delete(key1); - m_dict2 = m_dict2.Delete(key2); - - LLUDPClient[] oldArray = m_array; - int oldLength = oldArray.Length; - - // Copy the array by hand - - LLUDPClient[] newArray = new LLUDPClient[oldLength - 1]; - int j = 0; - - for (int i = 0; i < oldLength; i++) - { - if (oldArray[i].AgentID != key1) - newArray[j++] = oldArray[i]; - } - - m_array = newArray; + m_dict = m_dict.Delete(key); } public void Clear() { - m_dict1 = new ImmutableMap(new UUIDComparer()); - m_dict2 = new ImmutableMap(new IPEndPointComparer()); - m_array = new LLUDPClient[0]; + m_dict = new ImmutableMap(new IPEndPointComparer()); } public int Count { - get { return m_array.Length; } - } - - public bool ContainsKey(UUID key) - { - return m_dict1.ContainsKey(key); + get { return m_dict.Count; } } public bool ContainsKey(IPEndPoint key) { - return m_dict2.ContainsKey(key); - } - - public bool TryGetValue(UUID key, out LLUDPClient value) - { - return m_dict1.TryGetValue(key, out value); + return m_dict.ContainsKey(key); } public bool TryGetValue(IPEndPoint key, out LLUDPClient value) { - return m_dict2.TryGetValue(key, out value); + return m_dict.TryGetValue(key, out value); } public void ForEach(Action action) { - Parallel.ForEach(m_array, action); + Parallel.ForEach(m_dict.Values, action); } } } -- cgit v1.1 From a5b9971fd77c0c4bf70656be7f3e7999f59d9f85 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 9 Oct 2009 01:53:06 -0700 Subject: * Added a lock object for the write functions in LLUDPClientCollection (immutable != concurrent write safety) * Allow the UDP server to bind to a user-specified port again * Updated to a newer version of OpenSimUDPBase that streamlines the code even more. This also reintroduces the highly concurrent packet handling which needs more testing --- .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 56 ++++++++++++++++++---- 1 file changed, 48 insertions(+), 8 deletions(-) (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index abf3882..2222a33 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -36,6 +36,9 @@ using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim; namespace OpenSim.Region.ClientStack.LindenUDP { + /// + /// A thread safe mapping from endpoints to client references + /// public sealed class UDPClientCollection { #region IComparers @@ -52,43 +55,80 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion IComparers + /// An immutable dictionary mapping from + /// to references private ImmutableMap m_dict; + /// Immutability grants thread safety for concurrent reads and + /// read-writes, but not concurrent writes + private object m_writeLock; + /// Number of clients in the collection + public int Count { get { return m_dict.Count; } } + + /// + /// Default constructor + /// public UDPClientCollection() { m_dict = new ImmutableMap(new IPEndPointComparer()); } + /// + /// Add a client reference to the collection + /// + /// Remote endpoint of the client + /// Reference to the client object public void Add(IPEndPoint key, LLUDPClient value) { - m_dict = m_dict.Add(key, value); + lock (m_writeLock) + m_dict = m_dict.Add(key, value); } + /// + /// Remove a client from the collection + /// + /// Remote endpoint of the client public void Remove(IPEndPoint key) { - m_dict = m_dict.Delete(key); + lock (m_writeLock) + m_dict = m_dict.Delete(key); } + /// + /// Resets the client collection + /// public void Clear() { - m_dict = new ImmutableMap(new IPEndPointComparer()); - } - - public int Count - { - get { return m_dict.Count; } + lock (m_writeLock) + m_dict = new ImmutableMap(new IPEndPointComparer()); } + /// + /// 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_dict.ContainsKey(key); } + /// + /// 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 LLUDPClient value) { return m_dict.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); -- cgit v1.1 From 76a5cae0b4170b191e370a663bb6355b4dc38fa2 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 9 Oct 2009 02:18:25 -0700 Subject: Forgot to initialize m_writeLock --- OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index 2222a33..2972d46 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -60,7 +60,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private ImmutableMap m_dict; /// Immutability grants thread safety for concurrent reads and /// read-writes, but not concurrent writes - private object m_writeLock; + private object m_writeLock = new object(); /// Number of clients in the collection public int Count { get { return m_dict.Count; } } -- cgit v1.1 From f55b282078e4e7c5ee7d0ca613891302d2b9957d Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 11:28:08 -0700 Subject: Avoid checking m_clients collection twice when a UseCircuitCode packet is received --- OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index 2972d46..4f375e4 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -131,7 +131,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Action to perform on each element public void ForEach(Action action) { - Parallel.ForEach(m_dict.Values, action); + Parallel.ForEach(m_dict.Values, action); } } } -- 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) --- .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 137 --------------------- 1 file changed, 137 deletions(-) delete mode 100644 OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs (limited to 'OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs deleted file mode 100644 index 4f375e4..0000000 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * 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. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project 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 DEVELOPERS ``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 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.Collections.Generic; -using System.Net; -using OpenSim.Framework; -using OpenMetaverse; -using BclExtras.Collections; - -using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim; - -namespace OpenSim.Region.ClientStack.LindenUDP -{ - /// - /// A thread safe mapping from endpoints to client references - /// - public sealed class UDPClientCollection - { - #region IComparers - - private sealed class IPEndPointComparer : IComparer - { - public int Compare(IPEndPoint x, IPEndPoint y) - { - 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 - /// to references - private ImmutableMap m_dict; - /// 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 UDPClientCollection() - { - m_dict = new ImmutableMap(new IPEndPointComparer()); - } - - /// - /// Add a client reference to the collection - /// - /// Remote endpoint of the client - /// Reference to the client object - public void Add(IPEndPoint key, LLUDPClient value) - { - lock (m_writeLock) - m_dict = m_dict.Add(key, value); - } - - /// - /// Remove a client from the collection - /// - /// Remote endpoint of the client - public void Remove(IPEndPoint key) - { - lock (m_writeLock) - m_dict = m_dict.Delete(key); - } - - /// - /// Resets the client collection - /// - public void Clear() - { - lock (m_writeLock) - m_dict = new ImmutableMap(new IPEndPointComparer()); - } - - /// - /// 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_dict.ContainsKey(key); - } - - /// - /// 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 LLUDPClient value) - { - return m_dict.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); - } - } -} -- cgit v1.1