diff --git a/OpenSim/Framework/Monitoring/Watchdog.cs b/OpenSim/Framework/Monitoring/Watchdog.cs
index 89ae11f12a..6f62c8437d 100644
--- a/OpenSim/Framework/Monitoring/Watchdog.cs
+++ b/OpenSim/Framework/Monitoring/Watchdog.cs
@@ -453,7 +453,38 @@ namespace OpenSim.Framework.Monitoring
m_watchdogTimer.Start();
}
- public static void RunWhenPossible(string jobType, WaitCallback callback, string name, object obj, bool log = false)
+ ///
+ /// Run a job.
+ ///
+ ///
+ /// This differs from direct scheduling (e.g. Util.FireAndForget) in that a job can be run in the job
+ /// engine if it is running, where all jobs are currently performed in sequence on a single thread. This is
+ /// to prevent observed overload and server freeze problems when there are hundreds of connections which all attempt to
+ /// perform work at once (e.g. in conference situations). With lower numbers of connections, the small
+ /// delay in performing jobs in sequence rather than concurrently has not been notiecable in testing, though a future more
+ /// sophisticated implementation could perform jobs concurrently when the server is under low load.
+ ///
+ /// However, be advised that some callers of this function rely on all jobs being performed in sequence if any
+ /// jobs are performed in sequence (i.e. if jobengine is active or not). Therefore, expanding the jobengine
+ /// beyond a single thread will require considerable thought.
+ ///
+ /// Also, any jobs submitted must be guaranteed to complete within a reasonable timeframe (e.g. they cannot
+ /// incorporate a network delay with a long timeout). At the moment, work that could suffer such issues
+ /// should still be run directly with RunInThread(), Util.FireAndForget(), etc. This is another area where
+ /// the job engine could be improved and so CPU utilization improved by better management of concurrency within
+ /// OpenSimulator.
+ ///
+ /// General classification for the job (e.g. "RezAttachments").
+ /// Callback for job.
+ /// Specific name of job (e.g. "RezAttachments for Joe Bloggs"
+ /// Object to pass to callback when run
+ /// If set to true then the job may be run in ths calling thread.
+ /// If the true then the job must never timeout.
+ /// If set to true then extra logging is performed.
+ public static void RunJob(
+ string jobType, WaitCallback callback, string name, object obj,
+ bool canRunInThisThread = false, bool mustNotTimeout = false,
+ bool log = false)
{
if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
{
@@ -464,8 +495,12 @@ namespace OpenSim.Framework.Monitoring
if (JobEngine.IsRunning)
JobEngine.QueueRequest(name, callback, obj);
- else
+ else if (canRunInThisThread)
+ callback(obj);
+ else if (mustNotTimeout)
RunInThread(callback, name, obj, log);
+ else
+ Util.FireAndForget(callback, obj, name);
}
}
}
\ No newline at end of file
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
index 1d234e23a8..757ec4335b 100644
--- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs
+++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
@@ -1228,15 +1228,11 @@ namespace OpenSim.Region.Framework.Scenes
// viewers without (e.g. v1 viewers) will not, so we still need to make this call.
if (Scene.AttachmentsModule != null)
{
- if (Watchdog.JobEngine.IsRunning)
- Watchdog.RunWhenPossible(
- "RezAttachments",
- o => Scene.AttachmentsModule.RezAttachments(this),
- string.Format("Rez attachments for {0} in {1}", Name, Scene.Name),
- null);
- else
- Util.FireAndForget(
- o => Scene.AttachmentsModule.RezAttachments(this), null, "ScenePresence.RezAttachmentsOnLogin");
+ Watchdog.RunJob(
+ "RezAttachments",
+ o => Scene.AttachmentsModule.RezAttachments(this),
+ string.Format("Rez attachments for {0} in {1}", Name, Scene.Name),
+ null);
}
}
else
@@ -1258,18 +1254,12 @@ namespace OpenSim.Region.Framework.Scenes
if (attachments.Count > 0)
{
- if (Watchdog.JobEngine.IsRunning)
- {
- Watchdog.RunWhenPossible(
- "StartAttachmentScripts",
- o => RestartAttachmentScripts(attachments),
- string.Format("Start attachment scripts for {0} in {1}", Name, Scene.Name),
- null);
- }
- else
- {
- RestartAttachmentScripts(attachments);
- }
+ Watchdog.RunJob(
+ "StartAttachmentScripts",
+ o => RestartAttachmentScripts(attachments),
+ string.Format("Start attachment scripts for {0} in {1}", Name, Scene.Name),
+ null,
+ true);
}
}
@@ -1825,18 +1815,12 @@ namespace OpenSim.Region.Framework.Scenes
// XXX: If we force an update after activity has completed, then multiple attachments do appear correctly on a destination region
// If we do it a little bit earlier (e.g. when converting the child to a root agent) then this does not work.
// This may be due to viewer code or it may be something we're not doing properly simulator side.
- if (Watchdog.JobEngine.IsRunning)
- {
- Watchdog.RunWhenPossible(
- "ScheduleAttachmentsForFullUpdate",
- o => ScheduleAttachmentsForFullUpdate(),
- string.Format("Schedule attachments for full update for {0} in {1}", Name, Scene.Name),
- null);
- }
- else
- {
- ScheduleAttachmentsForFullUpdate();
- }
+ Watchdog.RunJob(
+ "ScheduleAttachmentsForFullUpdate",
+ o => ScheduleAttachmentsForFullUpdate(),
+ string.Format("Schedule attachments for full update for {0} in {1}", Name, Scene.Name),
+ null,
+ true);
// m_log.DebugFormat(
// "[SCENE PRESENCE]: Completing movement of {0} into region {1} took {2}ms",
@@ -3391,7 +3375,7 @@ namespace OpenSim.Region.Framework.Scenes
SentInitialDataToClient = true;
// Send all scene object to the new client
- Watchdog.RunWhenPossible("SendInitialDataToClient", delegate
+ Watchdog.RunJob("SendInitialDataToClient", delegate
{
// m_log.DebugFormat(
// "[SCENE PRESENCE]: Sending initial data to {0} agent {1} in {2}, tp flags {3}",
@@ -3409,7 +3393,7 @@ namespace OpenSim.Region.Framework.Scenes
if (e != null && e is SceneObjectGroup)
((SceneObjectGroup)e).SendFullUpdateToClient(ControllingClient);
}
- }, string.Format("SendInitialDataToClient ({0} in {1})", Name, Scene.Name), null);
+ }, string.Format("SendInitialDataToClient ({0} in {1})", Name, Scene.Name),null, false, true);
}
///
@@ -4073,14 +4057,12 @@ namespace OpenSim.Region.Framework.Scenes
// We don't need to worry about a race condition as the job to later start the scripts is also
// JobEngine scheduled and so will always occur after this task.
// XXX: This will not be true if JobEngine ever gets more than one thread.
- if (Watchdog.JobEngine.IsRunning)
- Watchdog.RunWhenPossible(
- "CopyAttachments",
- o => Scene.AttachmentsModule.CopyAttachments(cAgent, this),
- string.Format("Copy attachments for {0} entering {1}", Name, Scene.Name),
- null);
- else
- Scene.AttachmentsModule.CopyAttachments(cAgent, this);
+ Watchdog.RunJob(
+ "CopyAttachments",
+ o => Scene.AttachmentsModule.CopyAttachments(cAgent, this),
+ string.Format("Copy attachments for {0} entering {1}", Name, Scene.Name),
+ null,
+ true);
}
// This must occur after attachments are copied or scheduled to be copied, as it releases the CompleteMovement() calling thread