aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework')
-rw-r--r--OpenSim/Framework/ClientInfo.cs4
-rw-r--r--OpenSim/Framework/Communications/RestClient.cs2
-rw-r--r--OpenSim/Framework/Monitoring/JobEngine.cs320
-rw-r--r--OpenSim/Framework/Monitoring/Watchdog.cs96
-rw-r--r--OpenSim/Framework/Monitoring/WorkManager.cs212
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs4
-rw-r--r--OpenSim/Framework/Servers/ServerBase.cs64
-rw-r--r--OpenSim/Framework/ServiceAuth/BasicHttpAuthentication.cs4
-rw-r--r--OpenSim/Framework/Tests/AnimationTests.cs1
-rw-r--r--OpenSim/Framework/Util.cs63
10 files changed, 669 insertions, 101 deletions
diff --git a/OpenSim/Framework/ClientInfo.cs b/OpenSim/Framework/ClientInfo.cs
index d68078e..98e4465 100644
--- a/OpenSim/Framework/ClientInfo.cs
+++ b/OpenSim/Framework/ClientInfo.cs
@@ -54,6 +54,10 @@ namespace OpenSim.Framework
54 public int assetThrottle; 54 public int assetThrottle;
55 public int textureThrottle; 55 public int textureThrottle;
56 public int totalThrottle; 56 public int totalThrottle;
57
58 // Used by adaptive only
59 public int targetThrottle;
60
57 public int maxThrottle; 61 public int maxThrottle;
58 62
59 public Dictionary<string, int> SyncRequests = new Dictionary<string,int>(); 63 public Dictionary<string, int> SyncRequests = new Dictionary<string,int>();
diff --git a/OpenSim/Framework/Communications/RestClient.cs b/OpenSim/Framework/Communications/RestClient.cs
index de6fe30..72018e4 100644
--- a/OpenSim/Framework/Communications/RestClient.cs
+++ b/OpenSim/Framework/Communications/RestClient.cs
@@ -483,7 +483,7 @@ namespace OpenSim.Framework.Communications
483 /// In case, we are invoked asynchroneously this object will keep track of the state 483 /// In case, we are invoked asynchroneously this object will keep track of the state
484 /// </summary> 484 /// </summary>
485 AsyncResult<Stream> ar = new AsyncResult<Stream>(callback, state); 485 AsyncResult<Stream> ar = new AsyncResult<Stream>(callback, state);
486 Util.FireAndForget(RequestHelper, ar); 486 Util.FireAndForget(RequestHelper, ar, "RestClient.BeginRequest");
487 return ar; 487 return ar;
488 } 488 }
489 489
diff --git a/OpenSim/Framework/Monitoring/JobEngine.cs b/OpenSim/Framework/Monitoring/JobEngine.cs
new file mode 100644
index 0000000..5925867
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/JobEngine.cs
@@ -0,0 +1,320 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Concurrent;
30using System.Reflection;
31using System.Threading;
32using log4net;
33using OpenSim.Framework;
34
35namespace OpenSim.Framework.Monitoring
36{
37 public class Job
38 {
39 public string Name;
40 public WaitCallback Callback;
41 public object O;
42
43 public Job(string name, WaitCallback callback, object o)
44 {
45 Name = name;
46 Callback = callback;
47 O = o;
48 }
49 }
50
51 public class JobEngine
52 {
53 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
54
55 public int LogLevel { get; set; }
56
57 public bool IsRunning { get; private set; }
58
59 /// <summary>
60 /// The timeout in milliseconds to wait for at least one event to be written when the recorder is stopping.
61 /// </summary>
62 public int RequestProcessTimeoutOnStop { get; set; }
63
64 /// <summary>
65 /// Controls whether we need to warn in the log about exceeding the max queue size.
66 /// </summary>
67 /// <remarks>
68 /// This is flipped to false once queue max has been exceeded and back to true when it falls below max, in
69 /// order to avoid spamming the log with lots of warnings.
70 /// </remarks>
71 private bool m_warnOverMaxQueue = true;
72
73 private BlockingCollection<Job> m_requestQueue;
74
75 private CancellationTokenSource m_cancelSource = new CancellationTokenSource();
76
77 private Stat m_requestsWaitingStat;
78
79 private Job m_currentJob;
80
81 /// <summary>
82 /// Used to signal that we are ready to complete stop.
83 /// </summary>
84 private ManualResetEvent m_finishedProcessingAfterStop = new ManualResetEvent(false);
85
86 public JobEngine()
87 {
88 RequestProcessTimeoutOnStop = 5000;
89
90 MainConsole.Instance.Commands.AddCommand(
91 "Debug",
92 false,
93 "debug jobengine",
94 "debug jobengine <start|stop|status|log>",
95 "Start, stop, get status or set logging level of the job engine.",
96 "If stopped then all outstanding jobs are processed immediately.",
97 HandleControlCommand);
98 }
99
100 public void Start()
101 {
102 lock (this)
103 {
104 if (IsRunning)
105 return;
106
107 IsRunning = true;
108
109 m_finishedProcessingAfterStop.Reset();
110
111 m_requestQueue = new BlockingCollection<Job>(new ConcurrentQueue<Job>(), 5000);
112
113 m_requestsWaitingStat =
114 new Stat(
115 "JobsWaiting",
116 "Number of jobs waiting for processing.",
117 "",
118 "",
119 "server",
120 "jobengine",
121 StatType.Pull,
122 MeasuresOfInterest.None,
123 stat => stat.Value = m_requestQueue.Count,
124 StatVerbosity.Debug);
125
126 StatsManager.RegisterStat(m_requestsWaitingStat);
127
128 WorkManager.StartThread(
129 ProcessRequests,
130 "JobEngineThread",
131 ThreadPriority.Normal,
132 false,
133 true,
134 null,
135 int.MaxValue);
136 }
137 }
138
139 public void Stop()
140 {
141 lock (this)
142 {
143 try
144 {
145 if (!IsRunning)
146 return;
147
148 IsRunning = false;
149
150 int requestsLeft = m_requestQueue.Count;
151
152 if (requestsLeft <= 0)
153 {
154 m_cancelSource.Cancel();
155 }
156 else
157 {
158 m_log.InfoFormat("[JOB ENGINE]: Waiting to write {0} events after stop.", requestsLeft);
159
160 while (requestsLeft > 0)
161 {
162 if (!m_finishedProcessingAfterStop.WaitOne(RequestProcessTimeoutOnStop))
163 {
164 // After timeout no events have been written
165 if (requestsLeft == m_requestQueue.Count)
166 {
167 m_log.WarnFormat(
168 "[JOB ENGINE]: No requests processed after {0} ms wait. Discarding remaining {1} requests",
169 RequestProcessTimeoutOnStop, requestsLeft);
170
171 break;
172 }
173 }
174
175 requestsLeft = m_requestQueue.Count;
176 }
177 }
178 }
179 finally
180 {
181 m_cancelSource.Dispose();
182 StatsManager.DeregisterStat(m_requestsWaitingStat);
183 m_requestsWaitingStat = null;
184 m_requestQueue = null;
185 }
186 }
187 }
188
189 public bool QueueRequest(string name, WaitCallback req, object o)
190 {
191 if (LogLevel >= 1)
192 m_log.DebugFormat("[JOB ENGINE]: Queued job {0}", name);
193
194 if (m_requestQueue.Count < m_requestQueue.BoundedCapacity)
195 {
196 // m_log.DebugFormat(
197 // "[OUTGOING QUEUE REFILL ENGINE]: Adding request for categories {0} for {1} in {2}",
198 // categories, client.AgentID, m_udpServer.Scene.Name);
199
200 m_requestQueue.Add(new Job(name, req, o));
201
202 if (!m_warnOverMaxQueue)
203 m_warnOverMaxQueue = true;
204
205 return true;
206 }
207 else
208 {
209 if (m_warnOverMaxQueue)
210 {
211// m_log.WarnFormat(
212// "[JOB ENGINE]: Request queue at maximum capacity, not recording request from {0} in {1}",
213// client.AgentID, m_udpServer.Scene.Name);
214
215 m_log.WarnFormat("[JOB ENGINE]: Request queue at maximum capacity, not recording job");
216
217 m_warnOverMaxQueue = false;
218 }
219
220 return false;
221 }
222 }
223
224 private void ProcessRequests()
225 {
226 try
227 {
228 while (IsRunning || m_requestQueue.Count > 0)
229 {
230 m_currentJob = m_requestQueue.Take(m_cancelSource.Token);
231
232 // QueueEmpty callback = req.Client.OnQueueEmpty;
233 //
234 // if (callback != null)
235 // {
236 // try
237 // {
238 // callback(req.Categories);
239 // }
240 // catch (Exception e)
241 // {
242 // m_log.Error("[OUTGOING QUEUE REFILL ENGINE]: ProcessRequests(" + req.Categories + ") threw an exception: " + e.Message, e);
243 // }
244 // }
245
246 if (LogLevel >= 1)
247 m_log.DebugFormat("[JOB ENGINE]: Processing job {0}", m_currentJob.Name);
248
249 try
250 {
251 m_currentJob.Callback.Invoke(m_currentJob.O);
252 }
253 catch (Exception e)
254 {
255 m_log.Error(
256 string.Format(
257 "[JOB ENGINE]: Job {0} failed, continuing. Exception ", m_currentJob.Name), e);
258 }
259
260 if (LogLevel >= 1)
261 m_log.DebugFormat("[JOB ENGINE]: Processed job {0}", m_currentJob.Name);
262
263 m_currentJob = null;
264 }
265 }
266 catch (OperationCanceledException)
267 {
268 }
269
270 m_finishedProcessingAfterStop.Set();
271 }
272
273 private void HandleControlCommand(string module, string[] args)
274 {
275// if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene)
276// return;
277
278 if (args.Length < 3)
279 {
280 MainConsole.Instance.Output("Usage: debug jobengine <stop|start|status|log>");
281 return;
282 }
283
284 string subCommand = args[2];
285
286 if (subCommand == "stop")
287 {
288 Stop();
289 MainConsole.Instance.OutputFormat("Stopped job engine.");
290 }
291 else if (subCommand == "start")
292 {
293 Start();
294 MainConsole.Instance.OutputFormat("Started job engine.");
295 }
296 else if (subCommand == "status")
297 {
298 MainConsole.Instance.OutputFormat("Job engine running: {0}", IsRunning);
299 MainConsole.Instance.OutputFormat("Current job {0}", m_currentJob != null ? m_currentJob.Name : "none");
300 MainConsole.Instance.OutputFormat(
301 "Jobs waiting: {0}", IsRunning ? m_requestQueue.Count.ToString() : "n/a");
302 MainConsole.Instance.OutputFormat("Log Level: {0}", LogLevel);
303 }
304 else if (subCommand == "log")
305 {
306// int logLevel;
307 int logLevel = int.Parse(args[3]);
308// if (ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out logLevel))
309// {
310 LogLevel = logLevel;
311 MainConsole.Instance.OutputFormat("Set debug log level to {0}", LogLevel);
312// }
313 }
314 else
315 {
316 MainConsole.Instance.OutputFormat("Unrecognized job engine subcommand {0}", subCommand);
317 }
318 }
319 }
320}
diff --git a/OpenSim/Framework/Monitoring/Watchdog.cs b/OpenSim/Framework/Monitoring/Watchdog.cs
index 0fcb195..a644fa5 100644
--- a/OpenSim/Framework/Monitoring/Watchdog.cs
+++ b/OpenSim/Framework/Monitoring/Watchdog.cs
@@ -38,6 +38,8 @@ namespace OpenSim.Framework.Monitoring
38 /// </summary> 38 /// </summary>
39 public static class Watchdog 39 public static class Watchdog
40 { 40 {
41 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
42
41 /// <summary>Timer interval in milliseconds for the watchdog timer</summary> 43 /// <summary>Timer interval in milliseconds for the watchdog timer</summary>
42 public const double WATCHDOG_INTERVAL_MS = 2500.0d; 44 public const double WATCHDOG_INTERVAL_MS = 2500.0d;
43 45
@@ -141,7 +143,7 @@ namespace OpenSim.Framework.Monitoring
141 get { return m_enabled; } 143 get { return m_enabled; }
142 set 144 set
143 { 145 {
144// m_log.DebugFormat("[MEMORY WATCHDOG]: Setting MemoryWatchdog.Enabled to {0}", value); 146 // m_log.DebugFormat("[MEMORY WATCHDOG]: Setting MemoryWatchdog.Enabled to {0}", value);
145 147
146 if (value == m_enabled) 148 if (value == m_enabled)
147 return; 149 return;
@@ -157,9 +159,8 @@ namespace OpenSim.Framework.Monitoring
157 m_watchdogTimer.Enabled = m_enabled; 159 m_watchdogTimer.Enabled = m_enabled;
158 } 160 }
159 } 161 }
160 private static bool m_enabled;
161 162
162 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 163 private static bool m_enabled;
163 private static Dictionary<int, ThreadWatchdogInfo> m_threads; 164 private static Dictionary<int, ThreadWatchdogInfo> m_threads;
164 private static System.Timers.Timer m_watchdogTimer; 165 private static System.Timers.Timer m_watchdogTimer;
165 166
@@ -180,94 +181,19 @@ namespace OpenSim.Framework.Monitoring
180 } 181 }
181 182
182 /// <summary> 183 /// <summary>
183 /// Start a new thread that is tracked by the watchdog timer. 184 /// Add a thread to the watchdog tracker.
184 /// </summary> 185 /// </summary>
185 /// <param name="start">The method that will be executed in a new thread</param> 186 /// <param name="info">Information about the thread.</info>
186 /// <param name="name">A name to give to the new thread</param> 187 /// <param name="info">Name of the thread.</info>
187 /// <param name="priority">Priority to run the thread at</param>
188 /// <param name="isBackground">True to run this thread as a background thread, otherwise false</param>
189 /// <param name="alarmIfTimeout">Trigger an alarm function is we have timed out</param>
190 /// <param name="log">If true then creation of thread is logged.</param> 188 /// <param name="log">If true then creation of thread is logged.</param>
191 /// <returns>The newly created Thread object</returns> 189 public static void AddThread(ThreadWatchdogInfo info, string name, bool log = true)
192 public static Thread StartThread(
193 ThreadStart start, string name, ThreadPriority priority, bool isBackground, bool alarmIfTimeout, bool log = true)
194 { 190 {
195 return StartThread(start, name, priority, isBackground, alarmIfTimeout, null, DEFAULT_WATCHDOG_TIMEOUT_MS, log);
196 }
197
198 /// <summary>
199 /// Start a new thread that is tracked by the watchdog
200 /// </summary>
201 /// <param name="start">The method that will be executed in a new thread</param>
202 /// <param name="name">A name to give to the new thread</param>
203 /// <param name="priority">Priority to run the thread at</param>
204 /// <param name="isBackground">True to run this thread as a background
205 /// thread, otherwise false</param>
206 /// <param name="alarmIfTimeout">Trigger an alarm function is we have timed out</param>
207 /// <param name="alarmMethod">
208 /// Alarm method to call if alarmIfTimeout is true and there is a timeout.
209 /// Normally, this will just return some useful debugging information.
210 /// </param>
211 /// <param name="timeout">Number of milliseconds to wait until we issue a warning about timeout.</param>
212 /// <param name="log">If true then creation of thread is logged.</param>
213 /// <returns>The newly created Thread object</returns>
214 public static Thread StartThread(
215 ThreadStart start, string name, ThreadPriority priority, bool isBackground,
216 bool alarmIfTimeout, Func<string> alarmMethod, int timeout, bool log = true)
217 {
218 Thread thread = new Thread(start);
219 thread.Priority = priority;
220 thread.IsBackground = isBackground;
221
222 ThreadWatchdogInfo twi
223 = new ThreadWatchdogInfo(thread, timeout, name)
224 { AlarmIfTimeout = alarmIfTimeout, AlarmMethod = alarmMethod };
225
226 if (log) 191 if (log)
227 m_log.DebugFormat( 192 m_log.DebugFormat(
228 "[WATCHDOG]: Started tracking thread {0}, ID {1}", name, twi.Thread.ManagedThreadId); 193 "[WATCHDOG]: Started tracking thread {0}, ID {1}", name, info.Thread.ManagedThreadId);
229 194
230 lock (m_threads) 195 lock (m_threads)
231 m_threads.Add(twi.Thread.ManagedThreadId, twi); 196 m_threads.Add(info.Thread.ManagedThreadId, info);
232
233 thread.Start();
234 thread.Name = name;
235
236
237 return thread;
238 }
239
240 /// <summary>
241 /// Run the callback in a new thread immediately. If the thread exits with an exception log it but do
242 /// not propogate it.
243 /// </summary>
244 /// <param name="callback">Code for the thread to execute.</param>
245 /// <param name="name">Name of the thread</param>
246 /// <param name="obj">Object to pass to the thread.</param>
247 public static void RunInThread(WaitCallback callback, string name, object obj, bool log = false)
248 {
249 if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
250 {
251 Culture.SetCurrentCulture();
252 callback(obj);
253 return;
254 }
255
256 ThreadStart ts = new ThreadStart(delegate()
257 {
258 try
259 {
260 Culture.SetCurrentCulture();
261 callback(obj);
262 Watchdog.RemoveThread(log:false);
263 }
264 catch (Exception e)
265 {
266 m_log.Error(string.Format("[WATCHDOG]: Exception in thread {0}.", name), e);
267 }
268 });
269
270 StartThread(ts, name, ThreadPriority.Normal, true, false, log:log);
271 } 197 }
272 198
273 /// <summary> 199 /// <summary>
@@ -358,7 +284,7 @@ namespace OpenSim.Framework.Monitoring
358 } 284 }
359 catch { } 285 catch { }
360 } 286 }
361 287
362 /// <summary> 288 /// <summary>
363 /// Get currently watched threads for diagnostic purposes 289 /// Get currently watched threads for diagnostic purposes
364 /// </summary> 290 /// </summary>
diff --git a/OpenSim/Framework/Monitoring/WorkManager.cs b/OpenSim/Framework/Monitoring/WorkManager.cs
new file mode 100644
index 0000000..9d0eefc
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/WorkManager.cs
@@ -0,0 +1,212 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Reflection;
30using System.Threading;
31using log4net;
32
33namespace OpenSim.Framework.Monitoring
34{
35 /// <summary>
36 /// Manages various work items in the simulator.
37 /// </summary>
38 /// <remarks>
39 /// Currently, here work can be started
40 /// * As a long-running and monitored thread.
41 /// * In a thread that will never timeout but where the job is expected to eventually complete.
42 /// * In a threadpool thread that will timeout if it takes a very long time to complete (> 10 mins).
43 /// * As a job which will be run in a single-threaded job engine. Such jobs must not incorporate delays (sleeps,
44 /// network waits, etc.).
45 ///
46 /// This is an evolving approach to better manage the work that OpenSimulator is asked to do from a very diverse
47 /// range of sources (client actions, incoming network, outgoing network calls, etc.).
48 ///
49 /// Util.FireAndForget is still available to insert jobs in the threadpool, though this is equivalent to
50 /// WorkManager.RunInThreadPool().
51 /// </remarks>
52 public static class WorkManager
53 {
54 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
55
56 public static JobEngine JobEngine { get; private set; }
57
58 static WorkManager()
59 {
60 JobEngine = new JobEngine();
61 }
62
63 /// <summary>
64 /// Start a new long-lived thread.
65 /// </summary>
66 /// <param name="start">The method that will be executed in a new thread</param>
67 /// <param name="name">A name to give to the new thread</param>
68 /// <param name="priority">Priority to run the thread at</param>
69 /// <param name="isBackground">True to run this thread as a background thread, otherwise false</param>
70 /// <param name="alarmIfTimeout">Trigger an alarm function is we have timed out</param>
71 /// <param name="log">If true then creation of thread is logged.</param>
72 /// <returns>The newly created Thread object</returns>
73 public static Thread StartThread(
74 ThreadStart start, string name, ThreadPriority priority, bool isBackground, bool alarmIfTimeout, bool log = true)
75 {
76 return StartThread(start, name, priority, isBackground, alarmIfTimeout, null, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS, log);
77 }
78
79 /// <summary>
80 /// Start a new thread that is tracked by the watchdog
81 /// </summary>
82 /// <param name="start">The method that will be executed in a new thread</param>
83 /// <param name="name">A name to give to the new thread</param>
84 /// <param name="priority">Priority to run the thread at</param>
85 /// <param name="isBackground">True to run this thread as a background
86 /// thread, otherwise false</param>
87 /// <param name="alarmIfTimeout">Trigger an alarm function is we have timed out</param>
88 /// <param name="alarmMethod">
89 /// Alarm method to call if alarmIfTimeout is true and there is a timeout.
90 /// Normally, this will just return some useful debugging information.
91 /// </param>
92 /// <param name="timeout">Number of milliseconds to wait until we issue a warning about timeout.</param>
93 /// <param name="log">If true then creation of thread is logged.</param>
94 /// <returns>The newly created Thread object</returns>
95 public static Thread StartThread(
96 ThreadStart start, string name, ThreadPriority priority, bool isBackground,
97 bool alarmIfTimeout, Func<string> alarmMethod, int timeout, bool log = true)
98 {
99 Thread thread = new Thread(start);
100 thread.Priority = priority;
101 thread.IsBackground = isBackground;
102
103 Watchdog.ThreadWatchdogInfo twi
104 = new Watchdog.ThreadWatchdogInfo(thread, timeout, name)
105 { AlarmIfTimeout = alarmIfTimeout, AlarmMethod = alarmMethod };
106
107 Watchdog.AddThread(twi, name, log:log);
108
109 thread.Start();
110 thread.Name = name;
111
112 return thread;
113 }
114
115 /// <summary>
116 /// Run the callback in a new thread immediately. If the thread exits with an exception log it but do
117 /// not propogate it.
118 /// </summary>
119 /// <param name="callback">Code for the thread to execute.</param>
120 /// <param name="obj">Object to pass to the thread.</param>
121 /// <param name="name">Name of the thread</param>
122 public static void RunInThread(WaitCallback callback, object obj, string name, bool log = false)
123 {
124 if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
125 {
126 Culture.SetCurrentCulture();
127 callback(obj);
128 return;
129 }
130
131 ThreadStart ts = new ThreadStart(delegate()
132 {
133 try
134 {
135 Culture.SetCurrentCulture();
136 callback(obj);
137 Watchdog.RemoveThread(log:false);
138 }
139 catch (Exception e)
140 {
141 m_log.Error(string.Format("[WATCHDOG]: Exception in thread {0}.", name), e);
142 }
143 });
144
145 StartThread(ts, name, ThreadPriority.Normal, true, false, log:log);
146 }
147
148 /// <summary>
149 /// Run the callback via a threadpool thread.
150 /// </summary>
151 /// <remarks>
152 /// Such jobs may run after some delay but must always complete.
153 /// </remarks>
154 /// <param name="callback"></param>
155 /// <param name="obj"></param>
156 /// <param name="name">The name of the job. This is used in monitoring and debugging.</param>
157 public static void RunInThreadPool(System.Threading.WaitCallback callback, object obj, string name)
158 {
159 Util.FireAndForget(callback, obj, name);
160 }
161
162 /// <summary>
163 /// Run a job.
164 /// </summary>
165 /// <remarks>
166 /// This differs from direct scheduling (e.g. Util.FireAndForget) in that a job can be run in the job
167 /// engine if it is running, where all jobs are currently performed in sequence on a single thread. This is
168 /// to prevent observed overload and server freeze problems when there are hundreds of connections which all attempt to
169 /// perform work at once (e.g. in conference situations). With lower numbers of connections, the small
170 /// delay in performing jobs in sequence rather than concurrently has not been notiecable in testing, though a future more
171 /// sophisticated implementation could perform jobs concurrently when the server is under low load.
172 ///
173 /// However, be advised that some callers of this function rely on all jobs being performed in sequence if any
174 /// jobs are performed in sequence (i.e. if jobengine is active or not). Therefore, expanding the jobengine
175 /// beyond a single thread will require considerable thought.
176 ///
177 /// Also, any jobs submitted must be guaranteed to complete within a reasonable timeframe (e.g. they cannot
178 /// incorporate a network delay with a long timeout). At the moment, work that could suffer such issues
179 /// should still be run directly with RunInThread(), Util.FireAndForget(), etc. This is another area where
180 /// the job engine could be improved and so CPU utilization improved by better management of concurrency within
181 /// OpenSimulator.
182 /// </remarks>
183 /// <param name="jobType">General classification for the job (e.g. "RezAttachments").</param>
184 /// <param name="callback">Callback for job.</param>
185 /// <param name="obj">Object to pass to callback when run</param>
186 /// <param name="name">Specific name of job (e.g. "RezAttachments for Joe Bloggs"</param>
187 /// <param name="canRunInThisThread">If set to true then the job may be run in ths calling thread.</param>
188 /// <param name="mustNotTimeout">If the true then the job must never timeout.</param>
189 /// <param name="log">If set to true then extra logging is performed.</param>
190 public static void RunJob(
191 string jobType, WaitCallback callback, object obj, string name,
192 bool canRunInThisThread = false, bool mustNotTimeout = false,
193 bool log = false)
194 {
195 if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
196 {
197 Culture.SetCurrentCulture();
198 callback(obj);
199 return;
200 }
201
202 if (JobEngine.IsRunning)
203 JobEngine.QueueRequest(name, callback, obj);
204 else if (canRunInThisThread)
205 callback(obj);
206 else if (mustNotTimeout)
207 RunInThread(callback, obj, name, log);
208 else
209 Util.FireAndForget(callback, obj, name);
210 }
211 }
212} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
index 4561d23..28bba70 100644
--- a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
@@ -120,7 +120,7 @@ namespace OpenSim.Framework.Servers.HttpServer
120 for (uint i = 0; i < m_WorkerThreadCount; i++) 120 for (uint i = 0; i < m_WorkerThreadCount; i++)
121 { 121 {
122 m_workerThreads[i] 122 m_workerThreads[i]
123 = Watchdog.StartThread( 123 = WorkManager.StartThread(
124 PoolWorkerJob, 124 PoolWorkerJob,
125 string.Format("PollServiceWorkerThread{0}:{1}", i, m_server.Port), 125 string.Format("PollServiceWorkerThread{0}:{1}", i, m_server.Port),
126 ThreadPriority.Normal, 126 ThreadPriority.Normal,
@@ -130,7 +130,7 @@ namespace OpenSim.Framework.Servers.HttpServer
130 int.MaxValue); 130 int.MaxValue);
131 } 131 }
132 132
133 Watchdog.StartThread( 133 WorkManager.StartThread(
134 this.CheckLongPollThreads, 134 this.CheckLongPollThreads,
135 string.Format("LongPollServiceWatcherThread:{0}", m_server.Port), 135 string.Format("LongPollServiceWatcherThread:{0}", m_server.Port),
136 ThreadPriority.Normal, 136 ThreadPriority.Normal,
diff --git a/OpenSim/Framework/Servers/ServerBase.cs b/OpenSim/Framework/Servers/ServerBase.cs
index fd56587..c22c119 100644
--- a/OpenSim/Framework/Servers/ServerBase.cs
+++ b/OpenSim/Framework/Servers/ServerBase.cs
@@ -29,6 +29,7 @@ using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Diagnostics; 30using System.Diagnostics;
31using System.IO; 31using System.IO;
32using System.Linq;
32using System.Reflection; 33using System.Reflection;
33using System.Text; 34using System.Text;
34using System.Text.RegularExpressions; 35using System.Text.RegularExpressions;
@@ -292,6 +293,18 @@ namespace OpenSim.Framework.Servers
292 HandleDebugThreadpoolLevel); 293 HandleDebugThreadpoolLevel);
293 294
294 m_console.Commands.AddCommand( 295 m_console.Commands.AddCommand(
296 "Debug", false, "show threadpool calls active",
297 "show threadpool calls active",
298 "Show details about threadpool calls that are still active (currently waiting or in progress)",
299 HandleShowThreadpoolCallsActive);
300
301 m_console.Commands.AddCommand(
302 "Debug", false, "show threadpool calls complete",
303 "show threadpool calls complete",
304 "Show details about threadpool calls that have been completed.",
305 HandleShowThreadpoolCallsComplete);
306
307 m_console.Commands.AddCommand(
295 "Debug", false, "force gc", 308 "Debug", false, "force gc",
296 "force gc", 309 "force gc",
297 "Manually invoke runtime garbage collection. For debugging purposes", 310 "Manually invoke runtime garbage collection. For debugging purposes",
@@ -354,6 +367,57 @@ namespace OpenSim.Framework.Servers
354 Notice("serialosdreq is now {0}", setSerializeOsdRequests); 367 Notice("serialosdreq is now {0}", setSerializeOsdRequests);
355 } 368 }
356 369
370 private void HandleShowThreadpoolCallsActive(string module, string[] args)
371 {
372 List<KeyValuePair<string, int>> calls = Util.GetFireAndForgetCallsInProgress().ToList();
373 calls.Sort((kvp1, kvp2) => kvp2.Value.CompareTo(kvp1.Value));
374 int namedCalls = 0;
375
376 ConsoleDisplayList cdl = new ConsoleDisplayList();
377 foreach (KeyValuePair<string, int> kvp in calls)
378 {
379 if (kvp.Value > 0)
380 {
381 cdl.AddRow(kvp.Key, kvp.Value);
382 namedCalls += kvp.Value;
383 }
384 }
385
386 cdl.AddRow("TOTAL NAMED", namedCalls);
387
388 long allQueuedCalls = Util.TotalQueuedFireAndForgetCalls;
389 long allRunningCalls = Util.TotalRunningFireAndForgetCalls;
390
391 cdl.AddRow("TOTAL QUEUED", allQueuedCalls);
392 cdl.AddRow("TOTAL RUNNING", allRunningCalls);
393 cdl.AddRow("TOTAL ANONYMOUS", allQueuedCalls + allRunningCalls - namedCalls);
394 cdl.AddRow("TOTAL ALL", allQueuedCalls + allRunningCalls);
395
396 MainConsole.Instance.Output(cdl.ToString());
397 }
398
399 private void HandleShowThreadpoolCallsComplete(string module, string[] args)
400 {
401 List<KeyValuePair<string, int>> calls = Util.GetFireAndForgetCallsMade().ToList();
402 calls.Sort((kvp1, kvp2) => kvp2.Value.CompareTo(kvp1.Value));
403 int namedCallsMade = 0;
404
405 ConsoleDisplayList cdl = new ConsoleDisplayList();
406 foreach (KeyValuePair<string, int> kvp in calls)
407 {
408 cdl.AddRow(kvp.Key, kvp.Value);
409 namedCallsMade += kvp.Value;
410 }
411
412 cdl.AddRow("TOTAL NAMED", namedCallsMade);
413
414 long allCallsMade = Util.TotalFireAndForgetCallsMade;
415 cdl.AddRow("TOTAL ANONYMOUS", allCallsMade - namedCallsMade);
416 cdl.AddRow("TOTAL ALL", allCallsMade);
417
418 MainConsole.Instance.Output(cdl.ToString());
419 }
420
357 private void HandleDebugThreadpoolStatus(string module, string[] args) 421 private void HandleDebugThreadpoolStatus(string module, string[] args)
358 { 422 {
359 int workerThreads, iocpThreads; 423 int workerThreads, iocpThreads;
diff --git a/OpenSim/Framework/ServiceAuth/BasicHttpAuthentication.cs b/OpenSim/Framework/ServiceAuth/BasicHttpAuthentication.cs
index f33a045..d182a71 100644
--- a/OpenSim/Framework/ServiceAuth/BasicHttpAuthentication.cs
+++ b/OpenSim/Framework/ServiceAuth/BasicHttpAuthentication.cs
@@ -15,7 +15,7 @@ namespace OpenSim.Framework.ServiceAuth
15 private string m_Username, m_Password; 15 private string m_Username, m_Password;
16 private string m_CredentialsB64; 16 private string m_CredentialsB64;
17 17
18 private string remove_me; 18// private string remove_me;
19 19
20 public string Credentials 20 public string Credentials
21 { 21 {
@@ -24,7 +24,7 @@ namespace OpenSim.Framework.ServiceAuth
24 24
25 public BasicHttpAuthentication(IConfigSource config, string section) 25 public BasicHttpAuthentication(IConfigSource config, string section)
26 { 26 {
27 remove_me = section; 27// remove_me = section;
28 m_Username = Util.GetConfigVarFromSections<string>(config, "HttpAuthUsername", new string[] { "Network", section }, string.Empty); 28 m_Username = Util.GetConfigVarFromSections<string>(config, "HttpAuthUsername", new string[] { "Network", section }, string.Empty);
29 m_Password = Util.GetConfigVarFromSections<string>(config, "HttpAuthPassword", new string[] { "Network", section }, string.Empty); 29 m_Password = Util.GetConfigVarFromSections<string>(config, "HttpAuthPassword", new string[] { "Network", section }, string.Empty);
30 string str = m_Username + ":" + m_Password; 30 string str = m_Username + ":" + m_Password;
diff --git a/OpenSim/Framework/Tests/AnimationTests.cs b/OpenSim/Framework/Tests/AnimationTests.cs
index f3be81b..d8f17d0 100644
--- a/OpenSim/Framework/Tests/AnimationTests.cs
+++ b/OpenSim/Framework/Tests/AnimationTests.cs
@@ -32,7 +32,6 @@ using OpenMetaverse;
32using OpenMetaverse.StructuredData; 32using OpenMetaverse.StructuredData;
33using OpenSim.Framework; 33using OpenSim.Framework;
34using OpenSim.Tests.Common; 34using OpenSim.Tests.Common;
35using OpenSim.Tests.Common.Mock;
36using Animation = OpenSim.Framework.Animation; 35using Animation = OpenSim.Framework.Animation;
37 36
38namespace OpenSim.Framework.Tests 37namespace OpenSim.Framework.Tests
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index fefa050..97c958a 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -1928,11 +1928,6 @@ namespace OpenSim.Framework
1928 } 1928 }
1929 } 1929 }
1930 1930
1931 public static void FireAndForget(System.Threading.WaitCallback callback)
1932 {
1933 FireAndForget(callback, null, null);
1934 }
1935
1936 public static void InitThreadPool(int minThreads, int maxThreads) 1931 public static void InitThreadPool(int minThreads, int maxThreads)
1937 { 1932 {
1938 if (maxThreads < 2) 1933 if (maxThreads < 2)
@@ -1977,8 +1972,7 @@ namespace OpenSim.Framework
1977 throw new NotImplementedException(); 1972 throw new NotImplementedException();
1978 } 1973 }
1979 } 1974 }
1980 1975
1981
1982 /// <summary> 1976 /// <summary>
1983 /// Additional information about threads in the main thread pool. Used to time how long the 1977 /// Additional information about threads in the main thread pool. Used to time how long the
1984 /// thread has been running, and abort it if it has timed-out. 1978 /// thread has been running, and abort it if it has timed-out.
@@ -2052,12 +2046,15 @@ namespace OpenSim.Framework
2052 } 2046 }
2053 } 2047 }
2054 2048
2055
2056 private static long nextThreadFuncNum = 0; 2049 private static long nextThreadFuncNum = 0;
2057 private static long numQueuedThreadFuncs = 0; 2050 private static long numQueuedThreadFuncs = 0;
2058 private static long numRunningThreadFuncs = 0; 2051 private static long numRunningThreadFuncs = 0;
2052 private static long numTotalThreadFuncsCalled = 0;
2059 private static Int32 threadFuncOverloadMode = 0; 2053 private static Int32 threadFuncOverloadMode = 0;
2060 2054
2055 public static long TotalQueuedFireAndForgetCalls { get { return numQueuedThreadFuncs; } }
2056 public static long TotalRunningFireAndForgetCalls { get { return numRunningThreadFuncs; } }
2057
2061 // Maps (ThreadFunc number -> Thread) 2058 // Maps (ThreadFunc number -> Thread)
2062 private static ConcurrentDictionary<long, ThreadInfo> activeThreads = new ConcurrentDictionary<long, ThreadInfo>(); 2059 private static ConcurrentDictionary<long, ThreadInfo> activeThreads = new ConcurrentDictionary<long, ThreadInfo>();
2063 2060
@@ -2086,14 +2083,49 @@ namespace OpenSim.Framework
2086 } 2083 }
2087 } 2084 }
2088 2085
2086 public static long TotalFireAndForgetCallsMade { get { return numTotalThreadFuncsCalled; } }
2087
2088 public static Dictionary<string, int> GetFireAndForgetCallsMade()
2089 {
2090 return new Dictionary<string, int>(m_fireAndForgetCallsMade);
2091 }
2092
2093 private static Dictionary<string, int> m_fireAndForgetCallsMade = new Dictionary<string, int>();
2094
2095 public static Dictionary<string, int> GetFireAndForgetCallsInProgress()
2096 {
2097 return new Dictionary<string, int>(m_fireAndForgetCallsInProgress);
2098 }
2099
2100 private static Dictionary<string, int> m_fireAndForgetCallsInProgress = new Dictionary<string, int>();
2101
2102 public static void FireAndForget(System.Threading.WaitCallback callback)
2103 {
2104 FireAndForget(callback, null, null);
2105 }
2089 2106
2090 public static void FireAndForget(System.Threading.WaitCallback callback, object obj) 2107 public static void FireAndForget(System.Threading.WaitCallback callback, object obj)
2091 { 2108 {
2092 FireAndForget(callback, obj, null); 2109 FireAndForget(callback, obj, null);
2093 } 2110 }
2094 2111
2095 public static void FireAndForget(System.Threading.WaitCallback callback, object obj, string context) 2112 public static void FireAndForget(System.Threading.WaitCallback callback, object obj, string context)
2096 { 2113 {
2114 Interlocked.Increment(ref numTotalThreadFuncsCalled);
2115
2116 if (context != null)
2117 {
2118 if (!m_fireAndForgetCallsMade.ContainsKey(context))
2119 m_fireAndForgetCallsMade[context] = 1;
2120 else
2121 m_fireAndForgetCallsMade[context]++;
2122
2123 if (!m_fireAndForgetCallsInProgress.ContainsKey(context))
2124 m_fireAndForgetCallsInProgress[context] = 1;
2125 else
2126 m_fireAndForgetCallsInProgress[context]++;
2127 }
2128
2097 WaitCallback realCallback; 2129 WaitCallback realCallback;
2098 2130
2099 bool loggingEnabled = LogThreadPool > 0; 2131 bool loggingEnabled = LogThreadPool > 0;
@@ -2104,7 +2136,15 @@ namespace OpenSim.Framework
2104 if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest) 2136 if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
2105 { 2137 {
2106 // If we're running regression tests, then we want any exceptions to rise up to the test code. 2138 // If we're running regression tests, then we want any exceptions to rise up to the test code.
2107 realCallback = o => { Culture.SetCurrentCulture(); callback(o); }; 2139 realCallback =
2140 o =>
2141 {
2142 Culture.SetCurrentCulture();
2143 callback(o);
2144
2145 if (context != null)
2146 m_fireAndForgetCallsInProgress[context]--;
2147 };
2108 } 2148 }
2109 else 2149 else
2110 { 2150 {
@@ -2143,6 +2183,9 @@ namespace OpenSim.Framework
2143 activeThreads.TryRemove(threadFuncNum, out dummy); 2183 activeThreads.TryRemove(threadFuncNum, out dummy);
2144 if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread) 2184 if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread)
2145 m_log.DebugFormat("Exit threadfunc {0} ({1})", threadFuncNum, FormatDuration(threadInfo.Elapsed())); 2185 m_log.DebugFormat("Exit threadfunc {0} ({1})", threadFuncNum, FormatDuration(threadInfo.Elapsed()));
2186
2187 if (context != null)
2188 m_fireAndForgetCallsInProgress[context]--;
2146 } 2189 }
2147 }; 2190 };
2148 } 2191 }