diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs index e2178e59be..39d1875c7b 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs @@ -49,7 +49,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests m_regStatus = RegionStatus.Up; } - public override void Update(int frames) {} + public override bool Update(int frames) { return true; } public override void LoadWorldMap() {} public override ISceneAgent AddNewAgent(IClientAPI client, PresenceType type) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index a43de2904d..53001e9fca 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -328,6 +328,17 @@ namespace OpenSim.Region.Framework.Scenes private Dictionary m_extraSettings; + /// + /// If true then the next time the scene loop is activated, updates will be performed by firing of a timer + /// rather than on a single thread that sleeps. + /// + public bool UpdateOnTimer { get; set; } + + /// + /// Only used if we are updating scene on a timer rather than sleeping a thread. + /// + private Timer m_sceneUpdateTimer; + /// /// Current scene frame number /// @@ -430,7 +441,8 @@ namespace OpenSim.Region.Framework.Scenes /// Is the scene active? /// /// - /// If false, maintenance and update loops are not being run. Updates can still be triggered manually if + /// If false, maintenance and update loops are not being run, though after setting to false update may still + /// be active for a period (and IsRunning will still be true). Updates can still be triggered manually if /// the scene is not active. /// public bool Active @@ -453,8 +465,11 @@ namespace OpenSim.Region.Framework.Scenes } private volatile bool m_active; -// private int m_lastUpdate; -// private bool m_firstHeartbeat = true; + /// + /// If true then updates are running. This may be true for a short period after a scene is de-activated. + /// + public bool IsRunning { get { return m_isRunning; } } + private volatile bool m_isRunning; private Timer m_mapGenerationTimer = new Timer(); private bool m_generateMaptiles; @@ -1352,19 +1367,18 @@ namespace OpenSim.Region.Framework.Scenes /// public void Start(bool startScripts) { + if (IsRunning) + return; + + m_isRunning = true; m_active = true; // m_log.DebugFormat("[SCENE]: Starting Heartbeat timer for {0}", RegionInfo.RegionName); - - //m_heartbeatTimer.Enabled = true; - //m_heartbeatTimer.Interval = (int)(m_timespan * 1000); - //m_heartbeatTimer.Elapsed += new ElapsedEventHandler(Heartbeat); if (m_heartbeatThread != null) { m_heartbeatThread.Abort(); m_heartbeatThread = null; } -// m_lastUpdate = Util.EnvironmentTickCount(); m_heartbeatThread = Watchdog.StartThread( @@ -1401,15 +1415,6 @@ namespace OpenSim.Region.Framework.Scenes /// private void Heartbeat() { -// if (!Monitor.TryEnter(m_heartbeatLock)) -// { -// Watchdog.RemoveThread(); -// return; -// } - -// try -// { - m_eventManager.TriggerOnRegionStarted(this); // The first frame can take a very long time due to physics actors being added on startup. Therefore, @@ -1418,21 +1423,37 @@ namespace OpenSim.Region.Framework.Scenes Update(1); Watchdog.StartThread( - Maintenance, string.Format("Maintenance ({0})", RegionInfo.RegionName), ThreadPriority.Normal, false, true); + Maintenance, string.Format("Maintenance ({0})", RegionInfo.RegionName), ThreadPriority.Normal, false, true); Watchdog.GetCurrentThreadInfo().AlarmIfTimeout = true; - Update(-1); -// m_lastUpdate = Util.EnvironmentTickCount(); -// m_firstHeartbeat = false; -// } -// finally -// { -// Monitor.Pulse(m_heartbeatLock); -// Monitor.Exit(m_heartbeatLock); -// } + if (UpdateOnTimer) + { + m_sceneUpdateTimer = new Timer(MinFrameTime * 1000); + m_sceneUpdateTimer.AutoReset = true; + m_sceneUpdateTimer.Elapsed += Update; + m_sceneUpdateTimer.Start(); + } + else + { + Update(-1); + Watchdog.RemoveThread(); + m_isRunning = false; + } + } - Watchdog.RemoveThread(); + private void Update(object sender, ElapsedEventArgs e) + { + // If the last frame did not complete on time, then immediately start the next update on the same thread + // and ignore further timed updates until we have a frame that had spare time. + while (!Update(1) && Active) {} + + if (!Active || m_shuttingDown) + { + m_sceneUpdateTimer.Stop(); + m_sceneUpdateTimer = null; + m_isRunning = false; + } } private void Maintenance() @@ -1502,7 +1523,7 @@ namespace OpenSim.Region.Framework.Scenes } } - public override void Update(int frames) + public override bool Update(int frames) { long? endFrame = null; @@ -1652,7 +1673,8 @@ namespace OpenSim.Region.Framework.Scenes EventManager.TriggerRegionHeartbeatEnd(this); - Watchdog.UpdateThread(); + if (!UpdateOnTimer) + Watchdog.UpdateThread(); previousFrameTick = m_lastFrameTick; m_lastFrameTick = Util.EnvironmentTickCount(); @@ -1661,9 +1683,11 @@ namespace OpenSim.Region.Framework.Scenes if (tmpMS > 0) { - Thread.Sleep(tmpMS); - spareMS += tmpMS; - } + spareMS = tmpMS; + + if (!UpdateOnTimer) + Thread.Sleep(tmpMS); + } frameMS = Util.EnvironmentTickCountSubtract(maintc); maintc = Util.EnvironmentTickCount(); @@ -1683,7 +1707,7 @@ namespace OpenSim.Region.Framework.Scenes StatsReporter.AddSpareMS(spareMS); StatsReporter.addScriptLines(m_sceneGraph.GetScriptLPS()); - // Optionally warn if a frame takes double the amount of time that it should. + // Optionally warn if a frame takes double the amount of time that it should. if (DebugUpdates && Util.EnvironmentTickCountSubtract( m_lastFrameTick, previousFrameTick) > (int)(MinFrameTime * 1000 * 2)) @@ -1693,6 +1717,8 @@ namespace OpenSim.Region.Framework.Scenes MinFrameTime * 1000, RegionInfo.RegionName); } + + return spareMS >= 0; } public void AddGroupTarget(SceneObjectGroup grp) diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs index 0445268315..aaddce6050 100644 --- a/OpenSim/Region/Framework/Scenes/SceneBase.cs +++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs @@ -196,7 +196,8 @@ namespace OpenSim.Region.Framework.Scenes /// Number of frames to update. Exits on shutdown even if there are frames remaining. /// If -1 then updates until shutdown. /// - public abstract void Update(int frames); + /// true if update completed within minimum frame time, false otherwise. + public abstract bool Update(int frames); #endregion diff --git a/OpenSim/Region/OptionalModules/World/SceneCommands/SceneCommandsModule.cs b/OpenSim/Region/OptionalModules/World/SceneCommands/SceneCommandsModule.cs index f2595c8517..0927c4ffd6 100644 --- a/OpenSim/Region/OptionalModules/World/SceneCommands/SceneCommandsModule.cs +++ b/OpenSim/Region/OptionalModules/World/SceneCommands/SceneCommandsModule.cs @@ -30,6 +30,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; +using System.Threading; using log4net; using Mono.Addins; using Nini.Config; @@ -93,42 +94,44 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments "Debug", this, "debug scene get", "debug scene get", "List current scene options.", - "active - if false then main scene update and maintenance loops are suspended.\n" - + "animations - if true then extra animations debug information is logged.\n" - + "appear-refresh - if true then appearance is resent to other avatars every 60 seconds.\n" - + "child-repri - how far an avatar must move in meters before we update the position of its child agents in neighbouring regions.\n" - + "client-pos-upd - the tolerance before clients are updated with new rotation information for an avatar.\n" - + "client-rot-upd - the tolerance before clients are updated with new rotation information for an avatar.\n" - + "client-vel-upd - the tolerance before clients are updated with new velocity information for an avatar.\n" - + "root-upd-per - if greater than 1, terse updates are only sent to root agents other than the originator on every n updates.\n" - + "child-upd-per - if greater than 1, terse updates are only sent to child agents on every n updates.\n" - + "collisions - if false then collisions with other objects are turned off.\n" - + "pbackup - if false then periodic scene backup is turned off.\n" - + "physics - if false then all physics objects are non-physical.\n" - + "scripting - if false then no scripting operations happen.\n" - + "teleport - if true then some extra teleport debug information is logged.\n" - + "updates - if true then any frame which exceeds double the maximum desired frame time is logged.", + "active - if false then main scene update and maintenance loops are suspended.\n" + + "animations - if true then extra animations debug information is logged.\n" + + "appear-refresh - if true then appearance is resent to other avatars every 60 seconds.\n" + + "child-repri - how far an avatar must move in meters before we update the position of its child agents in neighbouring regions.\n" + + "client-pos-upd - the tolerance before clients are updated with new rotation information for an avatar.\n" + + "client-rot-upd - the tolerance before clients are updated with new rotation information for an avatar.\n" + + "client-vel-upd - the tolerance before clients are updated with new velocity information for an avatar.\n" + + "root-upd-per - if greater than 1, terse updates are only sent to root agents other than the originator on every n updates.\n" + + "child-upd-per - if greater than 1, terse updates are only sent to child agents on every n updates.\n" + + "collisions - if false then collisions with other objects are turned off.\n" + + "pbackup - if false then periodic scene backup is turned off.\n" + + "physics - if false then all physics objects are non-physical.\n" + + "scripting - if false then no scripting operations happen.\n" + + "teleport - if true then some extra teleport debug information is logged.\n" + + "update-on-timer - If true then the scene is updated via a timer. If false then a thread with sleep is used.\n" + + "updates - if true then any frame which exceeds double the maximum desired frame time is logged.", HandleDebugSceneGetCommand); scene.AddCommand( "Debug", this, "debug scene set", - "debug scene set active|collisions|pbackup|physics|scripting|teleport|updates true|false", + "debug scene set ", "Turn on scene debugging options.", - "active - if false then main scene update and maintenance loops are suspended.\n" - + "animations - if true then extra animations debug information is logged.\n" - + "appear-refresh - if true then appearance is resent to other avatars every 60 seconds.\n" - + "child-repri - how far an avatar must move in meters before we update the position of its child agents in neighbouring regions.\n" - + "client-pos-upd - the tolerance before clients are updated with new rotation information for an avatar.\n" - + "client-rot-upd - the tolerance before clients are updated with new rotation information for an avatar.\n" - + "client-vel-upd - the tolerance before clients are updated with new velocity information for an avatar.\n" - + "root-upd-per - if greater than 1, terse updates are only sent to root agents other than the originator on every n updates.\n" - + "child-upd-per - if greater than 1, terse updates are only sent to child agents on every n updates.\n" - + "collisions - if false then collisions with other objects are turned off.\n" - + "pbackup - if false then periodic scene backup is turned off.\n" - + "physics - if false then all physics objects are non-physical.\n" - + "scripting - if false then no scripting operations happen.\n" - + "teleport - if true then some extra teleport debug information is logged.\n" - + "updates - if true then any frame which exceeds double the maximum desired frame time is logged.", + "active - if false then main scene update and maintenance loops are suspended.\n" + + "animations - if true then extra animations debug information is logged.\n" + + "appear-refresh - if true then appearance is resent to other avatars every 60 seconds.\n" + + "child-repri - how far an avatar must move in meters before we update the position of its child agents in neighbouring regions.\n" + + "client-pos-upd - the tolerance before clients are updated with new rotation information for an avatar.\n" + + "client-rot-upd - the tolerance before clients are updated with new rotation information for an avatar.\n" + + "client-vel-upd - the tolerance before clients are updated with new velocity information for an avatar.\n" + + "root-upd-per - if greater than 1, terse updates are only sent to root agents other than the originator on every n updates.\n" + + "child-upd-per - if greater than 1, terse updates are only sent to child agents on every n updates.\n" + + "collisions - if false then collisions with other objects are turned off.\n" + + "pbackup - if false then periodic scene backup is turned off.\n" + + "physics - if false then all physics objects are non-physical.\n" + + "scripting - if false then no scripting operations happen.\n" + + "teleport - if true then some extra teleport debug information is logged.\n" + + "update-on-timer - If true then the scene is updated via a timer. If false then a thread with sleep is used.\n" + + "updates - if true then any frame which exceeds double the maximum desired frame time is logged.", HandleDebugSceneSetCommand); } @@ -163,6 +166,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments cdl.AddRow("physics", m_scene.PhysicsEnabled); cdl.AddRow("scripting", m_scene.ScriptsEnabled); cdl.AddRow("teleport", m_scene.DebugTeleporting); + cdl.AddRow("update-on-timer", m_scene.UpdateOnTimer); cdl.AddRow("updates", m_scene.DebugUpdates); MainConsole.Instance.OutputFormat("Scene {0} options:", m_scene.Name); @@ -304,6 +308,21 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments m_scene.DebugTeleporting = enableTeleportDebugging; } + if (options.ContainsKey("update-on-timer")) + { + bool enableUpdateOnTimer; + if (bool.TryParse(options["update-on-timer"], out enableUpdateOnTimer)) + { + m_scene.UpdateOnTimer = enableUpdateOnTimer; + m_scene.Active = false; + + while (m_scene.IsRunning) + Thread.Sleep(20); + + m_scene.Active = true; + } + } + if (options.ContainsKey("updates")) { bool enableUpdateDebugging;