From 80a8a9c4a7c9f1f0351f70f944a69650e9eca930 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 16 Oct 2009 14:34:42 -0700 Subject: Converted FireAndForget methods to use a singleton pattern to attempt to work around a Mono bug with nested delegates --- OpenSim/Framework/Util.cs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'OpenSim/Framework/Util.cs') diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 38729c6..3203fc1 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -44,6 +44,7 @@ using System.Xml; using log4net; using Nini.Config; using Nwc.XmlRpc; +using BclExtras; using OpenMetaverse; using OpenMetaverse.StructuredData; @@ -1269,14 +1270,32 @@ namespace OpenSim.Framework #region FireAndForget Threading Pattern + /// + /// Created to work around a limitation in Mono with nested delegates + /// + private class FireAndForgetWrapper + { + public void FireAndForget(System.Threading.WaitCallback callback) + { + callback.BeginInvoke(null, EndFireAndForget, callback); + } + + public void FireAndForget(System.Threading.WaitCallback callback, object obj) + { + callback.BeginInvoke(obj, EndFireAndForget, callback); + } + } + public static void FireAndForget(System.Threading.WaitCallback callback) { - callback.BeginInvoke(null, EndFireAndForget, callback); + FireAndForgetWrapper wrapper = Singleton.GetInstance(); + wrapper.FireAndForget(callback); } public static void FireAndForget(System.Threading.WaitCallback callback, object obj) { - callback.BeginInvoke(obj, EndFireAndForget, callback); + FireAndForgetWrapper wrapper = Singleton.GetInstance(); + wrapper.FireAndForget(callback, obj); } private static void EndFireAndForget(IAsyncResult ar) -- cgit v1.1 From 142008121e2e9c5ca5fca5de07b8a14e37279800 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Mon, 19 Oct 2009 15:19:09 -0700 Subject: * Change Util.FireAndForget to use ThreadPool.UnsafeQueueUserWorkItem(). This avoids .NET remoting and a managed->unmanaged->managed jump. Overall, a night and day performance difference * Initialize the LLClientView prim full update queue to the number of prims in the scene for a big performance boost * Reordered some comparisons on hot code paths for a minor speed boost * Removed an unnecessary call to the expensive DateTime.Now function (if you *have* to get the current time as opposed to Environment.TickCount, always use DateTime.UtcNow) * Don't fire the queue empty callback for the Resend category * Run the outgoing packet handler thread loop for each client synchronously. It seems like more time was being spent doing the execution asynchronously, and it made deadlocks very difficult to track down * Rewrote some expensive math in LandObject.cs * Optimized EntityManager to only lock on operations that need locking, and use TryGetValue() where possible * Only update the attachment database when an object is attached or detached * Other small misc. performance improvements --- OpenSim/Framework/Util.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'OpenSim/Framework/Util.cs') diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 3203fc1..d5ae3b7 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -1273,7 +1273,7 @@ namespace OpenSim.Framework /// /// Created to work around a limitation in Mono with nested delegates /// - private class FireAndForgetWrapper + /*private class FireAndForgetWrapper { public void FireAndForget(System.Threading.WaitCallback callback) { @@ -1284,21 +1284,23 @@ namespace OpenSim.Framework { callback.BeginInvoke(obj, EndFireAndForget, callback); } - } + }*/ public static void FireAndForget(System.Threading.WaitCallback callback) { - FireAndForgetWrapper wrapper = Singleton.GetInstance(); - wrapper.FireAndForget(callback); + //FireAndForgetWrapper wrapper = Singleton.GetInstance(); + //wrapper.FireAndForget(callback); + System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, null); } public static void FireAndForget(System.Threading.WaitCallback callback, object obj) { - FireAndForgetWrapper wrapper = Singleton.GetInstance(); - wrapper.FireAndForget(callback, obj); + //FireAndForgetWrapper wrapper = Singleton.GetInstance(); + //wrapper.FireAndForget(callback, obj); + System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, obj); } - private static void EndFireAndForget(IAsyncResult ar) + /*private static void EndFireAndForget(IAsyncResult ar) { System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState; @@ -1306,7 +1308,7 @@ namespace OpenSim.Framework catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); } ar.AsyncWaitHandle.Close(); - } + }*/ #endregion FireAndForget Threading Pattern } -- cgit v1.1 From 8a336c6860d66b9fbba6922c32e7a57fd355c57e Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 22 Oct 2009 02:28:53 +0100 Subject: Add MaxPoolThreads in startup to limit the size of the thread pool used for FireAndForget. This lets us limit concurrency to make OpenSim play nice --- OpenSim/Framework/Util.cs | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) (limited to 'OpenSim/Framework/Util.cs') diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index d5ae3b7..9dfb75e 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -41,12 +41,14 @@ using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Xml; +using System.Threading; using log4net; using Nini.Config; using Nwc.XmlRpc; using BclExtras; using OpenMetaverse; using OpenMetaverse.StructuredData; +using Amib.Threading; namespace OpenSim.Framework { @@ -55,6 +57,8 @@ namespace OpenSim.Framework /// public class Util { + private static SmartThreadPool m_ThreadPool = null; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static uint nextXferID = 5000; @@ -1293,22 +1297,28 @@ namespace OpenSim.Framework System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, null); } - public static void FireAndForget(System.Threading.WaitCallback callback, object obj) + public static void SetMaxThreads(int maxThreads) { - //FireAndForgetWrapper wrapper = Singleton.GetInstance(); - //wrapper.FireAndForget(callback, obj); - System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, obj); - } + STPStartInfo startInfo = new STPStartInfo(); + startInfo.IdleTimeout = 2000; // 2 seconds + startInfo.MaxWorkerThreads = maxThreads; + startInfo.MinWorkerThreads = 5; + startInfo.StackSize = 524288; + startInfo.ThreadPriority = ThreadPriority.Normal; - /*private static void EndFireAndForget(IAsyncResult ar) - { - System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState; + startInfo.StartSuspended = false; - try { callback.EndInvoke(ar); } - catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); } + m_ThreadPool = new SmartThreadPool(startInfo); + } - ar.AsyncWaitHandle.Close(); - }*/ + public static void FireAndForget(System.Threading.WaitCallback callback, object obj) + { + m_ThreadPool.QueueWorkItem(new WorkItemCallback(delegate(object o) + { + callback(o); + return null; + }), obj); + } #endregion FireAndForget Threading Pattern } -- cgit v1.1 From 8ce4fd7234bd460652f6159a3b7a21d2bebee05d Mon Sep 17 00:00:00 2001 From: Melanie Date: Thu, 22 Oct 2009 04:02:26 +0100 Subject: Reduce the default pool threads to 15 (from 30) and the minimum from 5 to 2 --- OpenSim/Framework/Util.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenSim/Framework/Util.cs') diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 9dfb75e..7dde6dd 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -1302,7 +1302,7 @@ namespace OpenSim.Framework STPStartInfo startInfo = new STPStartInfo(); startInfo.IdleTimeout = 2000; // 2 seconds startInfo.MaxWorkerThreads = maxThreads; - startInfo.MinWorkerThreads = 5; + startInfo.MinWorkerThreads = 2; startInfo.StackSize = 524288; startInfo.ThreadPriority = ThreadPriority.Normal; -- cgit v1.1 From 32ccd5bb40447ea4d96f1181cf73edff3645a55a Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 21 Oct 2009 23:03:18 -0700 Subject: * Changed the misc. methods calling ThreadPool.UnsafeQueueUserWorkItem() to Util.FireAndForget() * Changed Util.FireAndForget() to use any of five different methods set with async_call_method in the [Startup] section of OpenSim.ini. Look at the example config for possible values --- OpenSim/Framework/Util.cs | 70 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 19 deletions(-) (limited to 'OpenSim/Framework/Util.cs') diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index d5ae3b7..02f6d12 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -51,6 +51,18 @@ using OpenMetaverse.StructuredData; namespace OpenSim.Framework { /// + /// The method used by Util.FireAndForget for asynchronously firing events + /// + public enum FireAndForgetMethod + { + UnsafeQueueUserWorkItem, + QueueUserWorkItem, + BeginInvoke, + SmartThreadPool, + Thread, + } + + /// /// Miscellaneous utility functions /// public class Util @@ -70,7 +82,9 @@ namespace OpenSim.Framework public static readonly Regex UUIDPattern = new Regex("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); - + + public static FireAndForgetMethod FireAndForgetMethod = FireAndForgetMethod.UnsafeQueueUserWorkItem; + /// /// Linear interpolates B<->C using percent A /// @@ -1273,7 +1287,7 @@ namespace OpenSim.Framework /// /// Created to work around a limitation in Mono with nested delegates /// - /*private class FireAndForgetWrapper + private class FireAndForgetWrapper { public void FireAndForget(System.Threading.WaitCallback callback) { @@ -1284,32 +1298,50 @@ namespace OpenSim.Framework { callback.BeginInvoke(obj, EndFireAndForget, callback); } - }*/ + + private static void EndFireAndForget(IAsyncResult ar) + { + System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState; + + try { callback.EndInvoke(ar); } + catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); } + + ar.AsyncWaitHandle.Close(); + } + } public static void FireAndForget(System.Threading.WaitCallback callback) { - //FireAndForgetWrapper wrapper = Singleton.GetInstance(); - //wrapper.FireAndForget(callback); - System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, null); + FireAndForget(callback, null); } public static void FireAndForget(System.Threading.WaitCallback callback, object obj) { - //FireAndForgetWrapper wrapper = Singleton.GetInstance(); - //wrapper.FireAndForget(callback, obj); - System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, obj); + switch (FireAndForgetMethod) + { + case FireAndForgetMethod.UnsafeQueueUserWorkItem: + System.Threading.ThreadPool.UnsafeQueueUserWorkItem(callback, obj); + break; + case FireAndForgetMethod.QueueUserWorkItem: + System.Threading.ThreadPool.QueueUserWorkItem(callback, obj); + break; + case FireAndForgetMethod.BeginInvoke: + FireAndForgetWrapper wrapper = Singleton.GetInstance(); + wrapper.FireAndForget(callback, obj); + break; + case FireAndForgetMethod.SmartThreadPool: + Amib.Threading.SmartThreadPool stp = Singleton.GetInstance(); + stp.QueueWorkItem(delegate(object o) { callback(o); return null; }, obj); + break; + case FireAndForgetMethod.Thread: + System.Threading.Thread thread = new System.Threading.Thread(delegate(object o) { callback(o); }); + thread.Start(obj); + break; + default: + throw new NotImplementedException(); + } } - /*private static void EndFireAndForget(IAsyncResult ar) - { - System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState; - - try { callback.EndInvoke(ar); } - catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); } - - ar.AsyncWaitHandle.Close(); - }*/ - #endregion FireAndForget Threading Pattern } } -- cgit v1.1