From 4fa088bafb4c78ad3177b0e944a4312bd6abdea7 Mon Sep 17 00:00:00 2001 From: teravus Date: Sun, 4 Nov 2012 22:57:24 -0500 Subject: Pipe Throttle Update Event to EventManager, client --> ScenePresence --> EventManager, so that modules can know when throttles are updated. The event contains no client specific data to preserve the possibility of 'multiple clients' and you must still call ControllingClient.GetThrottlesPacked(f) to see what the throttles actually are once the event fires. Hook EventManager.OnUpdateThrottle to GetTextureModule. --- .../ClientStack/Linden/Caps/GetTextureModule.cs | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'OpenSim/Region/ClientStack/Linden/Caps') diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs index d1a1583..19d4b91 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs @@ -91,6 +91,7 @@ namespace OpenSim.Region.ClientStack.Linden { m_scene.EventManager.OnRegisterCaps -= RegisterCaps; m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps; + m_scene.EventManager.OnThrottleUpdate -= ThrottleUpdate; m_scene = null; } @@ -101,6 +102,7 @@ namespace OpenSim.Region.ClientStack.Linden m_scene.EventManager.OnRegisterCaps += RegisterCaps; m_scene.EventManager.OnDeregisterCaps += DeregisterCaps; + m_scene.EventManager.OnThrottleUpdate += ThrottleUpdate; if (m_workerThreads == null) { @@ -118,6 +120,46 @@ namespace OpenSim.Region.ClientStack.Linden } } } + private int ExtractImageThrottle(byte[] pthrottles) + { + + byte[] adjData; + int pos = 0; + + if (!BitConverter.IsLittleEndian) + { + byte[] newData = new byte[7 * 4]; + Buffer.BlockCopy(pthrottles, 0, newData, 0, 7 * 4); + + for (int i = 0; i < 7; i++) + Array.Reverse(newData, i * 4, 4); + + adjData = newData; + } + else + { + adjData = pthrottles; + } + + // 0.125f converts from bits to bytes + //int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; + // int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; + // int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; + // int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; + // int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; + pos = pos + 16; + int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; + //int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); + return texture; + } + + // Now we know when the throttle is changed by the client in the case of a root agent or by a neighbor region in the case of a child agent. + public void ThrottleUpdate(ScenePresence p) + { + byte[] throttles = p.ControllingClient.GetThrottlesPacked(1); + UUID user = p.UUID; + int imagethrottle = ExtractImageThrottle(throttles); + } public void PostInitialise() { -- cgit v1.1 From b7b96a5e4f1e26341742e35e5253e6e14797bd15 Mon Sep 17 00:00:00 2001 From: teravus Date: Mon, 5 Nov 2012 13:10:00 -0500 Subject: Another step in the chain. Pipe the throttle update to the appropriate PollServiceTextureEventArgs. Each poll service having it's own throttle member is more consistent with the model then the region module keeping track of all of them globally and better for locking too. The Poll Services object is not set static to handle multiple nearby regions on the same simulator. Next step is hooking it up to HasEvents --- .../ClientStack/Linden/Caps/GetTextureModule.cs | 47 ++++++++++++++++------ 1 file changed, 35 insertions(+), 12 deletions(-) (limited to 'OpenSim/Region/ClientStack/Linden/Caps') diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs index 19d4b91..4bfdbff 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs @@ -61,6 +61,13 @@ namespace OpenSim.Region.ClientStack.Linden public Hashtable request; } + public struct aPollResponse + { + public Hashtable response; + public int bytes; + } + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private Scene m_scene; @@ -75,6 +82,8 @@ namespace OpenSim.Region.ClientStack.Linden private static OpenMetaverse.BlockingQueue m_queue = new OpenMetaverse.BlockingQueue(); + private Dictionary m_pollservices = new Dictionary(); + #region ISharedRegionModule Members public void Initialise(IConfigSource source) @@ -147,7 +156,7 @@ namespace OpenSim.Region.ClientStack.Linden // int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; // int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; // int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; - pos = pos + 16; + pos = pos + 20; int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; //int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); return texture; @@ -159,6 +168,11 @@ namespace OpenSim.Region.ClientStack.Linden byte[] throttles = p.ControllingClient.GetThrottlesPacked(1); UUID user = p.UUID; int imagethrottle = ExtractImageThrottle(throttles); + PollServiceTextureEventArgs args; + if (m_pollservices.TryGetValue(user,out args)) + { + args.UpdateThrottle(imagethrottle); + } } public void PostInitialise() @@ -187,8 +201,8 @@ namespace OpenSim.Region.ClientStack.Linden { private List requests = new List(); - private Dictionary responses = - new Dictionary(); + private Dictionary responses = + new Dictionary(); private Scene m_scene; @@ -196,7 +210,7 @@ namespace OpenSim.Region.ClientStack.Linden base(null, null, null, null, pId, int.MaxValue) { m_scene = scene; - + // x is request id, y is userid HasEvents = (x, y) => { lock (responses) @@ -208,7 +222,7 @@ namespace OpenSim.Region.ClientStack.Linden { try { - return responses[x]; + return responses[x].response; } finally { @@ -216,14 +230,14 @@ namespace OpenSim.Region.ClientStack.Linden } } }; - + // x is request id, y is request data hashtable Request = (x, y) => { aPollRequest reqinfo = new aPollRequest(); reqinfo.thepoll = this; reqinfo.reqID = x; reqinfo.request = y; - + m_queue.Enqueue(reqinfo); }; @@ -265,16 +279,21 @@ namespace OpenSim.Region.ClientStack.Linden response["content_type"] = "text/plain"; response["keepalive"] = false; response["reusecontext"] = false; - + lock (responses) - responses[requestID] = response; + responses[requestID] = new aPollResponse() {bytes = 0,response = response}; return; } response = m_getTextureHandler.Handle(requestinfo.request); lock (responses) - responses[requestID] = response; + responses[requestID] = new aPollResponse() { bytes = (int)response["int_bytes"], response = response}; + } + + internal void UpdateThrottle(int pimagethrottle) + { + } } @@ -299,19 +318,23 @@ namespace OpenSim.Region.ClientStack.Linden protocol = "https"; } caps.RegisterHandler("GetTexture", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl)); - + m_pollservices.Add(agentID, args); m_capsDict[agentID] = capUrl; } private void DeregisterCaps(UUID agentID, Caps caps) { string capUrl; - + PollServiceTextureEventArgs args; if (m_capsDict.TryGetValue(agentID, out capUrl)) { MainServer.Instance.RemoveHTTPHandler("", capUrl); m_capsDict.Remove(agentID); } + if (m_pollservices.TryGetValue(agentID, out args)) + { + m_pollservices.Remove(agentID); + } } private void DoTextureRequests() -- cgit v1.1 From 182b4872437e97762ac494dbf1815fe63aa29405 Mon Sep 17 00:00:00 2001 From: teravus Date: Mon, 5 Nov 2012 22:05:10 -0500 Subject: This implements the Caps throttler. After some testing, the system seemed to be OK with me specifying allowing 1 oversized image per 70,000b/sec with at least one. Try it out, start with a low bandwidth setting and then, set your bandwidth setting middle/high and see the difference. Tested with Two Clients on a region with 1800 textures all visible at once. --- .../ClientStack/Linden/Caps/GetTextureModule.cs | 119 ++++++++++++++++++--- 1 file changed, 106 insertions(+), 13 deletions(-) (limited to 'OpenSim/Region/ClientStack/Linden/Caps') diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs index 4bfdbff..8cba6c8 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetTextureModule.cs @@ -61,7 +61,7 @@ namespace OpenSim.Region.ClientStack.Linden public Hashtable request; } - public struct aPollResponse + public class aPollResponse { public Hashtable response; public int bytes; @@ -151,13 +151,18 @@ namespace OpenSim.Region.ClientStack.Linden } // 0.125f converts from bits to bytes - //int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; - // int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; - // int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; - // int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; - // int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; + //int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); + //pos += 4; + // int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); + //pos += 4; + // int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); + // pos += 4; + // int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); + // pos += 4; + // int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); + // pos += 4; pos = pos + 20; - int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; + int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); //pos += 4; //int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); return texture; } @@ -205,7 +210,7 @@ namespace OpenSim.Region.ClientStack.Linden new Dictionary(); private Scene m_scene; - + private CapsDataThrottler m_throttler = new CapsDataThrottler(100000, 1400000,10000); public PollServiceTextureEventArgs(UUID pId, Scene scene) : base(null, null, null, null, pId, int.MaxValue) { @@ -214,7 +219,12 @@ namespace OpenSim.Region.ClientStack.Linden HasEvents = (x, y) => { lock (responses) - return responses.ContainsKey(x); + { + bool ret = m_throttler.hasEvents(x, responses); + m_throttler.ProcessTime(); + return ret; + + } }; GetEvents = (x, y) => { @@ -281,19 +291,27 @@ namespace OpenSim.Region.ClientStack.Linden response["reusecontext"] = false; lock (responses) - responses[requestID] = new aPollResponse() {bytes = 0,response = response}; + responses[requestID] = new aPollResponse() {bytes = 0, response = response}; return; } - + response = m_getTextureHandler.Handle(requestinfo.request); lock (responses) - responses[requestID] = new aPollResponse() { bytes = (int)response["int_bytes"], response = response}; + { + responses[requestID] = new aPollResponse() + { + bytes = (int) response["int_bytes"], + response = response + }; + + } + m_throttler.ProcessTime(); } internal void UpdateThrottle(int pimagethrottle) { - + m_throttler.ThrottleBytes = pimagethrottle; } } @@ -347,4 +365,79 @@ namespace OpenSim.Region.ClientStack.Linden } } } + + internal sealed class CapsDataThrottler + { + + private volatile int currenttime = 0; + private volatile int lastTimeElapsed = 0; + private volatile int BytesSent = 0; + private int oversizedImages = 0; + public CapsDataThrottler(int pBytes, int max, int min) + { + ThrottleBytes = pBytes; + lastTimeElapsed = Util.EnvironmentTickCount(); + } + public bool hasEvents(UUID key, Dictionary responses) + { + PassTime(); + // Note, this is called IN LOCK + bool haskey = responses.ContainsKey(key); + if (!haskey) + { + return false; + } + GetTextureModule.aPollResponse response; + if (responses.TryGetValue(key,out response)) + { + + // Normal + if (BytesSent + response.bytes <= ThrottleBytes) + { + BytesSent += response.bytes; + //TimeBasedAction timeBasedAction = new TimeBasedAction { byteRemoval = response.bytes, requestId = key, timeMS = currenttime + 1000, unlockyn = false }; + //m_actions.Add(timeBasedAction); + return true; + } + // Big textures + else if (response.bytes > ThrottleBytes && oversizedImages <= ((ThrottleBytes%50000) + 1)) + { + Interlocked.Increment(ref oversizedImages); + BytesSent += response.bytes; + //TimeBasedAction timeBasedAction = new TimeBasedAction { byteRemoval = response.bytes, requestId = key, timeMS = currenttime + (((response.bytes % ThrottleBytes)+1)*1000) , unlockyn = false }; + //m_actions.Add(timeBasedAction); + return true; + } + else + { + return false; + } + } + + return haskey; + } + public void ProcessTime() + { + PassTime(); + } + + + private void PassTime() + { + currenttime = Util.EnvironmentTickCount(); + int timeElapsed = Util.EnvironmentTickCountSubtract(currenttime, lastTimeElapsed); + //processTimeBasedActions(responses); + if (Util.EnvironmentTickCountSubtract(currenttime, timeElapsed) >= 1000) + { + lastTimeElapsed = Util.EnvironmentTickCount(); + BytesSent -= ThrottleBytes; + if (BytesSent < 0) BytesSent = 0; + if (BytesSent < ThrottleBytes) + { + oversizedImages = 0; + } + } + } + public int ThrottleBytes; + } } -- cgit v1.1