diff options
-rw-r--r-- | OpenSim/Framework/Util.cs | 143 |
1 files changed, 114 insertions, 29 deletions
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index c2c9698..ed94c6f 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs | |||
@@ -116,6 +116,16 @@ namespace OpenSim.Framework | |||
116 | { | 116 | { |
117 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 117 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
118 | 118 | ||
119 | /// <summary> | ||
120 | /// Log every invocation of a thread using the threadpool. | ||
121 | /// </summary> | ||
122 | public static bool LogThreadPool { get; set; } | ||
123 | |||
124 | static Util() | ||
125 | { | ||
126 | LogThreadPool = false; | ||
127 | } | ||
128 | |||
119 | private static uint nextXferID = 5000; | 129 | private static uint nextXferID = 5000; |
120 | private static Random randomClass = new Random(); | 130 | private static Random randomClass = new Random(); |
121 | 131 | ||
@@ -1887,10 +1897,17 @@ namespace OpenSim.Framework | |||
1887 | } | 1897 | } |
1888 | } | 1898 | } |
1889 | 1899 | ||
1900 | |||
1901 | private static long nextThreadFuncNum = 0; | ||
1902 | private static long numQueuedThreadFuncs = 0; | ||
1903 | private static long numRunningThreadFuncs = 0; | ||
1904 | |||
1890 | public static void FireAndForget(System.Threading.WaitCallback callback, object obj) | 1905 | public static void FireAndForget(System.Threading.WaitCallback callback, object obj) |
1891 | { | 1906 | { |
1892 | WaitCallback realCallback; | 1907 | WaitCallback realCallback; |
1893 | 1908 | ||
1909 | long threadFuncNum = Interlocked.Increment(ref nextThreadFuncNum); | ||
1910 | |||
1894 | if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest) | 1911 | if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest) |
1895 | { | 1912 | { |
1896 | // If we're running regression tests, then we want any exceptions to rise up to the test code. | 1913 | // If we're running regression tests, then we want any exceptions to rise up to the test code. |
@@ -1903,49 +1920,117 @@ namespace OpenSim.Framework | |||
1903 | // for decimals places but is read by a culture that treats commas as number seperators. | 1920 | // for decimals places but is read by a culture that treats commas as number seperators. |
1904 | realCallback = o => | 1921 | realCallback = o => |
1905 | { | 1922 | { |
1906 | Culture.SetCurrentCulture(); | 1923 | long numQueued1 = Interlocked.Decrement(ref numQueuedThreadFuncs); |
1924 | long numRunning1 = Interlocked.Increment(ref numRunningThreadFuncs); | ||
1907 | 1925 | ||
1908 | try | 1926 | try |
1909 | { | 1927 | { |
1928 | if (LogThreadPool) | ||
1929 | m_log.DebugFormat("Run threadfunc {0} (Queued {1}, Running {2})", threadFuncNum, numQueued1, numRunning1); | ||
1930 | |||
1931 | Culture.SetCurrentCulture(); | ||
1932 | |||
1910 | callback(o); | 1933 | callback(o); |
1911 | } | 1934 | } |
1912 | catch (Exception e) | 1935 | catch (Exception e) |
1913 | { | 1936 | { |
1914 | m_log.ErrorFormat( | 1937 | m_log.Error("[UTIL]: FireAndForget thread terminated with error ", e); |
1915 | "[UTIL]: Continuing after async_call_method thread terminated with exception {0}{1}", | 1938 | } |
1916 | e.Message, e.StackTrace); | 1939 | finally |
1940 | { | ||
1941 | Interlocked.Decrement(ref numRunningThreadFuncs); | ||
1942 | if (LogThreadPool) | ||
1943 | m_log.Debug("Exit threadfunc " + threadFuncNum); | ||
1917 | } | 1944 | } |
1918 | }; | 1945 | }; |
1919 | } | 1946 | } |
1920 | 1947 | ||
1921 | switch (FireAndForgetMethod) | 1948 | long numQueued = Interlocked.Increment(ref numQueuedThreadFuncs); |
1949 | try | ||
1922 | { | 1950 | { |
1923 | case FireAndForgetMethod.RegressionTest: | 1951 | if (LogThreadPool) |
1924 | case FireAndForgetMethod.None: | 1952 | m_log.DebugFormat("Queue threadfunc {0} (Queued {1}, Running {2}) {3}", |
1925 | realCallback.Invoke(obj); | 1953 | threadFuncNum, numQueued, numRunningThreadFuncs, GetFireAndForgetStackTrace(true)); |
1926 | break; | 1954 | |
1927 | case FireAndForgetMethod.UnsafeQueueUserWorkItem: | 1955 | switch (FireAndForgetMethod) |
1928 | ThreadPool.UnsafeQueueUserWorkItem(realCallback, obj); | 1956 | { |
1929 | break; | 1957 | case FireAndForgetMethod.RegressionTest: |
1930 | case FireAndForgetMethod.QueueUserWorkItem: | 1958 | case FireAndForgetMethod.None: |
1931 | ThreadPool.QueueUserWorkItem(realCallback, obj); | 1959 | realCallback.Invoke(obj); |
1932 | break; | 1960 | break; |
1933 | case FireAndForgetMethod.BeginInvoke: | 1961 | case FireAndForgetMethod.UnsafeQueueUserWorkItem: |
1934 | FireAndForgetWrapper wrapper = FireAndForgetWrapper.Instance; | 1962 | ThreadPool.UnsafeQueueUserWorkItem(realCallback, obj); |
1935 | wrapper.FireAndForget(realCallback, obj); | 1963 | break; |
1936 | break; | 1964 | case FireAndForgetMethod.QueueUserWorkItem: |
1937 | case FireAndForgetMethod.SmartThreadPool: | 1965 | ThreadPool.QueueUserWorkItem(realCallback, obj); |
1938 | if (m_ThreadPool == null) | 1966 | break; |
1939 | InitThreadPool(2, 15); | 1967 | case FireAndForgetMethod.BeginInvoke: |
1940 | m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj); | 1968 | FireAndForgetWrapper wrapper = FireAndForgetWrapper.Instance; |
1941 | break; | 1969 | wrapper.FireAndForget(realCallback, obj); |
1942 | case FireAndForgetMethod.Thread: | 1970 | break; |
1943 | Thread thread = new Thread(delegate(object o) { realCallback(o); }); | 1971 | case FireAndForgetMethod.SmartThreadPool: |
1944 | thread.Start(obj); | 1972 | if (m_ThreadPool == null) |
1973 | InitThreadPool(2, 15); | ||
1974 | m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj); | ||
1975 | break; | ||
1976 | case FireAndForgetMethod.Thread: | ||
1977 | Thread thread = new Thread(delegate(object o) { realCallback(o); }); | ||
1978 | thread.Start(obj); | ||
1979 | break; | ||
1980 | default: | ||
1981 | throw new NotImplementedException(); | ||
1982 | } | ||
1983 | } | ||
1984 | catch (Exception) | ||
1985 | { | ||
1986 | Interlocked.Decrement(ref numQueuedThreadFuncs); | ||
1987 | throw; | ||
1988 | } | ||
1989 | } | ||
1990 | |||
1991 | /// <summary> | ||
1992 | /// Returns a stack trace for a thread added using FireAndForget(). | ||
1993 | /// </summary> | ||
1994 | /// <param name="full">True: return full stack trace; False: return only the first frame</param> | ||
1995 | private static string GetFireAndForgetStackTrace(bool full) | ||
1996 | { | ||
1997 | string src = Environment.StackTrace; | ||
1998 | string[] lines = src.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); | ||
1999 | |||
2000 | StringBuilder dest = new StringBuilder(src.Length); | ||
2001 | |||
2002 | bool started = false; | ||
2003 | bool first = true; | ||
2004 | |||
2005 | for (int i = 0; i < lines.Length; i++) | ||
2006 | { | ||
2007 | string line = lines[i]; | ||
2008 | |||
2009 | if (!started) | ||
2010 | { | ||
2011 | // Skip the initial stack frames, because they're of no interest for debugging | ||
2012 | if (line.Contains("StackTrace") || line.Contains("FireAndForget")) | ||
2013 | continue; | ||
2014 | started = true; | ||
2015 | } | ||
2016 | |||
2017 | if (first) | ||
2018 | { | ||
2019 | line = line.TrimStart(); | ||
2020 | first = false; | ||
2021 | } | ||
2022 | |||
2023 | bool last = (i == lines.Length - 1) || !full; | ||
2024 | if (last) | ||
2025 | dest.Append(line); | ||
2026 | else | ||
2027 | dest.AppendLine(line); | ||
2028 | |||
2029 | if (!full) | ||
1945 | break; | 2030 | break; |
1946 | default: | ||
1947 | throw new NotImplementedException(); | ||
1948 | } | 2031 | } |
2032 | |||
2033 | return dest.ToString(); | ||
1949 | } | 2034 | } |
1950 | 2035 | ||
1951 | /// <summary> | 2036 | /// <summary> |