diff --git a/OpenSim/Framework/Location.cs b/OpenSim/Framework/Location.cs index 9504e03547..0b888347e7 100644 --- a/OpenSim/Framework/Location.cs +++ b/OpenSim/Framework/Location.cs @@ -33,10 +33,10 @@ namespace OpenSim.Framework [Serializable] public class Location : ICloneable { - private readonly int m_x; - private readonly int m_y; + private readonly uint m_x; + private readonly uint m_y; - public Location(int x, int y) + public Location(uint x, uint y) { m_x = x; m_y = y; @@ -44,21 +44,21 @@ namespace OpenSim.Framework public Location(ulong regionHandle) { - m_x = (int) regionHandle; - m_y = (int) (regionHandle >> 32); + m_x = (uint)(regionHandle >> 32); + m_y = (uint)(regionHandle & (ulong)uint.MaxValue); } public ulong RegionHandle { - get { return Utils.UIntsToLong((uint)m_x, (uint)m_y); } + get { return Utils.UIntsToLong(m_x, m_y); } } - public int X + public uint X { get { return m_x; } } - public int Y + public uint Y { get { return m_y; } } diff --git a/OpenSim/Framework/Tests/LocationTest.cs b/OpenSim/Framework/Tests/LocationTest.cs index a56ecb4049..af5f164d7f 100644 --- a/OpenSim/Framework/Tests/LocationTest.cs +++ b/OpenSim/Framework/Tests/LocationTest.cs @@ -51,21 +51,21 @@ namespace OpenSim.Framework.Tests [Test] public void locationXYRegionHandle() { - Location TestLocation1 = new Location(256000,256000); - Location TestLocation2 = new Location(1099511628032000); + Location TestLocation1 = new Location(255000,256000); + Location TestLocation2 = new Location(1095216660736000); Assert.That(TestLocation1 == TestLocation2); - Assert.That(TestLocation2.X == 256000 && TestLocation2.Y == 256000, "Test xy location doesn't match regionhandle provided"); + Assert.That(TestLocation2.X == 255000 && TestLocation2.Y == 256000, "Test xy location doesn't match regionhandle provided"); - Assert.That(TestLocation2.RegionHandle == 1099511628032000, + Assert.That(TestLocation2.RegionHandle == 1095216660736000, "Location RegionHandle Property didn't match regionhandle provided in constructor"); - TestLocation1 = new Location(256001, 256001); - TestLocation2 = new Location(1099511628032000); + TestLocation1 = new Location(255001, 256001); + TestLocation2 = new Location(1095216660736000); Assert.That(TestLocation1 != TestLocation2); - Assert.That(TestLocation1.Equals(256001, 256001), "Equals(x,y) failed to match the position in the constructor"); + Assert.That(TestLocation1.Equals(255001, 256001), "Equals(x,y) failed to match the position in the constructor"); Assert.That(TestLocation2.GetHashCode() == (TestLocation2.X.GetHashCode() ^ TestLocation2.Y.GetHashCode()), "GetHashCode failed to produce the expected hashcode"); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 2a2c819eb3..71b464bc07 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -698,7 +698,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { DefaultClientPacketDebugLevel = newDebug; MainConsole.Instance.OutputFormat( - "Debug packet debug for new clients set to {0}", DefaultClientPacketDebugLevel); + "Debug packet debug for new clients set to {0} in {1}", DefaultClientPacketDebugLevel, m_scene.Name); } else { @@ -1998,7 +1998,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // // Instead, now wait for data present to be explicitly signalled. Evidence so far is that with // modern mono it reduces CPU base load since there is no more continuous polling. - m_dataPresentEvent.WaitOne(100); + if (!m_packetSent) + m_dataPresentEvent.WaitOne(100); Watchdog.UpdateThread(); } diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs index 505bb5370e..7b12c81f27 100644 --- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.Reflection; using System.IO; +using System.Threading; using System.Xml; using log4net; using Mono.Addins; @@ -51,6 +52,15 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); public int DebugLevel { get; set; } + + /// + /// Period to sleep per 100 prims in order to avoid CPU spikes when an avatar with many attachments logs in + /// or many avatars with a medium levels of attachments login simultaneously. + /// + /// + /// A value of 0 will apply no pause. The pause is specified in milliseconds. + /// + public int ThrottlePer100PrimsRezzed { get; set; } private Scene m_scene; private IInventoryAccessModule m_invAccessModule; @@ -67,9 +77,15 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments { IConfig config = source.Configs["Attachments"]; if (config != null) + { Enabled = config.GetBoolean("Enabled", true); + + ThrottlePer100PrimsRezzed = config.GetInt("ThrottlePer100PrimsRezzed", 0); + } else + { Enabled = true; + } } public void AddRegion(Scene scene) @@ -88,24 +104,43 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments MainConsole.Instance.Commands.AddCommand( "Debug", false, - "debug attachments", - "debug attachments [0|1]", - "Turn on attachments debugging\n" - + " <= 0 - turns off debugging\n" - + " >= 1 - turns on attachment message logging\n", - HandleDebugAttachments); + "debug attachments log", + "debug attachments log [0|1]", + "Turn on attachments debug logging", + " <= 0 - turns off debug logging\n" + + " >= 1 - turns on attachment message debug logging", + HandleDebugAttachmentsLog); + + MainConsole.Instance.Commands.AddCommand( + "Debug", + false, + "debug attachments throttle", + "debug attachments throttle ", + "Turn on attachments throttling.", + "This requires a millisecond value. " + + " == 0 - disable throttling.\n" + + " > 0 - sleeps for this number of milliseconds per 100 prims rezzed.", + HandleDebugAttachmentsThrottle); + + MainConsole.Instance.Commands.AddCommand( + "Debug", + false, + "debug attachments status", + "debug attachments status", + "Show current attachments debug status", + HandleDebugAttachmentsStatus); } // TODO: Should probably be subscribing to CloseClient too, but this doesn't yet give us IClientAPI } - private void HandleDebugAttachments(string module, string[] args) + private void HandleDebugAttachmentsLog(string module, string[] args) { int debugLevel; - if (!(args.Length == 3 && int.TryParse(args[2], out debugLevel))) + if (!(args.Length == 4 && int.TryParse(args[3], out debugLevel))) { - MainConsole.Instance.OutputFormat("Usage: debug attachments [0|1]"); + MainConsole.Instance.OutputFormat("Usage: debug attachments log [0|1]"); } else { @@ -115,6 +150,29 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments } } + private void HandleDebugAttachmentsThrottle(string module, string[] args) + { + int ms; + + if (args.Length == 4 && int.TryParse(args[3], out ms)) + { + ThrottlePer100PrimsRezzed = ms; + MainConsole.Instance.OutputFormat( + "Attachments rez throttle per 100 prims is now {0} in {1}", ThrottlePer100PrimsRezzed, m_scene.Name); + + return; + } + + MainConsole.Instance.OutputFormat("Usage: debug attachments throttle "); + } + + private void HandleDebugAttachmentsStatus(string module, string[] args) + { + MainConsole.Instance.OutputFormat("Settings for {0}", m_scene.Name); + MainConsole.Instance.OutputFormat("Debug logging level: {0}", DebugLevel); + MainConsole.Instance.OutputFormat("Throttle per 100 prims: {0}ms", ThrottlePer100PrimsRezzed); + } + /// /// Listen for client triggered running state changes so that we can persist the script's object if necessary. /// @@ -275,7 +333,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments List attachments = sp.Appearance.GetAttachments(); foreach (AvatarAttachment attach in attachments) { - uint p = (uint)attach.AttachPoint; + uint attachmentPt = (uint)attach.AttachPoint; // m_log.DebugFormat( // "[ATTACHMENTS MODULE]: Doing initial rez of attachment with itemID {0}, assetID {1}, point {2} for {3} in {4}", @@ -303,14 +361,28 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments // If we're an NPC then skip all the item checks and manipulations since we don't have an // inventory right now. - RezSingleAttachmentFromInventoryInternal( - sp, sp.PresenceType == PresenceType.Npc ? UUID.Zero : attach.ItemID, attach.AssetID, p, true, null); + SceneObjectGroup objatt + = RezSingleAttachmentFromInventoryInternal( + sp, sp.PresenceType == PresenceType.Npc ? UUID.Zero : attach.ItemID, attach.AssetID, attachmentPt, true, null); + + + if (ThrottlePer100PrimsRezzed > 0) + { + int throttleMs = (int)Math.Round((float)objatt.PrimCount / 100 * ThrottlePer100PrimsRezzed); + + if (DebugLevel > 0) + m_log.DebugFormat( + "[ATTACHMENTS MODULE]: Throttling by {0}ms after rez of {1} with {2} prims for attachment to {3} on point {4} in {5}", + throttleMs, objatt.Name, objatt.PrimCount, sp.Name, attachmentPt, m_scene.Name); + + Thread.Sleep(throttleMs); + } } catch (Exception e) { UUID agentId = (sp.ControllingClient == null) ? (UUID)null : sp.ControllingClient.AgentId; m_log.ErrorFormat("[ATTACHMENTS MODULE]: Unable to rez attachment with itemID {0}, assetID {1}, point {2} for {3}: {4}\n{5}", - attach.ItemID, attach.AssetID, p, agentId, e.Message, e.StackTrace); + attach.ItemID, attach.AssetID, attachmentPt, agentId, e.Message, e.StackTrace); } } } @@ -984,8 +1056,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments if (DebugLevel > 0) m_log.DebugFormat( - "[ATTACHMENTS MODULE]: Rezzed single object {0} for attachment to {1} on point {2} in {3}", - objatt.Name, sp.Name, attachmentPt, m_scene.Name); + "[ATTACHMENTS MODULE]: Rezzed single object {0} with {1} prims for attachment to {2} on point {3} in {4}", + objatt.Name, objatt.PrimCount, sp.Name, attachmentPt, m_scene.Name); // HasGroupChanged is being set from within RezObject. Ideally it would be set by the caller. objatt.HasGroupChanged = false; diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index c9a79fce30..286076da19 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -703,11 +703,18 @@ ; on every login ReuseTextures = false + [Attachments] ; Controls whether avatar attachments are enabled. ; Defaults to true - only set to false for debugging purposes Enabled = true + ; Controls the number of milliseconds that are slept per 100 prims rezzed in attachments + ; Experimental setting to control CPU spiking when avatars with many attachments login + ; or when multiple avatars with medium level attachments login simultaneously. + ; If 0 then no throttling is performed. + ThrottlePer100PrimsRezzed = 0; + [Mesh] ; enable / disable Collada mesh support