From 3a04d706c9ebd13214e62fb944b4a8da6860bb91 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 8 Oct 2009 17:34:51 -0700 Subject: Fear the lockless LLUDP implementation! --- .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 252 +++++---------------- 1 file changed, 60 insertions(+), 192 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index dbb9861..06fa3e2 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -30,6 +30,7 @@ using System.Collections.Generic; using System.Net; using OpenSim.Framework; using OpenMetaverse; +using BclExtras.Collections; using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim; @@ -37,246 +38,113 @@ namespace OpenSim.Region.ClientStack.LindenUDP { public sealed class UDPClientCollection { - Dictionary Dictionary1; - Dictionary Dictionary2; - LLUDPClient[] Array; - ReaderWriterLockImpl rwLock = new ReaderWriterLockImpl(); - object m_sync = new object(); + #region IComparers - public UDPClientCollection() - { - Dictionary1 = new Dictionary(); - Dictionary2 = new Dictionary(); - Array = new LLUDPClient[0]; - } - - public UDPClientCollection(int capacity) + private sealed class UUIDComparer : IComparer { - Dictionary1 = new Dictionary(capacity); - Dictionary2 = new Dictionary(capacity); - Array = new LLUDPClient[0]; + public int Compare(UUID x, UUID y) + { + return x.Guid.CompareTo(y.Guid); + } } - public void Add(UUID key1, IPEndPoint key2, LLUDPClient value) + private sealed class IPEndPointComparer : IComparer { - //rwLock.EnterWriteLock(); - - //try - //{ - // if (Dictionary1.ContainsKey(key1)) - // { - // if (!Dictionary2.ContainsKey(key2)) - // throw new ArgumentException("key1 exists in the dictionary but not key2"); - // } - // else if (Dictionary2.ContainsKey(key2)) - // { - // if (!Dictionary1.ContainsKey(key1)) - // throw new ArgumentException("key2 exists in the dictionary but not key1"); - // } - - // Dictionary1[key1] = value; - // Dictionary2[key2] = value; - - // LLUDPClient[] oldArray = Array; - // int oldLength = oldArray.Length; - - // LLUDPClient[] newArray = new LLUDPClient[oldLength + 1]; - // for (int i = 0; i < oldLength; i++) - // newArray[i] = oldArray[i]; - // newArray[oldLength] = value; - - // Array = newArray; - //} - //finally { rwLock.ExitWriteLock(); } - - lock (m_sync) + public int Compare(IPEndPoint x, IPEndPoint y) { - if (Dictionary1.ContainsKey(key1)) - { - if (!Dictionary2.ContainsKey(key2)) - throw new ArgumentException("key1 exists in the dictionary but not key2"); - } - else if (Dictionary2.ContainsKey(key2)) - { - if (!Dictionary1.ContainsKey(key1)) - throw new ArgumentException("key2 exists in the dictionary but not key1"); - } - - Dictionary1[key1] = value; - Dictionary2[key2] = value; - - LLUDPClient[] oldArray = Array; - int oldLength = oldArray.Length; + int result = x.Address.Address.CompareTo(y.Address.Address); + if (result == 0) result = x.Port.CompareTo(y.Port); + return result; + } + } - LLUDPClient[] newArray = new LLUDPClient[oldLength + 1]; - for (int i = 0; i < oldLength; i++) - newArray[i] = oldArray[i]; - newArray[oldLength] = value; + #endregion IComparers - Array = newArray; - } + private ImmutableMap m_dict1; + private ImmutableMap m_dict2; + private LLUDPClient[] m_array; + public UDPClientCollection() + { + m_dict1 = new ImmutableMap(new UUIDComparer()); + m_dict2 = new ImmutableMap(new IPEndPointComparer()); + m_array = new LLUDPClient[0]; } - public bool Remove(UUID key1, IPEndPoint key2) + public void Add(UUID key1, IPEndPoint key2, LLUDPClient value) { - //rwLock.EnterWriteLock(); - - //try - //{ - // LLUDPClient value; - // if (Dictionary1.TryGetValue(key1, out value)) - // { - // Dictionary1.Remove(key1); - // Dictionary2.Remove(key2); + m_dict1 = m_dict1.Add(key1, value); + m_dict2 = m_dict2.Add(key2, value); + + // Copy the array by hand + LLUDPClient[] oldArray = m_array; + int oldLength = oldArray.Length; + LLUDPClient[] newArray = new LLUDPClient[oldLength + 1]; + + for (int i = 0; i < oldLength; i++) + newArray[i] = oldArray[i]; + newArray[oldLength] = value; + + m_array = newArray; + } - // LLUDPClient[] oldArray = Array; - // int oldLength = oldArray.Length; + public void Remove(UUID key1, IPEndPoint key2) + { + m_dict1 = m_dict1.Delete(key1); + m_dict2 = m_dict2.Delete(key2); - // LLUDPClient[] newArray = new LLUDPClient[oldLength - 1]; - // int j = 0; - // for (int i = 0; i < oldLength; i++) - // { - // if (oldArray[i] != value) - // newArray[j++] = oldArray[i]; - // } + LLUDPClient[] oldArray = m_array; + int oldLength = oldArray.Length; - // Array = newArray; - // return true; - // } - //} - //finally { rwLock.ExitWriteLock(); } + // Copy the array by hand - //return false; + LLUDPClient[] newArray = new LLUDPClient[oldLength - 1]; + int j = 0; - lock (m_sync) + for (int i = 0; i < oldLength; i++) { - LLUDPClient value; - if (Dictionary1.TryGetValue(key1, out value)) - { - Dictionary1.Remove(key1); - Dictionary2.Remove(key2); - - LLUDPClient[] oldArray = Array; - int oldLength = oldArray.Length; - - LLUDPClient[] newArray = new LLUDPClient[oldLength - 1]; - int j = 0; - for (int i = 0; i < oldLength; i++) - { - if (oldArray[i] != value) - newArray[j++] = oldArray[i]; - } - - Array = newArray; - return true; - } + if (oldArray[i].AgentID != key1) + newArray[j++] = oldArray[i]; } - return false; - + m_array = newArray; } public void Clear() { - //rwLock.EnterWriteLock(); - - //try - //{ - // Dictionary1.Clear(); - // Dictionary2.Clear(); - // Array = new LLUDPClient[0]; - //} - //finally { rwLock.ExitWriteLock(); } - - lock (m_sync) - { - Dictionary1.Clear(); - Dictionary2.Clear(); - Array = new LLUDPClient[0]; - } - + m_dict1 = new ImmutableMap(new UUIDComparer()); + m_dict2 = new ImmutableMap(new IPEndPointComparer()); + m_array = new LLUDPClient[0]; } public int Count { - get { return Array.Length; } + get { return m_array.Length; } } public bool ContainsKey(UUID key) { - return Dictionary1.ContainsKey(key); + return m_dict1.ContainsKey(key); } public bool ContainsKey(IPEndPoint key) { - return Dictionary2.ContainsKey(key); + return m_dict2.ContainsKey(key); } public bool TryGetValue(UUID key, out LLUDPClient value) { - ////bool success; - ////bool doLock = !rwLock.IsUpgradeableReadLockHeld; - ////if (doLock) rwLock.EnterReadLock(); - - ////try { success = Dictionary1.TryGetValue(key, out value); } - ////finally { if (doLock) rwLock.ExitReadLock(); } - - ////return success; - - lock (m_sync) - return Dictionary1.TryGetValue(key, out value); - - //try - //{ - // return Dictionary1.TryGetValue(key, out value); - //} - //catch { } - //value = null; - //return false; + return m_dict1.TryGetValue(key, out value); } public bool TryGetValue(IPEndPoint key, out LLUDPClient value) { - ////bool success; - ////bool doLock = !rwLock.IsUpgradeableReadLockHeld; - ////if (doLock) rwLock.EnterReadLock(); - - ////try { success = Dictionary2.TryGetValue(key, out value); } - ////finally { if (doLock) rwLock.ExitReadLock(); } - - ////return success; - - lock (m_sync) - return Dictionary2.TryGetValue(key, out value); - - //try - //{ - // return Dictionary2.TryGetValue(key, out value); - //} - //catch { } - //value = null; - //return false; - + return m_dict2.TryGetValue(key, out value); } public void ForEach(Action action) { - //bool doLock = !rwLock.IsUpgradeableReadLockHeld; - //if (doLock) rwLock.EnterUpgradeableReadLock(); - - //try { Parallel.ForEach(Array, action); } - //finally { if (doLock) rwLock.ExitUpgradeableReadLock(); } - - LLUDPClient[] localArray = null; - lock (m_sync) - { - localArray = new LLUDPClient[Array.Length]; - Array.CopyTo(localArray, 0); - } - - Parallel.ForEach(localArray, action); - + Parallel.ForEach(m_array, action); } } } -- cgit v1.1 From 56a27c37d3e84495988e423be7b52007cea595cc Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Thu, 8 Oct 2009 21:51:53 -0700 Subject: Simplified LLUDPClientCollection from three collections down to one. This will prevent any potential problems from inconsistency between the internal collections --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 3 +- .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 75 ++++------------------ .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 46 +++++-------- 3 files changed, 29 insertions(+), 95 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 84e705a..139dc3b 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -119,6 +119,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Properties + public LLUDPClient UDPClient { get { return m_udpClient; } } public UUID SecureSessionId { get { return m_secureSessionId; } } public IScene Scene { get { return m_scene; } } public UUID SessionId { get { return m_sessionId; } } @@ -504,7 +505,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP + "Any further actions taken will not be processed.\n" + "Please relog", true); - m_udpServer.SendPacket(m_agentId, packet, ThrottleOutPacketType.Unknown, false); + OutPacket(packet, ThrottleOutPacketType.Unknown); // There may be a better way to do this. Perhaps kick? Not sure this propogates notifications to // listeners yet, though. diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index 06fa3e2..abf3882 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -40,14 +40,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP { #region IComparers - private sealed class UUIDComparer : IComparer - { - public int Compare(UUID x, UUID y) - { - return x.Guid.CompareTo(y.Guid); - } - } - private sealed class IPEndPointComparer : IComparer { public int Compare(IPEndPoint x, IPEndPoint y) @@ -60,91 +52,46 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion IComparers - private ImmutableMap m_dict1; - private ImmutableMap m_dict2; - private LLUDPClient[] m_array; + private ImmutableMap m_dict; public UDPClientCollection() { - m_dict1 = new ImmutableMap(new UUIDComparer()); - m_dict2 = new ImmutableMap(new IPEndPointComparer()); - m_array = new LLUDPClient[0]; + m_dict = new ImmutableMap(new IPEndPointComparer()); } - public void Add(UUID key1, IPEndPoint key2, LLUDPClient value) + public void Add(IPEndPoint key, LLUDPClient value) { - m_dict1 = m_dict1.Add(key1, value); - m_dict2 = m_dict2.Add(key2, value); - - // Copy the array by hand - LLUDPClient[] oldArray = m_array; - int oldLength = oldArray.Length; - LLUDPClient[] newArray = new LLUDPClient[oldLength + 1]; - - for (int i = 0; i < oldLength; i++) - newArray[i] = oldArray[i]; - newArray[oldLength] = value; - - m_array = newArray; + m_dict = m_dict.Add(key, value); } - public void Remove(UUID key1, IPEndPoint key2) + public void Remove(IPEndPoint key) { - m_dict1 = m_dict1.Delete(key1); - m_dict2 = m_dict2.Delete(key2); - - LLUDPClient[] oldArray = m_array; - int oldLength = oldArray.Length; - - // Copy the array by hand - - LLUDPClient[] newArray = new LLUDPClient[oldLength - 1]; - int j = 0; - - for (int i = 0; i < oldLength; i++) - { - if (oldArray[i].AgentID != key1) - newArray[j++] = oldArray[i]; - } - - m_array = newArray; + m_dict = m_dict.Delete(key); } public void Clear() { - m_dict1 = new ImmutableMap(new UUIDComparer()); - m_dict2 = new ImmutableMap(new IPEndPointComparer()); - m_array = new LLUDPClient[0]; + m_dict = new ImmutableMap(new IPEndPointComparer()); } public int Count { - get { return m_array.Length; } - } - - public bool ContainsKey(UUID key) - { - return m_dict1.ContainsKey(key); + get { return m_dict.Count; } } public bool ContainsKey(IPEndPoint key) { - return m_dict2.ContainsKey(key); - } - - public bool TryGetValue(UUID key, out LLUDPClient value) - { - return m_dict1.TryGetValue(key, out value); + return m_dict.ContainsKey(key); } public bool TryGetValue(IPEndPoint key, out LLUDPClient value) { - return m_dict2.TryGetValue(key, out value); + return m_dict.TryGetValue(key, out value); } public void ForEach(Action action) { - Parallel.ForEach(m_array, action); + Parallel.ForEach(m_dict.Values, action); } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 1e6927f..a6aa048 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -181,22 +181,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP return x == m_location; } - public void RemoveClient(IClientAPI client) + public void RemoveClient(LLUDPClient udpClient) { - m_scene.ClientManager.Remove(client.CircuitCode); - client.Close(false); + m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + udpClient.ClientAPI.Name); - LLUDPClient udpClient; - if (clients.TryGetValue(client.AgentId, out udpClient)) - { - m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + client.Name); - udpClient.Shutdown(); - clients.Remove(client.AgentId, udpClient.RemoteEndPoint); - } - else - { - m_log.Warn("[LLUDPSERVER]: Failed to remove LLUDPClient for " + client.Name); - } + m_scene.ClientManager.Remove(udpClient.CircuitCode); + udpClient.ClientAPI.Close(false); + udpClient.Shutdown(); + clients.Remove(udpClient.RemoteEndPoint); } public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) @@ -230,15 +222,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public void SendPacket(UUID agentID, Packet packet, ThrottleOutPacketType category, bool allowSplitting) - { - LLUDPClient client; - if (clients.TryGetValue(agentID, out client)) - SendPacket(client, packet, category, allowSplitting); - else - m_log.Warn("[LLUDPSERVER]: Attempted to send a packet to unknown agentID " + agentID); - } - public void SendPacket(LLUDPClient client, Packet packet, ThrottleOutPacketType category, bool allowSplitting) { // CoarseLocationUpdate packets cannot be split in an automated way @@ -391,7 +374,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + client.ClientAPI.Name); - RemoveClient(client.ClientAPI); + RemoveClient(client); return; } } @@ -647,23 +630,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void AddClient(uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo) { // Create the LLUDPClient - LLUDPClient client = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint); + LLUDPClient udpClient = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint); // Create the LLClientView - LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, client, sessionInfo, agentID, sessionID, circuitCode); + LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); clientApi.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler; clientApi.OnLogout += LogoutHandler; - clientApi.OnConnectionClosed += RemoveClient; + clientApi.OnConnectionClosed += + delegate(IClientAPI client) + { if (client is LLClientView) RemoveClient(((LLClientView)client).UDPClient); }; // Start the IClientAPI m_scene.ClientManager.Add(circuitCode, clientApi); clientApi.Start(); // Give LLUDPClient a reference to IClientAPI - client.ClientAPI = clientApi; + udpClient.ClientAPI = clientApi; // Add the new client to our list of tracked clients - clients.Add(agentID, client.RemoteEndPoint, client); + clients.Add(udpClient.RemoteEndPoint, udpClient); } private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend) @@ -798,7 +783,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void LogoutHandler(IClientAPI client) { client.SendLogoutPacket(); - RemoveClient(client); + if (client is LLClientView) + RemoveClient(((LLClientView)client).UDPClient); } } } -- cgit v1.1 From a5b9971fd77c0c4bf70656be7f3e7999f59d9f85 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 9 Oct 2009 01:53:06 -0700 Subject: * Added a lock object for the write functions in LLUDPClientCollection (immutable != concurrent write safety) * Allow the UDP server to bind to a user-specified port again * Updated to a newer version of OpenSimUDPBase that streamlines the code even more. This also reintroduces the highly concurrent packet handling which needs more testing --- .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 56 +++- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 20 +- .../Region/ClientStack/LindenUDP/OpenSimUDPBase.cs | 303 ++++++++------------- 3 files changed, 169 insertions(+), 210 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index abf3882..2222a33 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -36,6 +36,9 @@ using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim; namespace OpenSim.Region.ClientStack.LindenUDP { + /// + /// A thread safe mapping from endpoints to client references + /// public sealed class UDPClientCollection { #region IComparers @@ -52,43 +55,80 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion IComparers + /// An immutable dictionary mapping from + /// to references private ImmutableMap m_dict; + /// Immutability grants thread safety for concurrent reads and + /// read-writes, but not concurrent writes + private object m_writeLock; + /// Number of clients in the collection + public int Count { get { return m_dict.Count; } } + + /// + /// Default constructor + /// public UDPClientCollection() { m_dict = new ImmutableMap(new IPEndPointComparer()); } + /// + /// Add a client reference to the collection + /// + /// Remote endpoint of the client + /// Reference to the client object public void Add(IPEndPoint key, LLUDPClient value) { - m_dict = m_dict.Add(key, value); + lock (m_writeLock) + m_dict = m_dict.Add(key, value); } + /// + /// Remove a client from the collection + /// + /// Remote endpoint of the client public void Remove(IPEndPoint key) { - m_dict = m_dict.Delete(key); + lock (m_writeLock) + m_dict = m_dict.Delete(key); } + /// + /// Resets the client collection + /// public void Clear() { - m_dict = new ImmutableMap(new IPEndPointComparer()); - } - - public int Count - { - get { return m_dict.Count; } + lock (m_writeLock) + m_dict = new ImmutableMap(new IPEndPointComparer()); } + /// + /// Checks if an endpoint is in the collection + /// + /// Endpoint to check for + /// True if the endpoint was found in the collection, otherwise false public bool ContainsKey(IPEndPoint key) { return m_dict.ContainsKey(key); } + /// + /// Attempts to fetch a value out of the collection + /// + /// Endpoint of the client to retrieve + /// Retrieved client, or null on lookup failure + /// True if the lookup succeeded, otherwise false public bool TryGetValue(IPEndPoint key, out LLUDPClient value) { return m_dict.TryGetValue(key, out value); } + /// + /// Performs a given task in parallel for each of the elements in the + /// collection + /// + /// Action to perform on each element public void ForEach(Action action) { Parallel.ForEach(m_dict.Values, action); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index a6aa048..9aeea9a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -96,7 +96,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Incoming packets that are awaiting handling private OpenMetaverse.BlockingQueue packetInbox = new OpenMetaverse.BlockingQueue(); /// - private UDPClientCollection clients = new UDPClientCollection(); + private UDPClientCollection m_clients = new UDPClientCollection(); /// Bandwidth throttle for this UDP server private TokenBucket m_throttle; /// Bandwidth throttle rates for this UDP server @@ -115,7 +115,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public Socket Server { get { return null; } } public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) - : base((int)port) + : base(listenIP, (int)port) { #region Environment.TickCount Measurement @@ -143,7 +143,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public new void Start() { if (m_scene == null) - throw new InvalidOperationException("Cannot LLUDPServer.Start() without an IScene reference"); + throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); base.Start(); @@ -188,7 +188,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene.ClientManager.Remove(udpClient.CircuitCode); udpClient.ClientAPI.Close(false); udpClient.Shutdown(); - clients.Remove(udpClient.RemoteEndPoint); + m_clients.Remove(udpClient.RemoteEndPoint); } public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) @@ -208,7 +208,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < packetCount; i++) { byte[] data = datas[i]; - clients.ForEach( + m_clients.ForEach( delegate(LLUDPClient client) { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); }); } @@ -216,7 +216,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP else { byte[] data = packet.ToBytes(); - clients.ForEach( + m_clients.ForEach( delegate(LLUDPClient client) { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); }); } @@ -502,7 +502,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } // Determine which agent this packet came from - if (!clients.TryGetValue(address, out client)) + if (!m_clients.TryGetValue(address, out client)) { m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address); return; @@ -606,7 +606,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_scene.RegionStatus != RegionStatus.SlaveScene) { AuthenticateResponse sessionInfo; - bool isNewCircuit = !clients.ContainsKey(remoteEndPoint); + bool isNewCircuit = !m_clients.ContainsKey(remoteEndPoint); if (!IsClientAuthorized(useCircuitCode, out sessionInfo)) { @@ -648,7 +648,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP udpClient.ClientAPI = clientApi; // Add the new client to our list of tracked clients - clients.Add(udpClient.RemoteEndPoint, udpClient); + m_clients.Add(udpClient.RemoteEndPoint, udpClient); } private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend) @@ -726,7 +726,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP elapsed500MS = 0; } - clients.ForEach( + m_clients.ForEach( delegate(LLUDPClient client) { if (client.DequeueOutgoing()) diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs index 218aaac..9b1751d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs @@ -29,101 +29,90 @@ using System; using System.Net; using System.Net.Sockets; using System.Threading; -using OpenMetaverse; +using log4net; -namespace OpenSim.Region.ClientStack.LindenUDP +namespace OpenMetaverse { /// - /// + /// Base UDP server /// public abstract class OpenSimUDPBase { - // these abstract methods must be implemented in a derived class to actually do - // something with the packets that are sent and received. + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// This method is called when an incoming packet is received + /// + /// Incoming packet buffer protected abstract void PacketReceived(UDPPacketBuffer buffer); + + /// + /// This method is called when an outgoing packet is sent + /// + /// Outgoing packet buffer + /// Number of bytes written to the wire protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent); - // the port to listen on - internal int udpPort; - - // the UDP socket - private Socket udpSocket; + /// UDP port to bind to in server mode + protected int m_udpPort; - // the ReaderWriterLock is used solely for the purposes of shutdown (Stop()). - // since there are potentially many "reader" threads in the internal .NET IOCP - // thread pool, this is a cheaper synchronization primitive than using - // a Mutex object. This allows many UDP socket "reads" concurrently - when - // Stop() is called, it attempts to obtain a writer lock which will then - // wait until all outstanding operations are completed before shutting down. - // this avoids the problem of closing the socket with outstanding operations - // and trying to catch the inevitable ObjectDisposedException. - private ReaderWriterLock rwLock = new ReaderWriterLock(); + /// Local IP address to bind to in server mode + protected IPAddress m_localBindAddress; - // number of outstanding operations. This is a reference count - // which we use to ensure that the threads exit cleanly. Note that - // we need this because the threads will potentially still need to process - // data even after the socket is closed. - private int rwOperationCount = 0; + /// UDP socket, used in either client or server mode + private Socket m_udpSocket; - // the all important shutdownFlag. This is synchronized through the ReaderWriterLock. - private volatile bool shutdownFlag = true; - - // the remote endpoint to communicate with - protected IPEndPoint remoteEndPoint = null; + /// The all important shutdown flag + private volatile bool m_shutdownFlag = true; + /// Returns true if the server is currently listening, otherwise false + public bool IsRunning { get { return !m_shutdownFlag; } } /// - /// Initialize the UDP packet handler in server mode + /// Default constructor /// + /// Local IP address to bind the server to /// Port to listening for incoming UDP packets on - public OpenSimUDPBase(int port) - { - udpPort = port; - } - - /// - /// Initialize the UDP packet handler in client mode - /// - /// Remote UDP server to connect to - public OpenSimUDPBase(IPEndPoint endPoint) + public OpenSimUDPBase(IPAddress bindAddress, int port) { - remoteEndPoint = endPoint; - udpPort = 0; + m_localBindAddress = bindAddress; + m_udpPort = port; } /// - /// + /// Start the UDP server /// + /// This method will attempt to set the SIO_UDP_CONNRESET flag + /// on the socket to get newer versions of Windows to behave in a sane + /// manner (not throwing an exception when the remote side resets the + /// connection). This call is ignored on Mono where the flag is not + /// necessary public void Start() { - if (shutdownFlag) + if (m_shutdownFlag) { - if (remoteEndPoint == null) - { - // Server mode + const int SIO_UDP_CONNRESET = -1744830452; - // create and bind the socket - IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort); - udpSocket = new Socket( - AddressFamily.InterNetwork, - SocketType.Dgram, - ProtocolType.Udp); - udpSocket.Bind(ipep); + IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); + m_udpSocket = new Socket( + AddressFamily.InterNetwork, + SocketType.Dgram, + ProtocolType.Udp); + try + { + // this udp socket flag is not supported under mono, + // so we'll catch the exception and continue + m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); + m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag set"); } - else + catch (SocketException) { - // Client mode - IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort); - udpSocket = new Socket( - AddressFamily.InterNetwork, - SocketType.Dgram, - ProtocolType.Udp); - udpSocket.Bind(ipep); - //udpSocket.Connect(remoteEndPoint); + m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring"); } + m_udpSocket.Bind(ipep); // we're not shutting down, we're starting up - shutdownFlag = false; + m_shutdownFlag = false; // kick off an async receive. The Start() method will return, the // actual receives will occur asynchronously and will be caught in @@ -133,104 +122,85 @@ namespace OpenSim.Region.ClientStack.LindenUDP } /// - /// + /// Stops the UDP server /// public void Stop() { - if (!shutdownFlag) + if (!m_shutdownFlag) { // wait indefinitely for a writer lock. Once this is called, the .NET runtime // will deny any more reader locks, in effect blocking all other send/receive // threads. Once we have the lock, we set shutdownFlag to inform the other // threads that the socket is closed. - rwLock.AcquireWriterLock(-1); - shutdownFlag = true; - udpSocket.Close(); - rwLock.ReleaseWriterLock(); - - // wait for any pending operations to complete on other - // threads before exiting. - const int FORCE_STOP = 100; - int i = 0; - while (rwOperationCount > 0 && i < FORCE_STOP) - { - Thread.Sleep(10); - ++i; - } - - if (i >= FORCE_STOP) - { - Logger.Log("UDPBase.Stop() forced shutdown while waiting on pending operations", - Helpers.LogLevel.Warning); - } + m_shutdownFlag = true; + m_udpSocket.Close(); } } - /// - /// - /// - public bool IsRunning - { - get { return !shutdownFlag; } - } - private void AsyncBeginReceive() { - // this method actually kicks off the async read on the socket. - // we aquire a reader lock here to ensure that no other thread - // is trying to set shutdownFlag and close the socket. - rwLock.AcquireReaderLock(-1); + // allocate a packet buffer + //WrappedObject wrappedBuffer = Pool.CheckOut(); + UDPPacketBuffer buf = new UDPPacketBuffer(); - if (!shutdownFlag) + if (!m_shutdownFlag) { - // increment the count of pending operations - Interlocked.Increment(ref rwOperationCount); - - // allocate a packet buffer - //WrappedObject wrappedBuffer = Pool.CheckOut(); - UDPPacketBuffer buf = new UDPPacketBuffer(); - try { // kick off an async read - udpSocket.BeginReceiveFrom( + m_udpSocket.BeginReceiveFrom( //wrappedBuffer.Instance.Data, buf.Data, 0, UDPPacketBuffer.BUFFER_SIZE, SocketFlags.None, - //ref wrappedBuffer.Instance.RemoteEndPoint, ref buf.RemoteEndPoint, - new AsyncCallback(AsyncEndReceive), + AsyncEndReceive, //wrappedBuffer); buf); } - catch (SocketException) + catch (SocketException e) { - // something bad happened - //Logger.Log( - // "A SocketException occurred in UDPServer.AsyncBeginReceive()", - // Helpers.LogLevel.Error, se); - - // an error occurred, therefore the operation is void. Decrement the reference count. - Interlocked.Decrement(ref rwOperationCount); + if (e.SocketErrorCode == SocketError.ConnectionReset) + { + m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort); + bool salvaged = false; + while (!salvaged) + { + try + { + m_udpSocket.BeginReceiveFrom( + //wrappedBuffer.Instance.Data, + buf.Data, + 0, + UDPPacketBuffer.BUFFER_SIZE, + SocketFlags.None, + ref buf.RemoteEndPoint, + AsyncEndReceive, + //wrappedBuffer); + buf); + salvaged = true; + } + catch (SocketException) { } + catch (ObjectDisposedException) { return; } + } + + m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); + } } + catch (ObjectDisposedException) { } } - - // we're done with the socket for now, release the reader lock. - rwLock.ReleaseReaderLock(); } private void AsyncEndReceive(IAsyncResult iar) { // Asynchronous receive operations will complete here through the call // to AsyncBeginReceive - - // aquire a reader lock - rwLock.AcquireReaderLock(-1); - - if (!shutdownFlag) + if (!m_shutdownFlag) { + // start another receive - this keeps the server going! + AsyncBeginReceive(); + // get the buffer that was created in AsyncBeginReceive // this is the received data //WrappedObject wrappedBuffer = (WrappedObject)iar.AsyncState; @@ -241,100 +211,49 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // get the length of data actually read from the socket, store it with the // buffer - buffer.DataLength = udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); - - // this operation is now complete, decrement the reference count - Interlocked.Decrement(ref rwOperationCount); - - // we're done with the socket, release the reader lock - rwLock.ReleaseReaderLock(); + buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); // call the abstract method PacketReceived(), passing the buffer that // has just been filled from the socket read. PacketReceived(buffer); } - catch (SocketException) - { - // an error occurred, therefore the operation is void. Decrement the reference count. - Interlocked.Decrement(ref rwOperationCount); - - // we're done with the socket for now, release the reader lock. - rwLock.ReleaseReaderLock(); - } - finally - { - // start another receive - this keeps the server going! - AsyncBeginReceive(); - - //wrappedBuffer.Dispose(); - } - } - else - { - // nothing bad happened, but we are done with the operation - // decrement the reference count and release the reader lock - Interlocked.Decrement(ref rwOperationCount); - rwLock.ReleaseReaderLock(); + catch (SocketException) { } + catch (ObjectDisposedException) { } + //finally { wrappedBuffer.Dispose(); } } } public void AsyncBeginSend(UDPPacketBuffer buf) { - rwLock.AcquireReaderLock(-1); - - if (!shutdownFlag) + if (!m_shutdownFlag) { try { - Interlocked.Increment(ref rwOperationCount); - udpSocket.BeginSendTo( + m_udpSocket.BeginSendTo( buf.Data, 0, buf.DataLength, SocketFlags.None, buf.RemoteEndPoint, - new AsyncCallback(AsyncEndSend), + AsyncEndSend, buf); } - catch (SocketException) - { - //Logger.Log( - // "A SocketException occurred in UDPServer.AsyncBeginSend()", - // Helpers.LogLevel.Error, se); - } + catch (SocketException) { } + catch (ObjectDisposedException) { } } - - rwLock.ReleaseReaderLock(); } - private void AsyncEndSend(IAsyncResult iar) + void AsyncEndSend(IAsyncResult result) { - rwLock.AcquireReaderLock(-1); - - if (!shutdownFlag) + try { - UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; + UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; + int bytesSent = m_udpSocket.EndSendTo(result); - try - { - int bytesSent = udpSocket.EndSendTo(iar); - - // note that call to the abstract PacketSent() method - we are passing the number - // of bytes sent in a separate parameter, since we can't use buffer.DataLength which - // is the number of bytes to send (or bytes received depending upon whether this - // buffer was part of a send or a receive). - PacketSent(buffer, bytesSent); - } - catch (SocketException) - { - //Logger.Log( - // "A SocketException occurred in UDPServer.AsyncEndSend()", - // Helpers.LogLevel.Error, se); - } + PacketSent(buf, bytesSent); } - - Interlocked.Decrement(ref rwOperationCount); - rwLock.ReleaseReaderLock(); + catch (SocketException) { } + catch (ObjectDisposedException) { } } } } -- cgit v1.1 From a3e31cdeafbcfd371291213413b6d0d97a71c13b Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 9 Oct 2009 02:13:21 -0700 Subject: Added more debugging output to the "unrecognized source" warning --- OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 9aeea9a..7052e0e 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -504,7 +504,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Determine which agent this packet came from if (!m_clients.TryGetValue(address, out client)) { - m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address); + m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + + ", currently tracking " + m_clients.Count + " clients"); return; } -- cgit v1.1 From 76a5cae0b4170b191e370a663bb6355b4dc38fa2 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 9 Oct 2009 02:18:25 -0700 Subject: Forgot to initialize m_writeLock --- OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index 2222a33..2972d46 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -60,7 +60,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private ImmutableMap m_dict; /// Immutability grants thread safety for concurrent reads and /// read-writes, but not concurrent writes - private object m_writeLock; + private object m_writeLock = new object(); /// Number of clients in the collection public int Count { get { return m_dict.Count; } } -- cgit v1.1 From 23586b69a16c75b8c7cc9a99d1a7693686285ff7 Mon Sep 17 00:00:00 2001 From: Melanie Date: Fri, 9 Oct 2009 12:17:55 +0100 Subject: Slow down the packet receiving code again after new reports of thread storms. --- OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs index 9b1751d..e78a4fe 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs @@ -198,9 +198,6 @@ namespace OpenMetaverse // to AsyncBeginReceive if (!m_shutdownFlag) { - // start another receive - this keeps the server going! - AsyncBeginReceive(); - // get the buffer that was created in AsyncBeginReceive // this is the received data //WrappedObject wrappedBuffer = (WrappedObject)iar.AsyncState; @@ -219,7 +216,14 @@ namespace OpenMetaverse } catch (SocketException) { } catch (ObjectDisposedException) { } - //finally { wrappedBuffer.Dispose(); } + finally + { + // wrappedBuffer.Dispose(); + + // start another receive - this keeps the server going! + AsyncBeginReceive(); + } + } } -- cgit v1.1 From 29543514e6a8ad91a90e244a9488e9d0408f45bf Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Fri, 9 Oct 2009 16:33:50 -0700 Subject: * Changed the "Packet exceeded buffer size" log line to debug and include the packet type. This message is normal, but could be evidence of a message marked for zerocoding that probably shouldn't be * Changing OpenSimUDPBase back to high concurrency. High concurrency mode seems to make other problems happen faster, so it's helpful for working out other bugs and will probably --- OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 2 +- OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 7052e0e..fcb2cd0 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -268,7 +268,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // The packet grew larger than the bufferSize while zerocoding. // Remove the MSG_ZEROCODED flag and send the unencoded data // instead - m_log.Info("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding. Removing MSG_ZEROCODED flag"); + m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". Removing MSG_ZEROCODED flag"); data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs index e78a4fe..fad2ea8 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs @@ -198,6 +198,9 @@ namespace OpenMetaverse // to AsyncBeginReceive if (!m_shutdownFlag) { + // start another receive - this keeps the server going! + AsyncBeginReceive(); + // get the buffer that was created in AsyncBeginReceive // this is the received data //WrappedObject wrappedBuffer = (WrappedObject)iar.AsyncState; @@ -216,13 +219,7 @@ namespace OpenMetaverse } catch (SocketException) { } catch (ObjectDisposedException) { } - finally - { - // wrappedBuffer.Dispose(); - - // start another receive - this keeps the server going! - AsyncBeginReceive(); - } + //finally { wrappedBuffer.Dispose(); } } } -- cgit v1.1 From c0beeb929e22509329781cdf85f7a5d90c4b0e36 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 12 Oct 2009 17:00:01 -0700 Subject: * Fixes http://opensimulator.org/mantis/view.php?id=4225 * Fixes http://opensimulator.org/mantis/view.php?id=3959 * Allows for viewing inventory textures outside home grid --- OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 14 ++++++++++++++ .../Region/ClientStack/LindenUDP/LLClientView.cs | 21 ++++++++++++++------- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 5 +++++ 3 files changed, 33 insertions(+), 7 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index 5877779..1bbe00f 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs @@ -31,6 +31,7 @@ using OpenMetaverse; using OpenMetaverse.Imaging; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes.Hypergrid; using OpenSim.Services.Interfaces; using log4net; using System.Reflection; @@ -54,6 +55,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public UUID TextureID; public IJ2KDecoder J2KDecoder; public IAssetService AssetService; + public UUID AgentID; + public IHyperAssetService HyperAssets; public OpenJPEG.J2KLayerInfo[] Layers; public bool IsDecoded; public bool HasAsset; @@ -370,6 +373,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP UUID assetID = UUID.Zero; if (asset != null) assetID = asset.FullID; + else if (HyperAssets != null) + { + // Try the user's inventory, but only if it's different from the regions' + string userAssets = HyperAssets.GetUserAssetServer(AgentID); + if ((userAssets != string.Empty) && (userAssets != HyperAssets.GetSimAssetServer())) + { + m_log.DebugFormat("[J2KIMAGE]: texture {0} not found in local asset storage. Trying user's storage.", id); + AssetService.Get(userAssets + "/" + id, this, AssetReceived); + return; + } + } AssetDataCallback(assetID, asset); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 84e705a..25eb5cd 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -43,6 +43,7 @@ using OpenSim.Framework.Communications.Cache; using OpenSim.Framework.Statistics; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Scenes.Hypergrid; using OpenSim.Services.Interfaces; using Timer=System.Timers.Timer; using AssetLandmark = OpenSim.Framework.AssetLandmark; @@ -117,6 +118,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected int m_packetMTU = 1400; protected IAssetService m_assetService; + #region Properties public UUID SecureSessionId { get { return m_secureSessionId; } } @@ -7013,7 +7015,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion //handlerTextureRequest = null; - for (int i = 0; i < imageRequest.RequestImage.Length; i++) { if (OnRequestTexture != null) @@ -7024,7 +7025,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP args.PacketNumber = imageRequest.RequestImage[i].Packet; args.Priority = imageRequest.RequestImage[i].DownloadPriority; args.requestSequence = imageRequest.Header.Sequence; - //handlerTextureRequest = OnRequestTexture; //if (handlerTextureRequest != null) @@ -7047,10 +7047,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Validate inventory transfers // Has to be done here, because AssetCache can't do it // - + UUID taskID = UUID.Zero; if (transfer.TransferInfo.SourceType == 3) { - UUID taskID = new UUID(transfer.TransferInfo.Params, 48); + taskID = new UUID(transfer.TransferInfo.Params, 48); UUID itemID = new UUID(transfer.TransferInfo.Params, 64); UUID requestID = new UUID(transfer.TransferInfo.Params, 80); if (!(((Scene)m_scene).Permissions.BypassPermissions())) @@ -7121,7 +7121,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP //m_assetCache.AddAssetRequest(this, transfer); - MakeAssetRequest(transfer); + MakeAssetRequest(transfer, taskID); /* RequestAsset = OnRequestAsset; if (RequestAsset != null) @@ -10330,7 +10330,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return String.Empty; } - public void MakeAssetRequest(TransferRequestPacket transferRequest) + public void MakeAssetRequest(TransferRequestPacket transferRequest, UUID taskID) { UUID requestID = UUID.Zero; if (transferRequest.TransferInfo.SourceType == 2) @@ -10343,11 +10343,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP //inventory asset request requestID = new UUID(transferRequest.TransferInfo.Params, 80); //m_log.Debug("asset request " + requestID); + if (taskID == UUID.Zero) // Agent + if (m_scene is HGScene) + { + // We may need to fetch the asset from the user's asset server into the local asset server + HGAssetMapper mapper = ((HGScene)m_scene).AssetMapper; + mapper.Get(requestID, AgentId); + } } //check to see if asset is in local cache, if not we need to request it from asset server. //m_log.Debug("asset request " + requestID); - + m_assetService.Get(requestID.ToString(), transferRequest, AssetReceived); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 343f537..56d34e6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -59,6 +59,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private C5.IntervalHeap m_priorityQueue = new C5.IntervalHeap(10, new J2KImageComparer()); private object m_syncRoot = new object(); + private IHyperAssetService m_hyperAssets; + public LLClientView Client { get { return m_client; } } public AssetBase MissingImage { get { return m_missingImage; } } @@ -74,6 +76,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.Error("[ClientView] - Couldn't set missing image asset, falling back to missing image packet. This is known to crash the client"); m_j2kDecodeModule = pJ2kDecodeModule; + m_hyperAssets = client.Scene.RequestModuleInterface(); } /// @@ -146,6 +149,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP imgrequest = new J2KImage(this); imgrequest.J2KDecoder = m_j2kDecodeModule; imgrequest.AssetService = m_assetCache; + imgrequest.AgentID = m_client.AgentId; + imgrequest.HyperAssets = m_hyperAssets; imgrequest.DiscardLevel = newRequest.DiscardLevel; imgrequest.StartPacket = Math.Max(1, newRequest.PacketNumber); imgrequest.Priority = newRequest.Priority; -- cgit v1.1 From 63ed605eba7d4655bbbf956cef0d6e6b02b4a64e Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 12 Oct 2009 17:36:13 -0700 Subject: Stop the recurring texture requests for textures that truly don't exist. --- OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index 1bbe00f..bb98f24 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs @@ -373,14 +373,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP UUID assetID = UUID.Zero; if (asset != null) assetID = asset.FullID; - else if (HyperAssets != null) + else if ((HyperAssets != null) && (sender != HyperAssets)) { // Try the user's inventory, but only if it's different from the regions' string userAssets = HyperAssets.GetUserAssetServer(AgentID); if ((userAssets != string.Empty) && (userAssets != HyperAssets.GetSimAssetServer())) { m_log.DebugFormat("[J2KIMAGE]: texture {0} not found in local asset storage. Trying user's storage.", id); - AssetService.Get(userAssets + "/" + id, this, AssetReceived); + AssetService.Get(userAssets + "/" + id, HyperAssets, AssetReceived); return; } } -- cgit v1.1 From f3d2192cd486cf32517e6c4549a3a691422a5f88 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Mon, 12 Oct 2009 18:30:06 -0700 Subject: Better handling of missing assets. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 32 ++++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 25eb5cd..ad92494 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -117,6 +117,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected int m_avatarTerseUpdatesPerPacket = 5; protected int m_packetMTU = 1400; protected IAssetService m_assetService; + private IHyperAssetService m_hyperAssets; #region Properties @@ -172,6 +173,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene = scene; m_assetService = m_scene.RequestModuleInterface(); + m_hyperAssets = m_scene.RequestModuleInterface(); m_GroupsModule = scene.RequestModuleInterface(); m_imageManager = new LLImageManager(this, m_assetService, Scene.RequestModuleInterface()); m_channelVersion = Utils.StringToBytes(scene.GetSimulatorVersion()); @@ -10342,14 +10344,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP { //inventory asset request requestID = new UUID(transferRequest.TransferInfo.Params, 80); - //m_log.Debug("asset request " + requestID); - if (taskID == UUID.Zero) // Agent - if (m_scene is HGScene) - { - // We may need to fetch the asset from the user's asset server into the local asset server - HGAssetMapper mapper = ((HGScene)m_scene).AssetMapper; - mapper.Get(requestID, AgentId); - } + //m_log.Debug("[XXX] inventory asset request " + requestID); + //if (taskID == UUID.Zero) // Agent + // if (m_scene is HGScene) + // { + // m_log.Debug("[XXX] hg asset request " + requestID); + // // We may need to fetch the asset from the user's asset server into the local asset server + // HGAssetMapper mapper = ((HGScene)m_scene).AssetMapper; + // mapper.Get(requestID, AgentId); + // } } //check to see if asset is in local cache, if not we need to request it from asset server. @@ -10378,10 +10381,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP //m_log.Debug("asset request " + requestID); } - // FIXME: We never tell the client about assets which do not exist when requested by this transfer mechanism, which can't be right. if (null == asset) { + // Try the user's inventory, but only if it's different from the regions' + string userAssets = m_hyperAssets.GetUserAssetServer(AgentId); + if ((userAssets != string.Empty) && (userAssets != m_hyperAssets.GetSimAssetServer())) + { + m_log.DebugFormat("[CLIENT]: asset {0} not found in local asset storage. Trying user's storage.", id); + transferRequest.TransferInfo.SourceType = 9999; // marker + m_assetService.Get(userAssets + "/" + id, transferRequest, AssetReceived); + return; + } + //m_log.DebugFormat("[ASSET CACHE]: Asset transfer request for asset which is {0} already known to be missing. Dropping", requestID); + + // FIXME: We never tell the client about assets which do not exist when requested by this transfer mechanism, which can't be right. return; } -- cgit v1.1 From e3d5beebfb44aee8a9bd30526be8d4e65adf632b Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Tue, 13 Oct 2009 06:39:11 -0700 Subject: Better handling of missing assets. --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 25 ++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index ad92494..d64f655 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -10368,12 +10368,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP UUID requestID = UUID.Zero; byte source = 2; - if (transferRequest.TransferInfo.SourceType == 2) + if ((transferRequest.TransferInfo.SourceType == 2) || (transferRequest.TransferInfo.SourceType == 2222)) { //direct asset request requestID = new UUID(transferRequest.TransferInfo.Params, 0); } - else if (transferRequest.TransferInfo.SourceType == 3) + else if ((transferRequest.TransferInfo.SourceType == 3) || (transferRequest.TransferInfo.SourceType == 3333)) { //inventory asset request requestID = new UUID(transferRequest.TransferInfo.Params, 80); @@ -10383,14 +10383,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (null == asset) { - // Try the user's inventory, but only if it's different from the regions' - string userAssets = m_hyperAssets.GetUserAssetServer(AgentId); - if ((userAssets != string.Empty) && (userAssets != m_hyperAssets.GetSimAssetServer())) + if ((m_hyperAssets != null) && (transferRequest.TransferInfo.SourceType < 2000)) { - m_log.DebugFormat("[CLIENT]: asset {0} not found in local asset storage. Trying user's storage.", id); - transferRequest.TransferInfo.SourceType = 9999; // marker - m_assetService.Get(userAssets + "/" + id, transferRequest, AssetReceived); - return; + // Try the user's inventory, but only if it's different from the regions' + string userAssets = m_hyperAssets.GetUserAssetServer(AgentId); + if ((userAssets != string.Empty) && (userAssets != m_hyperAssets.GetSimAssetServer())) + { + m_log.DebugFormat("[CLIENT]: asset {0} not found in local asset storage. Trying user's storage.", id); + if (transferRequest.TransferInfo.SourceType == 2) + transferRequest.TransferInfo.SourceType = 2222; // marker + else if (transferRequest.TransferInfo.SourceType == 3) + transferRequest.TransferInfo.SourceType = 3333; // marker + + m_assetService.Get(userAssets + "/" + id, transferRequest, AssetReceived); + return; + } } //m_log.DebugFormat("[ASSET CACHE]: Asset transfer request for asset which is {0} already known to be missing. Dropping", requestID); -- cgit v1.1 From 3828b3c0e86bbb444dabadcc6e509675b058639e Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 10:38:35 -0700 Subject: * Eliminated unnecessary parameters from LLUDPServer.SendPacketData() * Changed PrimMesher's Quat.Identity to return <0,0,0,1> instead of <0,0,0,1.1> --- OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index fcb2cd0..07764cb 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -210,7 +210,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte[] data = datas[i]; m_clients.ForEach( delegate(LLUDPClient client) - { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); }); + { SendPacketData(client, data, packet.Type, category); }); } } else @@ -218,7 +218,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte[] data = packet.ToBytes(); m_clients.ForEach( delegate(LLUDPClient client) - { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); }); + { SendPacketData(client, data, packet.Type, category); }); } } @@ -239,18 +239,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < packetCount; i++) { byte[] data = datas[i]; - SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); + SendPacketData(client, data, packet.Type, category); } } else { byte[] data = packet.ToBytes(); - SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); + SendPacketData(client, data, packet.Type, category); } } - public void SendPacketData(LLUDPClient client, byte[] data, int dataLength, PacketType type, bool doZerocode, ThrottleOutPacketType category) + public void SendPacketData(LLUDPClient client, byte[] data, PacketType type, ThrottleOutPacketType category) { + int dataLength = data.Length; + bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; + // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum. // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here -- cgit v1.1 From 82ace481c9ea180e7e5b9d6d7c7c191e438fdbe1 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 11:14:45 -0700 Subject: * Broke the circular reference between LLClientView and LLUDPClient. This should speed up garbage collection on the large LLClientView objects, and also prevents handling packets for disconnected clients * Renamed local LLUDPClient variables to udpClient to avoid naming confusion between LLUDPClient and LLClientView --- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 2 - .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 134 ++++++++++++--------- 2 files changed, 77 insertions(+), 59 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 871e8e8..10e22d5 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -82,8 +82,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// ACKs that are queued up, waiting to be sent to the client public readonly LocklessQueue PendingAcks = new LocklessQueue(); - /// Reference to the IClientAPI for this client - public LLClientView ClientAPI; /// Current packet sequence number public int CurrentSequence; /// Current ping sequence number diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 07764cb..0b5b51d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -183,10 +183,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void RemoveClient(LLUDPClient udpClient) { - m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + udpClient.ClientAPI.Name); + m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + udpClient.AgentID); - m_scene.ClientManager.Remove(udpClient.CircuitCode); - udpClient.ClientAPI.Close(false); + // Shut down the IClientAPI and remove it from the scene + IClientAPI client; + if (m_scene.ClientManager.TryGetClient(udpClient.CircuitCode, out client)) + { + client.Close(false); + m_scene.ClientManager.Remove(udpClient.CircuitCode); + } + + // Shut down the LLUDPClient and remove it from the list of UDP clients udpClient.Shutdown(); m_clients.Remove(udpClient.RemoteEndPoint); } @@ -222,7 +229,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public void SendPacket(LLUDPClient client, Packet packet, ThrottleOutPacketType category, bool allowSplitting) + public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting) { // CoarseLocationUpdate packets cannot be split in an automated way if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) @@ -239,17 +246,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < packetCount; i++) { byte[] data = datas[i]; - SendPacketData(client, data, packet.Type, category); + SendPacketData(udpClient, data, packet.Type, category); } } else { byte[] data = packet.ToBytes(); - SendPacketData(client, data, packet.Type, category); + SendPacketData(udpClient, data, packet.Type, category); } } - public void SendPacketData(LLUDPClient client, byte[] data, PacketType type, ThrottleOutPacketType category) + public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category) { int dataLength = data.Length; bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; @@ -260,7 +267,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // to accomodate for both common scenarios and provide ample room for ACK appending in both int bufferSize = (dataLength > 180) ? Packet.MTU : 200; - UDPPacketBuffer buffer = new UDPPacketBuffer(client.RemoteEndPoint, bufferSize); + UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); // Zerocode if needed if (doZerocode) @@ -285,7 +292,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Queue or Send // Look up the UDPClient this is going to - OutgoingPacket outgoingPacket = new OutgoingPacket(client, buffer, category); + OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category); if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) SendPacketFinal(outgoingPacket); @@ -293,18 +300,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Queue or Send } - public void SendAcks(LLUDPClient client) + public void SendAcks(LLUDPClient udpClient) { uint ack; - if (client.PendingAcks.Dequeue(out ack)) + if (udpClient.PendingAcks.Dequeue(out ack)) { List blocks = new List(); PacketAckPacket.PacketsBlock block = new PacketAckPacket.PacketsBlock(); block.ID = ack; blocks.Add(block); - while (client.PendingAcks.Dequeue(out ack)) + while (udpClient.PendingAcks.Dequeue(out ack)) { block = new PacketAckPacket.PacketsBlock(); block.ID = ack; @@ -315,22 +322,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.Header.Reliable = false; packet.Packets = blocks.ToArray(); - SendPacket(client, packet, ThrottleOutPacketType.Unknown, true); + SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true); } } - public void SendPing(LLUDPClient client) + public void SendPing(LLUDPClient udpClient) { - IClientAPI api = client.ClientAPI; - if (api != null) - api.SendStartPingCheck(client.CurrentPingSequence++); + StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck); + pc.Header.Reliable = false; + + OutgoingPacket oldestPacket = udpClient.NeedAcks.GetOldest(); + + pc.PingID.PingID = (byte)udpClient.CurrentPingSequence++; + pc.PingID.OldestUnacked = (oldestPacket != null) ? oldestPacket.SequenceNumber : 0; + + SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false); } - public void ResendUnacked(LLUDPClient client) + public void ResendUnacked(LLUDPClient udpClient) { - if (client.NeedAcks.Count > 0) + if (udpClient.NeedAcks.Count > 0) { - List expiredPackets = client.NeedAcks.GetExpiredPackets(client.RTO); + List expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO); if (expiredPackets != null) { @@ -366,18 +379,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.DebugFormat("[LLUDPSERVER]: Dropping packet #{0} for agent {1} after {2} failed attempts", outgoingPacket.SequenceNumber, outgoingPacket.Client.RemoteEndPoint, outgoingPacket.ResendCount); - lock (client.NeedAcks.SyncRoot) - client.NeedAcks.RemoveUnsafe(outgoingPacket.SequenceNumber); + lock (udpClient.NeedAcks.SyncRoot) + udpClient.NeedAcks.RemoveUnsafe(outgoingPacket.SequenceNumber); //Interlocked.Increment(ref Stats.DroppedPackets); // Disconnect an agent if no packets are received for some time //FIXME: Make 60 an .ini setting - if (Environment.TickCount - client.TickLastPacketReceived > 1000 * 60) + if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60) { - m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + client.ClientAPI.Name); + m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); - RemoveClient(client); + RemoveClient(udpClient); return; } } @@ -397,7 +410,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte flags = buffer.Data[0]; bool isResend = (flags & Helpers.MSG_RESENT) != 0; bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; - LLUDPClient client = outgoingPacket.Client; + LLUDPClient udpClient = outgoingPacket.Client; // Keep track of when this packet was sent out (right now) outgoingPacket.TickCount = Environment.TickCount; @@ -410,7 +423,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // no more ACKs to append uint ackCount = 0; uint ack; - while (dataLength + 5 < buffer.Data.Length && client.PendingAcks.Dequeue(out ack)) + while (dataLength + 5 < buffer.Data.Length && udpClient.PendingAcks.Dequeue(out ack)) { Utils.UIntToBytesBig(ack, buffer.Data, dataLength); dataLength += 4; @@ -429,24 +442,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion ACK Appending + #region Sequence Number Assignment + if (!isResend) { // Not a resend, assign a new sequence number - uint sequenceNumber = (uint)Interlocked.Increment(ref client.CurrentSequence); + uint sequenceNumber = (uint)Interlocked.Increment(ref udpClient.CurrentSequence); Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); outgoingPacket.SequenceNumber = sequenceNumber; if (isReliable) { // Add this packet to the list of ACK responses we are waiting on from the server - client.NeedAcks.Add(outgoingPacket); + udpClient.NeedAcks.Add(outgoingPacket); } } + #endregion Sequence Number Assignment + // Stats tracking - Interlocked.Increment(ref client.PacketsSent); + Interlocked.Increment(ref udpClient.PacketsSent); if (isReliable) - Interlocked.Add(ref client.UnackedBytes, outgoingPacket.Buffer.DataLength); + Interlocked.Add(ref udpClient.UnackedBytes, outgoingPacket.Buffer.DataLength); // Put the UDP payload on the wire AsyncBeginSend(buffer); @@ -455,7 +472,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected override void PacketReceived(UDPPacketBuffer buffer) { // Debugging/Profiling - //try { Thread.CurrentThread.Name = "PacketReceived (" + scene.RegionName + ")"; } + //try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; } //catch (Exception) { } LLUDPClient client = null; @@ -484,9 +501,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return; } - //Stats.RecvBytes += (ulong)buffer.DataLength; - //++Stats.RecvPackets; - #endregion Decoding #region UseCircuitCode Handling @@ -508,7 +522,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (!m_clients.TryGetValue(address, out client)) { m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + - ", currently tracking " + m_clients.Count + " clients"); + " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_clients.Count + " clients"); return; } @@ -549,7 +563,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region ACK Sending if (packet.Header.Reliable) - client.PendingAcks.Enqueue((uint)packet.Header.Sequence); + client.PendingAcks.Enqueue(packet.Header.Sequence); // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove @@ -648,9 +662,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene.ClientManager.Add(circuitCode, clientApi); clientApi.Start(); - // Give LLUDPClient a reference to IClientAPI - udpClient.ClientAPI = clientApi; - // Add the new client to our list of tracked clients m_clients.Add(udpClient.RemoteEndPoint, udpClient); } @@ -756,31 +767,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP { IncomingPacket incomingPacket = (IncomingPacket)state; Packet packet = incomingPacket.Packet; - LLUDPClient client = incomingPacket.Client; + LLUDPClient udpClient = incomingPacket.Client; + IClientAPI client; // Sanity check - if (packet == null || client == null || client.ClientAPI == null) + if (packet == null || udpClient == null) { - m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", Client=\"{1}\", Client.ClientAPI=\"{2}\"", - packet, client, (client != null) ? client.ClientAPI : null); + m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", UDPClient=\"{1}\"", + packet, udpClient); } - try + // Make sure this client is still alive + if (m_scene.ClientManager.TryGetClient(udpClient.CircuitCode, out client)) { - // Process this packet - client.ClientAPI.ProcessInPacket(packet); - } - catch (ThreadAbortException) - { - // If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down - m_log.Info("[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server"); - Stop(); + try + { + // Process this packet + client.ProcessInPacket(packet); + } + catch (ThreadAbortException) + { + // If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down + m_log.Info("[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server"); + Stop(); + } + catch (Exception e) + { + // Don't let a failure in an individual client thread crash the whole sim. + m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", udpClient.AgentID, packet.Type); + m_log.Error(e.Message, e); + } } - catch (Exception e) + else { - // Don't let a failure in an individual client thread crash the whole sim. - m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", client.AgentID, packet.Type); - m_log.Error(e.Message, e); + m_log.DebugFormat("[LLUDPSERVER]: Dropping incoming {0} packet for dead client {1}", packet.Type, udpClient.AgentID); } } -- cgit v1.1 From f55b282078e4e7c5ee7d0ca613891302d2b9957d Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 11:28:08 -0700 Subject: Avoid checking m_clients collection twice when a UseCircuitCode packet is received --- .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 2 +- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 56 +++++++++++----------- 2 files changed, 30 insertions(+), 28 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs index 2972d46..4f375e4 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs @@ -131,7 +131,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Action to perform on each element public void ForEach(Action action) { - Parallel.ForEach(m_dict.Values, action); + Parallel.ForEach(m_dict.Values, action); } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 0b5b51d..2228f39 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -503,19 +503,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Decoding - #region UseCircuitCode Handling + #region Packet to Client Mapping + // UseCircuitCode handling if (packet.Type == PacketType.UseCircuitCode) { - UseCircuitCodePacket useCircuitCode = (UseCircuitCodePacket)packet; - IClientAPI newuser; - uint circuitCode = useCircuitCode.CircuitCode.Code; - - // Check if the client is already established - if (!m_scene.ClientManager.TryGetClient(circuitCode, out newuser)) - { - AddNewClient(useCircuitCode, (IPEndPoint)buffer.RemoteEndPoint); - } + AddNewClient((UseCircuitCodePacket)packet, (IPEndPoint)buffer.RemoteEndPoint); } // Determine which agent this packet came from @@ -526,7 +519,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return; } - #endregion UseCircuitCode Handling + #endregion Packet to Client Mapping // Stats tracking Interlocked.Increment(ref client.PacketsReceived); @@ -620,29 +613,38 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void AddNewClient(UseCircuitCodePacket useCircuitCode, IPEndPoint remoteEndPoint) { - //Slave regions don't accept new clients if (m_scene.RegionStatus != RegionStatus.SlaveScene) { - AuthenticateResponse sessionInfo; - bool isNewCircuit = !m_clients.ContainsKey(remoteEndPoint); - - if (!IsClientAuthorized(useCircuitCode, out sessionInfo)) + if (!m_clients.ContainsKey(remoteEndPoint)) { - m_log.WarnFormat( - "[CONNECTION FAILURE]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", - useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint); - return; - } + AuthenticateResponse sessionInfo; + if (IsClientAuthorized(useCircuitCode, out sessionInfo)) + { + UUID agentID = useCircuitCode.CircuitCode.ID; + UUID sessionID = useCircuitCode.CircuitCode.SessionID; + uint circuitCode = useCircuitCode.CircuitCode.Code; - if (isNewCircuit) + AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo); + } + else + { + // Don't create circuits for unauthorized clients + m_log.WarnFormat( + "[LLUDPSERVER]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", + useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint); + } + } + else { - UUID agentID = useCircuitCode.CircuitCode.ID; - UUID sessionID = useCircuitCode.CircuitCode.SessionID; - uint circuitCode = useCircuitCode.CircuitCode.Code; - - AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo); + // Ignore repeated UseCircuitCode packets + m_log.Debug("[LLUDPSERVER]: Ignoring UseCircuitCode for already established circuit " + useCircuitCode.CircuitCode.Code); } } + else + { + // Slave regions don't accept new clients + m_log.Debug("[LLUDPSERVER]: Slave region " + m_scene.RegionInfo.RegionName + " ignoring UseCircuitCode packet"); + } } private void AddClient(uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo) -- cgit v1.1 From c893761319f7e61d13b2d2301180d0f227fde1a9 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 12:50:59 -0700 Subject: * Unregister event handlers in LLUDPServer when a client logs out and disconnects * Move ViewerEffect handling to Scene.PacketHandlers * Removing the unused CloseAllAgents function * Trimming ClientManager down. This class needs to be reworked to keep LLUDP circuit codes from intruding into the abstract OpenSim core code --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 442 ++++++++++----------- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 18 +- 2 files changed, 221 insertions(+), 239 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 139dc3b..bc9cfcf 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -58,6 +58,209 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public class LLClientView : IClientAPI, IClientCore, IClientIM, IClientChat, IClientIPEndpoint, IStatsCollector { + #region Events + + public event GenericMessage OnGenericMessage; + public event BinaryGenericMessage OnBinaryGenericMessage; + public event Action OnLogout; + public event ObjectPermissions OnObjectPermissions; + public event Action OnConnectionClosed; + public event ViewerEffectEventHandler OnViewerEffect; + public event ImprovedInstantMessage OnInstantMessage; + public event ChatMessage OnChatFromClient; + public event TextureRequest OnRequestTexture; + public event RezObject OnRezObject; + public event DeRezObject OnDeRezObject; + public event ModifyTerrain OnModifyTerrain; + public event Action OnRegionHandShakeReply; + public event GenericCall2 OnRequestWearables; + public event SetAppearance OnSetAppearance; + public event AvatarNowWearing OnAvatarNowWearing; + public event RezSingleAttachmentFromInv OnRezSingleAttachmentFromInv; + public event RezMultipleAttachmentsFromInv OnRezMultipleAttachmentsFromInv; + public event UUIDNameRequest OnDetachAttachmentIntoInv; + public event ObjectAttach OnObjectAttach; + public event ObjectDeselect OnObjectDetach; + public event ObjectDrop OnObjectDrop; + public event GenericCall2 OnCompleteMovementToRegion; + public event UpdateAgent OnAgentUpdate; + public event AgentRequestSit OnAgentRequestSit; + public event AgentSit OnAgentSit; + public event AvatarPickerRequest OnAvatarPickerRequest; + public event StartAnim OnStartAnim; + public event StopAnim OnStopAnim; + public event Action OnRequestAvatarsData; + public event LinkObjects OnLinkObjects; + public event DelinkObjects OnDelinkObjects; + public event GrabObject OnGrabObject; + public event DeGrabObject OnDeGrabObject; + public event SpinStart OnSpinStart; + public event SpinStop OnSpinStop; + public event ObjectDuplicate OnObjectDuplicate; + public event ObjectDuplicateOnRay OnObjectDuplicateOnRay; + public event MoveObject OnGrabUpdate; + public event SpinObject OnSpinUpdate; + public event AddNewPrim OnAddPrim; + public event RequestGodlikePowers OnRequestGodlikePowers; + public event GodKickUser OnGodKickUser; + public event ObjectExtraParams OnUpdateExtraParams; + public event UpdateShape OnUpdatePrimShape; + public event ObjectRequest OnObjectRequest; + public event ObjectSelect OnObjectSelect; + public event ObjectDeselect OnObjectDeselect; + public event GenericCall7 OnObjectDescription; + public event GenericCall7 OnObjectName; + public event GenericCall7 OnObjectClickAction; + public event GenericCall7 OnObjectMaterial; + public event ObjectIncludeInSearch OnObjectIncludeInSearch; + public event RequestObjectPropertiesFamily OnRequestObjectPropertiesFamily; + public event UpdatePrimFlags OnUpdatePrimFlags; + public event UpdatePrimTexture OnUpdatePrimTexture; + public event UpdateVector OnUpdatePrimGroupPosition; + public event UpdateVector OnUpdatePrimSinglePosition; + public event UpdatePrimRotation OnUpdatePrimGroupRotation; + public event UpdatePrimSingleRotation OnUpdatePrimSingleRotation; + public event UpdatePrimSingleRotationPosition OnUpdatePrimSingleRotationPosition; + public event UpdatePrimGroupRotation OnUpdatePrimGroupMouseRotation; + public event UpdateVector OnUpdatePrimScale; + public event UpdateVector OnUpdatePrimGroupScale; + public event StatusChange OnChildAgentStatus; + public event GenericCall2 OnStopMovement; + public event Action OnRemoveAvatar; + public event RequestMapBlocks OnRequestMapBlocks; + public event RequestMapName OnMapNameRequest; + public event TeleportLocationRequest OnTeleportLocationRequest; + public event TeleportLandmarkRequest OnTeleportLandmarkRequest; + public event DisconnectUser OnDisconnectUser; + public event RequestAvatarProperties OnRequestAvatarProperties; + public event SetAlwaysRun OnSetAlwaysRun; + public event FetchInventory OnAgentDataUpdateRequest; + public event TeleportLocationRequest OnSetStartLocationRequest; + public event UpdateAvatarProperties OnUpdateAvatarProperties; + public event CreateNewInventoryItem OnCreateNewInventoryItem; + public event CreateInventoryFolder OnCreateNewInventoryFolder; + public event UpdateInventoryFolder OnUpdateInventoryFolder; + public event MoveInventoryFolder OnMoveInventoryFolder; + public event FetchInventoryDescendents OnFetchInventoryDescendents; + public event PurgeInventoryDescendents OnPurgeInventoryDescendents; + public event FetchInventory OnFetchInventory; + public event RequestTaskInventory OnRequestTaskInventory; + public event UpdateInventoryItem OnUpdateInventoryItem; + public event CopyInventoryItem OnCopyInventoryItem; + public event MoveInventoryItem OnMoveInventoryItem; + public event RemoveInventoryItem OnRemoveInventoryItem; + public event RemoveInventoryFolder OnRemoveInventoryFolder; + public event UDPAssetUploadRequest OnAssetUploadRequest; + public event XferReceive OnXferReceive; + public event RequestXfer OnRequestXfer; + public event ConfirmXfer OnConfirmXfer; + public event AbortXfer OnAbortXfer; + public event RequestTerrain OnRequestTerrain; + public event RezScript OnRezScript; + public event UpdateTaskInventory OnUpdateTaskInventory; + public event MoveTaskInventory OnMoveTaskItem; + public event RemoveTaskInventory OnRemoveTaskItem; + public event RequestAsset OnRequestAsset; + public event UUIDNameRequest OnNameFromUUIDRequest; + public event ParcelAccessListRequest OnParcelAccessListRequest; + public event ParcelAccessListUpdateRequest OnParcelAccessListUpdateRequest; + public event ParcelPropertiesRequest OnParcelPropertiesRequest; + public event ParcelDivideRequest OnParcelDivideRequest; + public event ParcelJoinRequest OnParcelJoinRequest; + public event ParcelPropertiesUpdateRequest OnParcelPropertiesUpdateRequest; + public event ParcelSelectObjects OnParcelSelectObjects; + public event ParcelObjectOwnerRequest OnParcelObjectOwnerRequest; + public event ParcelAbandonRequest OnParcelAbandonRequest; + public event ParcelGodForceOwner OnParcelGodForceOwner; + public event ParcelReclaim OnParcelReclaim; + public event ParcelReturnObjectsRequest OnParcelReturnObjectsRequest; + public event ParcelDeedToGroup OnParcelDeedToGroup; + public event RegionInfoRequest OnRegionInfoRequest; + public event EstateCovenantRequest OnEstateCovenantRequest; + public event FriendActionDelegate OnApproveFriendRequest; + public event FriendActionDelegate OnDenyFriendRequest; + public event FriendshipTermination OnTerminateFriendship; + public event MoneyTransferRequest OnMoneyTransferRequest; + public event EconomyDataRequest OnEconomyDataRequest; + public event MoneyBalanceRequest OnMoneyBalanceRequest; + public event ParcelBuy OnParcelBuy; + public event UUIDNameRequest OnTeleportHomeRequest; + public event UUIDNameRequest OnUUIDGroupNameRequest; + public event ScriptAnswer OnScriptAnswer; + public event RequestPayPrice OnRequestPayPrice; + public event ObjectSaleInfo OnObjectSaleInfo; + public event ObjectBuy OnObjectBuy; + public event BuyObjectInventory OnBuyObjectInventory; + public event AgentSit OnUndo; + public event ForceReleaseControls OnForceReleaseControls; + public event GodLandStatRequest OnLandStatRequest; + public event RequestObjectPropertiesFamily OnObjectGroupRequest; + public event DetailedEstateDataRequest OnDetailedEstateDataRequest; + public event SetEstateFlagsRequest OnSetEstateFlagsRequest; + public event SetEstateTerrainBaseTexture OnSetEstateTerrainBaseTexture; + public event SetEstateTerrainDetailTexture OnSetEstateTerrainDetailTexture; + public event SetEstateTerrainTextureHeights OnSetEstateTerrainTextureHeights; + public event CommitEstateTerrainTextureRequest OnCommitEstateTerrainTextureRequest; + public event SetRegionTerrainSettings OnSetRegionTerrainSettings; + public event BakeTerrain OnBakeTerrain; + public event RequestTerrain OnUploadTerrain; + public event EstateChangeInfo OnEstateChangeInfo; + public event EstateRestartSimRequest OnEstateRestartSimRequest; + public event EstateChangeCovenantRequest OnEstateChangeCovenantRequest; + public event UpdateEstateAccessDeltaRequest OnUpdateEstateAccessDeltaRequest; + public event SimulatorBlueBoxMessageRequest OnSimulatorBlueBoxMessageRequest; + public event EstateBlueBoxMessageRequest OnEstateBlueBoxMessageRequest; + public event EstateDebugRegionRequest OnEstateDebugRegionRequest; + public event EstateTeleportOneUserHomeRequest OnEstateTeleportOneUserHomeRequest; + public event EstateTeleportAllUsersHomeRequest OnEstateTeleportAllUsersHomeRequest; + public event RegionHandleRequest OnRegionHandleRequest; + public event ParcelInfoRequest OnParcelInfoRequest; + public event ScriptReset OnScriptReset; + public event GetScriptRunning OnGetScriptRunning; + public event SetScriptRunning OnSetScriptRunning; + public event UpdateVector OnAutoPilotGo; + public event TerrainUnacked OnUnackedTerrain; + public event ActivateGesture OnActivateGesture; + public event DeactivateGesture OnDeactivateGesture; + public event ObjectOwner OnObjectOwner; + public event DirPlacesQuery OnDirPlacesQuery; + public event DirFindQuery OnDirFindQuery; + public event DirLandQuery OnDirLandQuery; + public event DirPopularQuery OnDirPopularQuery; + public event DirClassifiedQuery OnDirClassifiedQuery; + public event EventInfoRequest OnEventInfoRequest; + public event ParcelSetOtherCleanTime OnParcelSetOtherCleanTime; + public event MapItemRequest OnMapItemRequest; + public event OfferCallingCard OnOfferCallingCard; + public event AcceptCallingCard OnAcceptCallingCard; + public event DeclineCallingCard OnDeclineCallingCard; + public event SoundTrigger OnSoundTrigger; + public event StartLure OnStartLure; + public event TeleportLureRequest OnTeleportLureRequest; + public event NetworkStats OnNetworkStatsUpdate; + public event ClassifiedInfoRequest OnClassifiedInfoRequest; + public event ClassifiedInfoUpdate OnClassifiedInfoUpdate; + public event ClassifiedDelete OnClassifiedDelete; + public event ClassifiedDelete OnClassifiedGodDelete; + public event EventNotificationAddRequest OnEventNotificationAddRequest; + public event EventNotificationRemoveRequest OnEventNotificationRemoveRequest; + public event EventGodDelete OnEventGodDelete; + public event ParcelDwellRequest OnParcelDwellRequest; + public event UserInfoRequest OnUserInfoRequest; + public event UpdateUserInfo OnUpdateUserInfo; + public event RetrieveInstantMessages OnRetrieveInstantMessages; + public event PickDelete OnPickDelete; + public event PickGodDelete OnPickGodDelete; + public event PickInfoUpdate OnPickInfoUpdate; + public event AvatarNotesUpdate OnAvatarNotesUpdate; + public event MuteListRequest OnMuteListRequest; + public event AvatarInterestUpdate OnAvatarInterestUpdate; + public event PlacesQuery OnPlacesQuery; + + #endregion Events + + #region Class Members + // LLClientView Only public delegate void BinaryGenericMessage(Object sender, string method, byte[][] args); @@ -114,9 +317,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected int m_textureDataLimit = 10; protected int m_avatarTerseUpdateRate = 50; protected int m_avatarTerseUpdatesPerPacket = 5; - protected int m_packetMTU = 1400; protected IAssetService m_assetService; + #endregion Class Members + #region Properties public LLUDPClient UDPClient { get { return m_udpClient; } } @@ -413,34 +617,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return result; } - /*protected void DebugPacket(string direction, Packet packet) - { - string info; - - if (m_debugPacketLevel < 255 && packet.Type == PacketType.AgentUpdate) - return; - if (m_debugPacketLevel < 254 && packet.Type == PacketType.ViewerEffect) - return; - if (m_debugPacketLevel < 253 && ( - packet.Type == PacketType.CompletePingCheck || - packet.Type == PacketType.StartPingCheck - )) - return; - if (m_debugPacketLevel < 252 && packet.Type == PacketType.PacketAck) - return; - - if (m_debugPacketLevel > 1) - { - info = packet.ToString(); - } - else - { - info = packet.Type.ToString(); - } - - Console.WriteLine(m_circuitCode + ":" + direction + ": " + info); - }*/ - #endregion Packet Handling # region Setup @@ -523,207 +699,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP # endregion - #region Events - - public event GenericMessage OnGenericMessage; - public event BinaryGenericMessage OnBinaryGenericMessage; - public event Action OnLogout; - public event ObjectPermissions OnObjectPermissions; - public event Action OnConnectionClosed; - public event ViewerEffectEventHandler OnViewerEffect; - public event ImprovedInstantMessage OnInstantMessage; - public event ChatMessage OnChatFromClient; - public event TextureRequest OnRequestTexture; - public event RezObject OnRezObject; - public event DeRezObject OnDeRezObject; - public event ModifyTerrain OnModifyTerrain; - public event Action OnRegionHandShakeReply; - public event GenericCall2 OnRequestWearables; - public event SetAppearance OnSetAppearance; - public event AvatarNowWearing OnAvatarNowWearing; - public event RezSingleAttachmentFromInv OnRezSingleAttachmentFromInv; - public event RezMultipleAttachmentsFromInv OnRezMultipleAttachmentsFromInv; - public event UUIDNameRequest OnDetachAttachmentIntoInv; - public event ObjectAttach OnObjectAttach; - public event ObjectDeselect OnObjectDetach; - public event ObjectDrop OnObjectDrop; - public event GenericCall2 OnCompleteMovementToRegion; - public event UpdateAgent OnAgentUpdate; - public event AgentRequestSit OnAgentRequestSit; - public event AgentSit OnAgentSit; - public event AvatarPickerRequest OnAvatarPickerRequest; - public event StartAnim OnStartAnim; - public event StopAnim OnStopAnim; - public event Action OnRequestAvatarsData; - public event LinkObjects OnLinkObjects; - public event DelinkObjects OnDelinkObjects; - public event GrabObject OnGrabObject; - public event DeGrabObject OnDeGrabObject; - public event SpinStart OnSpinStart; - public event SpinStop OnSpinStop; - public event ObjectDuplicate OnObjectDuplicate; - public event ObjectDuplicateOnRay OnObjectDuplicateOnRay; - public event MoveObject OnGrabUpdate; - public event SpinObject OnSpinUpdate; - public event AddNewPrim OnAddPrim; - public event RequestGodlikePowers OnRequestGodlikePowers; - public event GodKickUser OnGodKickUser; - public event ObjectExtraParams OnUpdateExtraParams; - public event UpdateShape OnUpdatePrimShape; - public event ObjectRequest OnObjectRequest; - public event ObjectSelect OnObjectSelect; - public event ObjectDeselect OnObjectDeselect; - public event GenericCall7 OnObjectDescription; - public event GenericCall7 OnObjectName; - public event GenericCall7 OnObjectClickAction; - public event GenericCall7 OnObjectMaterial; - public event ObjectIncludeInSearch OnObjectIncludeInSearch; - public event RequestObjectPropertiesFamily OnRequestObjectPropertiesFamily; - public event UpdatePrimFlags OnUpdatePrimFlags; - public event UpdatePrimTexture OnUpdatePrimTexture; - public event UpdateVector OnUpdatePrimGroupPosition; - public event UpdateVector OnUpdatePrimSinglePosition; - public event UpdatePrimRotation OnUpdatePrimGroupRotation; - public event UpdatePrimSingleRotation OnUpdatePrimSingleRotation; - public event UpdatePrimSingleRotationPosition OnUpdatePrimSingleRotationPosition; - public event UpdatePrimGroupRotation OnUpdatePrimGroupMouseRotation; - public event UpdateVector OnUpdatePrimScale; - public event UpdateVector OnUpdatePrimGroupScale; - public event StatusChange OnChildAgentStatus; - public event GenericCall2 OnStopMovement; - public event Action OnRemoveAvatar; - public event RequestMapBlocks OnRequestMapBlocks; - public event RequestMapName OnMapNameRequest; - public event TeleportLocationRequest OnTeleportLocationRequest; - public event TeleportLandmarkRequest OnTeleportLandmarkRequest; - public event DisconnectUser OnDisconnectUser; - public event RequestAvatarProperties OnRequestAvatarProperties; - public event SetAlwaysRun OnSetAlwaysRun; - public event FetchInventory OnAgentDataUpdateRequest; - public event TeleportLocationRequest OnSetStartLocationRequest; - public event UpdateAvatarProperties OnUpdateAvatarProperties; - public event CreateNewInventoryItem OnCreateNewInventoryItem; - public event CreateInventoryFolder OnCreateNewInventoryFolder; - public event UpdateInventoryFolder OnUpdateInventoryFolder; - public event MoveInventoryFolder OnMoveInventoryFolder; - public event FetchInventoryDescendents OnFetchInventoryDescendents; - public event PurgeInventoryDescendents OnPurgeInventoryDescendents; - public event FetchInventory OnFetchInventory; - public event RequestTaskInventory OnRequestTaskInventory; - public event UpdateInventoryItem OnUpdateInventoryItem; - public event CopyInventoryItem OnCopyInventoryItem; - public event MoveInventoryItem OnMoveInventoryItem; - public event RemoveInventoryItem OnRemoveInventoryItem; - public event RemoveInventoryFolder OnRemoveInventoryFolder; - public event UDPAssetUploadRequest OnAssetUploadRequest; - public event XferReceive OnXferReceive; - public event RequestXfer OnRequestXfer; - public event ConfirmXfer OnConfirmXfer; - public event AbortXfer OnAbortXfer; - public event RequestTerrain OnRequestTerrain; - public event RezScript OnRezScript; - public event UpdateTaskInventory OnUpdateTaskInventory; - public event MoveTaskInventory OnMoveTaskItem; - public event RemoveTaskInventory OnRemoveTaskItem; - public event RequestAsset OnRequestAsset; - public event UUIDNameRequest OnNameFromUUIDRequest; - public event ParcelAccessListRequest OnParcelAccessListRequest; - public event ParcelAccessListUpdateRequest OnParcelAccessListUpdateRequest; - public event ParcelPropertiesRequest OnParcelPropertiesRequest; - public event ParcelDivideRequest OnParcelDivideRequest; - public event ParcelJoinRequest OnParcelJoinRequest; - public event ParcelPropertiesUpdateRequest OnParcelPropertiesUpdateRequest; - public event ParcelSelectObjects OnParcelSelectObjects; - public event ParcelObjectOwnerRequest OnParcelObjectOwnerRequest; - public event ParcelAbandonRequest OnParcelAbandonRequest; - public event ParcelGodForceOwner OnParcelGodForceOwner; - public event ParcelReclaim OnParcelReclaim; - public event ParcelReturnObjectsRequest OnParcelReturnObjectsRequest; - public event ParcelDeedToGroup OnParcelDeedToGroup; - public event RegionInfoRequest OnRegionInfoRequest; - public event EstateCovenantRequest OnEstateCovenantRequest; - public event FriendActionDelegate OnApproveFriendRequest; - public event FriendActionDelegate OnDenyFriendRequest; - public event FriendshipTermination OnTerminateFriendship; - public event MoneyTransferRequest OnMoneyTransferRequest; - public event EconomyDataRequest OnEconomyDataRequest; - public event MoneyBalanceRequest OnMoneyBalanceRequest; - public event ParcelBuy OnParcelBuy; - public event UUIDNameRequest OnTeleportHomeRequest; - public event UUIDNameRequest OnUUIDGroupNameRequest; - public event ScriptAnswer OnScriptAnswer; - public event RequestPayPrice OnRequestPayPrice; - public event ObjectSaleInfo OnObjectSaleInfo; - public event ObjectBuy OnObjectBuy; - public event BuyObjectInventory OnBuyObjectInventory; - public event AgentSit OnUndo; - public event ForceReleaseControls OnForceReleaseControls; - public event GodLandStatRequest OnLandStatRequest; - public event RequestObjectPropertiesFamily OnObjectGroupRequest; - public event DetailedEstateDataRequest OnDetailedEstateDataRequest; - public event SetEstateFlagsRequest OnSetEstateFlagsRequest; - public event SetEstateTerrainBaseTexture OnSetEstateTerrainBaseTexture; - public event SetEstateTerrainDetailTexture OnSetEstateTerrainDetailTexture; - public event SetEstateTerrainTextureHeights OnSetEstateTerrainTextureHeights; - public event CommitEstateTerrainTextureRequest OnCommitEstateTerrainTextureRequest; - public event SetRegionTerrainSettings OnSetRegionTerrainSettings; - public event BakeTerrain OnBakeTerrain; - public event RequestTerrain OnUploadTerrain; - public event EstateChangeInfo OnEstateChangeInfo; - public event EstateRestartSimRequest OnEstateRestartSimRequest; - public event EstateChangeCovenantRequest OnEstateChangeCovenantRequest; - public event UpdateEstateAccessDeltaRequest OnUpdateEstateAccessDeltaRequest; - public event SimulatorBlueBoxMessageRequest OnSimulatorBlueBoxMessageRequest; - public event EstateBlueBoxMessageRequest OnEstateBlueBoxMessageRequest; - public event EstateDebugRegionRequest OnEstateDebugRegionRequest; - public event EstateTeleportOneUserHomeRequest OnEstateTeleportOneUserHomeRequest; - public event EstateTeleportAllUsersHomeRequest OnEstateTeleportAllUsersHomeRequest; - public event RegionHandleRequest OnRegionHandleRequest; - public event ParcelInfoRequest OnParcelInfoRequest; - public event ScriptReset OnScriptReset; - public event GetScriptRunning OnGetScriptRunning; - public event SetScriptRunning OnSetScriptRunning; - public event UpdateVector OnAutoPilotGo; - public event TerrainUnacked OnUnackedTerrain; - public event ActivateGesture OnActivateGesture; - public event DeactivateGesture OnDeactivateGesture; - public event ObjectOwner OnObjectOwner; - public event DirPlacesQuery OnDirPlacesQuery; - public event DirFindQuery OnDirFindQuery; - public event DirLandQuery OnDirLandQuery; - public event DirPopularQuery OnDirPopularQuery; - public event DirClassifiedQuery OnDirClassifiedQuery; - public event EventInfoRequest OnEventInfoRequest; - public event ParcelSetOtherCleanTime OnParcelSetOtherCleanTime; - public event MapItemRequest OnMapItemRequest; - public event OfferCallingCard OnOfferCallingCard; - public event AcceptCallingCard OnAcceptCallingCard; - public event DeclineCallingCard OnDeclineCallingCard; - public event SoundTrigger OnSoundTrigger; - public event StartLure OnStartLure; - public event TeleportLureRequest OnTeleportLureRequest; - public event NetworkStats OnNetworkStatsUpdate; - public event ClassifiedInfoRequest OnClassifiedInfoRequest; - public event ClassifiedInfoUpdate OnClassifiedInfoUpdate; - public event ClassifiedDelete OnClassifiedDelete; - public event ClassifiedDelete OnClassifiedGodDelete; - public event EventNotificationAddRequest OnEventNotificationAddRequest; - public event EventNotificationRemoveRequest OnEventNotificationRemoveRequest; - public event EventGodDelete OnEventGodDelete; - public event ParcelDwellRequest OnParcelDwellRequest; - public event UserInfoRequest OnUserInfoRequest; - public event UpdateUserInfo OnUpdateUserInfo; - public event RetrieveInstantMessages OnRetrieveInstantMessages; - public event PickDelete OnPickDelete; - public event PickGodDelete OnPickGodDelete; - public event PickInfoUpdate OnPickInfoUpdate; - public event AvatarNotesUpdate OnAvatarNotesUpdate; - public event MuteListRequest OnMuteListRequest; - public event AvatarInterestUpdate OnAvatarInterestUpdate; - public event PlacesQuery OnPlacesQuery; - - #endregion Events - public void ActivateGesture(UUID assetId, UUID gestureId) { } @@ -3367,7 +3342,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP int length = 0; m_avatarTerseUpdates[count].ToBytes(blockbuffer, ref length); length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); - if (size + length > m_packetMTU) + if (size + length > Packet.MTU) break; size += length; } @@ -3611,7 +3586,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP int length = 0; m_primFullUpdates[count].ToBytes(blockbuffer, ref length); length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); - if (size + length > m_packetMTU) + if (size + length > Packet.MTU) break; size += length; } @@ -3699,7 +3674,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP int length = 0; m_primTerseUpdates[count].ToBytes(blockbuffer, ref length); length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); - if (size + length > m_packetMTU) + if (size + length > Packet.MTU) break; size += length; } @@ -4802,7 +4777,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public bool HandleObjectGroupRequest(IClientAPI sender, Packet Pack) { - ObjectGroupPacket ogpack = (ObjectGroupPacket)Pack; if (ogpack.AgentData.SessionID != SessionId) return false; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 2228f39..04c9cb1 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -419,7 +419,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP int dataLength = buffer.DataLength; - // Keep appending ACKs until there is no room left in the packet or there are + // Keep appending ACKs until there is no room left in the buffer or there are // no more ACKs to append uint ackCount = 0; uint ack; @@ -654,11 +654,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Create the LLClientView LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); - clientApi.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler; clientApi.OnLogout += LogoutHandler; - clientApi.OnConnectionClosed += - delegate(IClientAPI client) - { if (client is LLClientView) RemoveClient(((LLClientView)client).UDPClient); }; + clientApi.OnConnectionClosed += ConnectionClosedHandler; // Start the IClientAPI m_scene.ClientManager.Add(circuitCode, clientApi); @@ -808,7 +805,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void LogoutHandler(IClientAPI client) { + client.OnLogout -= LogoutHandler; + client.SendLogoutPacket(); + + if (client is LLClientView) + RemoveClient(((LLClientView)client).UDPClient); + } + + private void ConnectionClosedHandler(IClientAPI client) + { + client.OnConnectionClosed -= ConnectionClosedHandler; + if (client is LLClientView) RemoveClient(((LLClientView)client).UDPClient); } -- cgit v1.1 From 23a334b9f54a1ef5df3b503c165e7b76b746a2b1 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 14:50:03 -0700 Subject: * Rewrote ClientManager to remove Lindenisms from OpenSim core, improve performance by removing locks, and replace LLUDPClientCollection * Removed the confusing (and LL-specific) shutdowncircuit parameter from IClientAPI.Close() * Updated the LLUDP code to only use ClientManager instead of trying to synchronize ClientManager and m_clients * Remove clients asynchronously since it is a very slow operation (including a 2000ms sleep) --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 77 +++------- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 1 - .../ClientStack/LindenUDP/LLUDPClientCollection.cs | 137 ----------------- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 163 +++++++++++---------- 4 files changed, 107 insertions(+), 271 deletions(-) delete mode 100644 OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index bc9cfcf..86d0112 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -403,39 +403,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Client Methods /// - /// Close down the client view. This *must* be the last method called, since the last # - /// statement of CloseCleanup() aborts the thread. + /// Shut down the client view /// - /// - public void Close(bool shutdownCircuit) + public void Close() { m_log.DebugFormat( - "[CLIENT]: Close has been called with shutdownCircuit = {0} for {1} attached to scene {2}", - shutdownCircuit, Name, m_scene.RegionInfo.RegionName); + "[CLIENT]: Close has been called for {0} attached to scene {1}", + Name, m_scene.RegionInfo.RegionName); + + // Remove ourselves from the scene + m_scene.ClientManager.Remove(m_agentId, m_udpClient.RemoteEndPoint); if (m_imageManager != null) + { m_imageManager.Close(); + m_imageManager = null; + } if (m_udpServer != null) - m_udpServer.Flush(); - - // raise an event on the packet server to Shutdown the circuit - // Now, if we raise the event then the packet server will call this method itself, so don't try cleanup - // here otherwise we'll end up calling it twice. - // FIXME: In truth, I might be wrong but this whole business of calling this method twice (with different args) looks - // horribly tangly. Hopefully it should be possible to greatly simplify it. - if (shutdownCircuit) { - if (OnConnectionClosed != null) - OnConnectionClosed(this); - } - else - { - CloseCleanup(shutdownCircuit); + m_udpServer.Flush(); } + + if (OnConnectionClosed != null) + OnConnectionClosed(this); + + CloseCleanup(); } - private void CloseCleanup(bool shutdownCircuit) + private void CloseCleanup() { m_scene.RemoveClient(AgentId); @@ -459,43 +455,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP lock (m_primFullUpdateTimer) m_primFullUpdateTimer.Stop(); - // This is just to give the client a reasonable chance of - // flushing out all it's packets. There should probably - // be a better mechanism here - // We can't reach into other scenes and close the connection // We need to do this over grid communications //m_scene.CloseAllAgents(CircuitCode); - // If we're not shutting down the circuit, then this is the last time we'll go here. - // If we are shutting down the circuit, the UDP Server will come back here with - // ShutDownCircuit = false - if (!(shutdownCircuit)) - { - GC.Collect(); - m_imageManager = null; - // Sends a KillPacket object, with which, the - // blockingqueue dequeues and sees it's a killpacket - // and terminates within the context of the client thread. - // This ensures that it's done from within the context - // of the client thread regardless of where Close() is called. - KillEndDone(); - } - IsActive = false; - m_avatarTerseUpdateTimer.Close(); - m_primTerseUpdateTimer.Close(); - m_primFullUpdateTimer.Close(); + m_avatarTerseUpdateTimer.Dispose(); + m_primTerseUpdateTimer.Dispose(); + m_primFullUpdateTimer.Dispose(); - //m_udpServer.OnPacketStats -= PopulateStats; + // Disable UDP handling for this client m_udpClient.Shutdown(); - - // wait for thread stoped - // m_clientThread.Join(); - - // delete circuit code - //m_networkServer.CloseClient(this); } public void Kick(string message) @@ -10225,7 +10196,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void KillEndDone() { - m_udpClient.Shutdown(); } #region IClientCore @@ -10268,15 +10238,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP { Kick(reason); Thread.Sleep(1000); - Close(true); + Close(); } public void Disconnect() { - Close(true); + Close(); } - #endregion public void RefreshGroupMembership() diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 10e22d5..e5b2594 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -181,7 +181,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public void Shutdown() { - // TODO: Do we need to invalidate the circuit? IsConnected = false; } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs deleted file mode 100644 index 4f375e4..0000000 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections.Generic; -using System.Net; -using OpenSim.Framework; -using OpenMetaverse; -using BclExtras.Collections; - -using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim; - -namespace OpenSim.Region.ClientStack.LindenUDP -{ - /// - /// A thread safe mapping from endpoints to client references - /// - public sealed class UDPClientCollection - { - #region IComparers - - private sealed class IPEndPointComparer : IComparer - { - public int Compare(IPEndPoint x, IPEndPoint y) - { - int result = x.Address.Address.CompareTo(y.Address.Address); - if (result == 0) result = x.Port.CompareTo(y.Port); - return result; - } - } - - #endregion IComparers - - /// An immutable dictionary mapping from - /// to references - private ImmutableMap m_dict; - /// Immutability grants thread safety for concurrent reads and - /// read-writes, but not concurrent writes - private object m_writeLock = new object(); - - /// Number of clients in the collection - public int Count { get { return m_dict.Count; } } - - /// - /// Default constructor - /// - public UDPClientCollection() - { - m_dict = new ImmutableMap(new IPEndPointComparer()); - } - - /// - /// Add a client reference to the collection - /// - /// Remote endpoint of the client - /// Reference to the client object - public void Add(IPEndPoint key, LLUDPClient value) - { - lock (m_writeLock) - m_dict = m_dict.Add(key, value); - } - - /// - /// Remove a client from the collection - /// - /// Remote endpoint of the client - public void Remove(IPEndPoint key) - { - lock (m_writeLock) - m_dict = m_dict.Delete(key); - } - - /// - /// Resets the client collection - /// - public void Clear() - { - lock (m_writeLock) - m_dict = new ImmutableMap(new IPEndPointComparer()); - } - - /// - /// Checks if an endpoint is in the collection - /// - /// Endpoint to check for - /// True if the endpoint was found in the collection, otherwise false - public bool ContainsKey(IPEndPoint key) - { - return m_dict.ContainsKey(key); - } - - /// - /// Attempts to fetch a value out of the collection - /// - /// Endpoint of the client to retrieve - /// Retrieved client, or null on lookup failure - /// True if the lookup succeeded, otherwise false - public bool TryGetValue(IPEndPoint key, out LLUDPClient value) - { - return m_dict.TryGetValue(key, out value); - } - - /// - /// Performs a given task in parallel for each of the elements in the - /// collection - /// - /// Action to perform on each element - public void ForEach(Action action) - { - Parallel.ForEach(m_dict.Values, action); - } - } -} diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 04c9cb1..8ec143a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -96,7 +96,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Incoming packets that are awaiting handling private OpenMetaverse.BlockingQueue packetInbox = new OpenMetaverse.BlockingQueue(); /// - private UDPClientCollection m_clients = new UDPClientCollection(); + //private UDPClientCollection m_clients = new UDPClientCollection(); /// Bandwidth throttle for this UDP server private TokenBucket m_throttle; /// Bandwidth throttle rates for this UDP server @@ -181,23 +181,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return x == m_location; } - public void RemoveClient(LLUDPClient udpClient) - { - m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + udpClient.AgentID); - - // Shut down the IClientAPI and remove it from the scene - IClientAPI client; - if (m_scene.ClientManager.TryGetClient(udpClient.CircuitCode, out client)) - { - client.Close(false); - m_scene.ClientManager.Remove(udpClient.CircuitCode); - } - - // Shut down the LLUDPClient and remove it from the list of UDP clients - udpClient.Shutdown(); - m_clients.Remove(udpClient.RemoteEndPoint); - } - public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) { // CoarseLocationUpdate packets cannot be split in an automated way @@ -215,17 +198,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < packetCount; i++) { byte[] data = datas[i]; - m_clients.ForEach( - delegate(LLUDPClient client) - { SendPacketData(client, data, packet.Type, category); }); + m_scene.ClientManager.ForEach( + delegate(IClientAPI client) + { + if (client is LLClientView) + SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); + } + ); } } else { byte[] data = packet.ToBytes(); - m_clients.ForEach( - delegate(LLUDPClient client) - { SendPacketData(client, data, packet.Type, category); }); + m_scene.ClientManager.ForEach( + delegate(IClientAPI client) + { + if (client is LLClientView) + SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category); + } + ); } } @@ -475,7 +466,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP //try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; } //catch (Exception) { } - LLUDPClient client = null; + LLUDPClient udpClient = null; Packet packet = null; int packetEnd = buffer.DataLength - 1; IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint; @@ -512,30 +503,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP } // Determine which agent this packet came from - if (!m_clients.TryGetValue(address, out client)) + IClientAPI client; + if (!m_scene.ClientManager.TryGetValue(address, out client) || !(client is LLClientView)) { m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + - " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_clients.Count + " clients"); + " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_scene.ClientManager.Count + " clients"); return; } + udpClient = ((LLClientView)client).UDPClient; + #endregion Packet to Client Mapping // Stats tracking - Interlocked.Increment(ref client.PacketsReceived); + Interlocked.Increment(ref udpClient.PacketsReceived); #region ACK Receiving int now = Environment.TickCount; - client.TickLastPacketReceived = now; + udpClient.TickLastPacketReceived = now; // Handle appended ACKs if (packet.Header.AppendedAcks && packet.Header.AckList != null) { - lock (client.NeedAcks.SyncRoot) + lock (udpClient.NeedAcks.SyncRoot) { for (int i = 0; i < packet.Header.AckList.Length; i++) - AcknowledgePacket(client, packet.Header.AckList[i], now, packet.Header.Resent); + AcknowledgePacket(udpClient, packet.Header.AckList[i], now, packet.Header.Resent); } } @@ -544,10 +538,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP { PacketAckPacket ackPacket = (PacketAckPacket)packet; - lock (client.NeedAcks.SyncRoot) + lock (udpClient.NeedAcks.SyncRoot) { for (int i = 0; i < ackPacket.Packets.Length; i++) - AcknowledgePacket(client, ackPacket.Packets[i].ID, now, packet.Header.Resent); + AcknowledgePacket(udpClient, ackPacket.Packets[i].ID, now, packet.Header.Resent); } } @@ -556,27 +550,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region ACK Sending if (packet.Header.Reliable) - client.PendingAcks.Enqueue(packet.Header.Sequence); + udpClient.PendingAcks.Enqueue(packet.Header.Sequence); // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove // 2*MTU bytes from the value and send ACKs, and finally add the local value back to // client.BytesSinceLastACK. Lockless thread safety - int bytesSinceLastACK = Interlocked.Exchange(ref client.BytesSinceLastACK, 0); + int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0); bytesSinceLastACK += buffer.DataLength; if (bytesSinceLastACK > Packet.MTU * 2) { bytesSinceLastACK -= Packet.MTU * 2; - SendAcks(client); + SendAcks(udpClient); } - Interlocked.Add(ref client.BytesSinceLastACK, bytesSinceLastACK); + Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK); #endregion ACK Sending #region Incoming Packet Accounting // Check the archive of received reliable packet IDs to see whether we already received this packet - if (packet.Header.Reliable && !client.PacketArchive.TryEnqueue(packet.Header.Sequence)) + if (packet.Header.Reliable && !udpClient.PacketArchive.TryEnqueue(packet.Header.Sequence)) { if (packet.Header.Resent) m_log.Debug("[LLUDPSERVER]: Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type); @@ -593,7 +587,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (packet.Type != PacketType.PacketAck) { // Inbox insertion - packetInbox.Enqueue(new IncomingPacket(client, packet)); + packetInbox.Enqueue(new IncomingPacket(udpClient, packet)); } } @@ -613,31 +607,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void AddNewClient(UseCircuitCodePacket useCircuitCode, IPEndPoint remoteEndPoint) { + UUID agentID = useCircuitCode.CircuitCode.ID; + UUID sessionID = useCircuitCode.CircuitCode.SessionID; + uint circuitCode = useCircuitCode.CircuitCode.Code; + if (m_scene.RegionStatus != RegionStatus.SlaveScene) { - if (!m_clients.ContainsKey(remoteEndPoint)) + AuthenticateResponse sessionInfo; + if (IsClientAuthorized(useCircuitCode, out sessionInfo)) { - AuthenticateResponse sessionInfo; - if (IsClientAuthorized(useCircuitCode, out sessionInfo)) - { - UUID agentID = useCircuitCode.CircuitCode.ID; - UUID sessionID = useCircuitCode.CircuitCode.SessionID; - uint circuitCode = useCircuitCode.CircuitCode.Code; - - AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo); - } - else - { - // Don't create circuits for unauthorized clients - m_log.WarnFormat( - "[LLUDPSERVER]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", - useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint); - } + AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo); } else { - // Ignore repeated UseCircuitCode packets - m_log.Debug("[LLUDPSERVER]: Ignoring UseCircuitCode for already established circuit " + useCircuitCode.CircuitCode.Code); + // Don't create circuits for unauthorized clients + m_log.WarnFormat( + "[LLUDPSERVER]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", + useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint); } } else @@ -652,17 +638,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Create the LLUDPClient LLUDPClient udpClient = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint); - // Create the LLClientView - LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); - clientApi.OnLogout += LogoutHandler; - clientApi.OnConnectionClosed += ConnectionClosedHandler; + if (!m_scene.ClientManager.ContainsKey(agentID)) + { + // Create the LLClientView + LLClientView client = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); + client.OnLogout += LogoutHandler; + client.OnConnectionClosed += ConnectionClosedHandler; - // Start the IClientAPI - m_scene.ClientManager.Add(circuitCode, clientApi); - clientApi.Start(); + m_scene.ClientManager.Add(agentID, remoteEndPoint, client); - // Add the new client to our list of tracked clients - m_clients.Add(udpClient.RemoteEndPoint, udpClient); + // Start the IClientAPI + m_scene.ClientManager.Add(agentID, remoteEndPoint, client); + client.Start(); + } + else + { + m_log.Debug("[LLUDPSERVER]: Ignoring a repeated UseCircuitCode from " + udpClient.AgentID); + } + } + + private void RemoveClient(LLUDPClient udpClient) + { + // Remove this client from the scene ClientManager + IClientAPI client; + if (m_scene.ClientManager.TryGetValue(udpClient.AgentID, out client)) + Util.FireAndForget(delegate(object o) { client.Close(); }); } private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend) @@ -740,20 +740,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP elapsed500MS = 0; } - m_clients.ForEach( - delegate(LLUDPClient client) + m_scene.ClientManager.ForEach( + delegate(IClientAPI client) { - if (client.DequeueOutgoing()) - packetSent = true; - if (resendUnacked) - ResendUnacked(client); - if (sendAcks) + if (client is LLClientView) { - SendAcks(client); - client.SendPacketStats(); + LLUDPClient udpClient = ((LLClientView)client).UDPClient; + + if (udpClient.DequeueOutgoing()) + packetSent = true; + if (resendUnacked) + ResendUnacked(udpClient); + if (sendAcks) + { + SendAcks(udpClient); + udpClient.SendPacketStats(); + } + if (sendPings) + SendPing(udpClient); } - if (sendPings) - SendPing(client); } ); @@ -777,7 +782,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } // Make sure this client is still alive - if (m_scene.ClientManager.TryGetClient(udpClient.CircuitCode, out client)) + if (m_scene.ClientManager.TryGetValue(udpClient.AgentID, out client)) { try { -- cgit v1.1 From 395a8680c3633ca131e7481f765517311ef51710 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 16:53:19 -0700 Subject: * Fixed a bug where clients were being added to ClientManager twice * Changed the ClientManager interface to reduce potential errors with duplicate or mismatched keys * Added IClientAPI.RemoteEndPoint, which can (hopefully) eventually replace IClientAPI.CircuitCode * Changed the order of operations during client shutdown --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 36 ++++++++---------- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 44 +++++++++++----------- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 9 ++--- 3 files changed, 41 insertions(+), 48 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 86d0112..aecb362 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -411,38 +411,34 @@ namespace OpenSim.Region.ClientStack.LindenUDP "[CLIENT]: Close has been called for {0} attached to scene {1}", Name, m_scene.RegionInfo.RegionName); - // Remove ourselves from the scene - m_scene.ClientManager.Remove(m_agentId, m_udpClient.RemoteEndPoint); + // Send the STOP packet + DisableSimulatorPacket disable = (DisableSimulatorPacket)PacketPool.Instance.GetPacket(PacketType.DisableSimulator); + OutPacket(disable, ThrottleOutPacketType.Unknown); + + IsActive = false; + // Shutdown the image manager if (m_imageManager != null) - { m_imageManager.Close(); - m_imageManager = null; - } - - if (m_udpServer != null) - { - m_udpServer.Flush(); - } + // Fire the callback for this connection closing if (OnConnectionClosed != null) OnConnectionClosed(this); - CloseCleanup(); - } + // Flush all of the packets out of the UDP server for this client + if (m_udpServer != null) + m_udpServer.Flush(m_udpClient); - private void CloseCleanup() - { + // Remove ourselves from the scene m_scene.RemoveClient(AgentId); + m_scene.ClientManager.Remove(this); //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false)); + //GC.Collect(); //m_log.InfoFormat("[CLIENTVIEW] Memory post GC {0}", System.GC.GetTotalMemory(true)); - // Send the STOP packet - DisableSimulatorPacket disable = (DisableSimulatorPacket)PacketPool.Instance.GetPacket(PacketType.DisableSimulator); - OutPacket(disable, ThrottleOutPacketType.Unknown); - - Thread.Sleep(2000); + // FIXME: Is this still necessary? + //Thread.Sleep(2000); // Shut down timers. Thread Context of this method is murky. Lock all timers if (m_avatarTerseUpdateTimer.Enabled) @@ -459,8 +455,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // We need to do this over grid communications //m_scene.CloseAllAgents(CircuitCode); - IsActive = false; - m_avatarTerseUpdateTimer.Dispose(); m_primTerseUpdateTimer.Dispose(); m_primFullUpdateTimer.Dispose(); diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 343f537..8469ba6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -167,39 +167,39 @@ namespace OpenSim.Region.ClientStack.LindenUDP J2KImage imagereq; int numCollected = 0; - //lock (m_syncRoot) - //{ - m_lastloopprocessed = DateTime.Now.Ticks; - - // This can happen during Close() - if (m_client == null) - return false; - - while ((imagereq = GetHighestPriorityImage()) != null) + m_lastloopprocessed = DateTime.Now.Ticks; + + // This can happen during Close() + if (m_client == null) + return false; + + while ((imagereq = GetHighestPriorityImage()) != null) + { + if (imagereq.IsDecoded == true) { - if (imagereq.IsDecoded == true) - { - ++numCollected; + ++numCollected; - if (imagereq.SendPackets(m_client, maxpack)) - { - // Send complete. Destroy any knowledge of this transfer - RemoveImageFromQueue(imagereq); - } + if (imagereq.SendPackets(m_client, maxpack)) + { + // Send complete. Destroy any knowledge of this transfer + RemoveImageFromQueue(imagereq); } - - if (numCollected == count) - break; } - //} + + if (numCollected == count) + break; + } return m_priorityQueue.Count > 0; } - //Faux destructor + /// + /// Faux destructor + /// public void Close() { m_shuttingdown = true; + m_priorityQueue = null; m_j2kDecodeModule = null; m_assetCache = null; m_client = null; diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 8ec143a..22c275c 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -390,7 +390,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public void Flush() + public void Flush(LLUDPClient udpClient) { // FIXME: Implement? } @@ -645,15 +645,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP client.OnLogout += LogoutHandler; client.OnConnectionClosed += ConnectionClosedHandler; - m_scene.ClientManager.Add(agentID, remoteEndPoint, client); - // Start the IClientAPI - m_scene.ClientManager.Add(agentID, remoteEndPoint, client); + m_scene.ClientManager.Add(client); client.Start(); } else { - m_log.Debug("[LLUDPSERVER]: Ignoring a repeated UseCircuitCode from " + udpClient.AgentID); + m_log.WarnFormat("[LLUDPSERVER]: Ignoring a repeated UseCircuitCode from {0} at {1} for circuit {2}", + udpClient.AgentID, remoteEndPoint, circuitCode); } } -- cgit v1.1 From dc11643c007adf7a640ec4fbabe25995352aaa18 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 17:33:45 -0700 Subject: * Consolidated adding / removing ClientManager IClientAPIs to two places in Scene * Added some missing implementations of IClientAPI.RemoteEndPoint * Added a ClientManager.Remove(UUID) overload * Removed a reference to a missing project from prebuild.xml --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 61 +--------------------- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 13 ++--- 2 files changed, 5 insertions(+), 69 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index aecb362..0acf6e8 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -324,6 +324,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Properties public LLUDPClient UDPClient { get { return m_udpClient; } } + public IPEndPoint RemoteEndPoint { get { return m_udpClient.RemoteEndPoint; } } public UUID SecureSessionId { get { return m_secureSessionId; } } public IScene Scene { get { return m_scene; } } public UUID SessionId { get { return m_sessionId; } } @@ -431,7 +432,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Remove ourselves from the scene m_scene.RemoveClient(AgentId); - m_scene.ClientManager.Remove(this); //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false)); //GC.Collect(); @@ -586,11 +586,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP # region Setup - /// - /// Starts up the timers to check the client and resend unacked packets - /// Adds the client to the OpenSim.Region.Framework.Scenes.Scene - /// - protected virtual void InitNewClient() + public virtual void Start() { m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate); m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); @@ -609,59 +605,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP RefreshGroupMembership(); } - public virtual void Start() - { - // This sets up all the timers - InitNewClient(); - } - - /// - /// Run a user session. This method lies at the base of the entire client thread. - /// - protected void RunUserSession() - { - try - { - - } - catch (Exception e) - { - if (e is ThreadAbortException) - throw; - - if (StatsManager.SimExtraStats != null) - StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); - - // Don't let a failure in an individual client thread crash the whole sim. - m_log.ErrorFormat( - "[CLIENT]: Client thread for {0} {1} crashed. Logging them out.", Name, AgentId); - m_log.Error(e.ToString()); - - try - { - // Make an attempt to alert the user that their session has crashed - AgentAlertMessagePacket packet - = BuildAgentAlertPacket( - "Unfortunately the session for this client on the server has crashed.\n" - + "Any further actions taken will not be processed.\n" - + "Please relog", true); - - OutPacket(packet, ThrottleOutPacketType.Unknown); - - // There may be a better way to do this. Perhaps kick? Not sure this propogates notifications to - // listeners yet, though. - Logout(this); - } - catch (Exception e2) - { - if (e2 is ThreadAbortException) - throw; - - m_log.ErrorFormat("[CLIENT]: Further exception thrown on forced session logout. {0}", e2); - } - } - } - # endregion public void ActivateGesture(UUID assetId, UUID gestureId) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 22c275c..8689af3 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -646,7 +646,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP client.OnConnectionClosed += ConnectionClosedHandler; // Start the IClientAPI - m_scene.ClientManager.Add(client); client.Start(); } else @@ -658,10 +657,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void RemoveClient(LLUDPClient udpClient) { - // Remove this client from the scene ClientManager + // Remove this client from the scene IClientAPI client; if (m_scene.ClientManager.TryGetValue(udpClient.AgentID, out client)) - Util.FireAndForget(delegate(object o) { client.Close(); }); + client.Close(); } private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend) @@ -810,19 +809,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void LogoutHandler(IClientAPI client) { client.OnLogout -= LogoutHandler; - client.SendLogoutPacket(); - - if (client is LLClientView) - RemoveClient(((LLClientView)client).UDPClient); } private void ConnectionClosedHandler(IClientAPI client) { client.OnConnectionClosed -= ConnectionClosedHandler; - - if (client is LLClientView) - RemoveClient(((LLClientView)client).UDPClient); + RemoveClient(((LLClientView)client).UDPClient); } } } -- cgit v1.1 From e8c1e69a0dbab1a7db894eeff6b052bbd350a8f5 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 18:56:54 -0700 Subject: * Copied LocklessQueue.cs into OpenSim.Framework and added the .Count property and .Clear() method * Changed the way the QueueEmpty callback is fired. It will be fired asynchronously as soon as an empty queue is detected (this can happen immediately following a dequeue), and will not be fired again until at least one packet is dequeued from that queue. This will give callbacks advanced notice of an empty queue and prevent callbacks from stacking up while the queue is empty * Added LLUDPClient.IsConnected checks in several places to prevent unwanted network activity after a client disconnects * Prevent LLClientView.Close() from being called twice every disconnect * Removed the packet resend limit and improved the client timeout check --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 11 +-- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 6 +- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 50 ++++++++-- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 107 +++++++++------------ .../LindenUDP/UnackedPacketCollection.cs | 9 ++ 5 files changed, 103 insertions(+), 80 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 0acf6e8..ac558ff 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -433,13 +433,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Remove ourselves from the scene m_scene.RemoveClient(AgentId); - //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false)); - //GC.Collect(); - //m_log.InfoFormat("[CLIENTVIEW] Memory post GC {0}", System.GC.GetTotalMemory(true)); - - // FIXME: Is this still necessary? - //Thread.Sleep(2000); - // Shut down timers. Thread Context of this method is murky. Lock all timers if (m_avatarTerseUpdateTimer.Enabled) lock (m_avatarTerseUpdateTimer) @@ -461,6 +454,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Disable UDP handling for this client m_udpClient.Shutdown(); + + //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false)); + //GC.Collect(); + //m_log.InfoFormat("[CLIENTVIEW] Memory post GC {0}", System.GC.GetTotalMemory(true)); } public void Kick(string message) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 8469ba6..8410ee9 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -213,13 +213,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP lock (m_syncRoot) { - if (m_priorityQueue.Count > 0) { - try - { - image = m_priorityQueue.FindMax(); - } + try { image = m_priorityQueue.FindMax(); } catch (Exception) { } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index e5b2594..b27d8d6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -80,7 +80,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Packets we have sent that need to be ACKed by the client public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); /// ACKs that are queued up, waiting to be sent to the client - public readonly LocklessQueue PendingAcks = new LocklessQueue(); + public readonly OpenSim.Framework.LocklessQueue PendingAcks = new OpenSim.Framework.LocklessQueue(); /// Current packet sequence number public int CurrentSequence; @@ -127,13 +127,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Throttle rate defaults and limits private readonly ThrottleRates defaultThrottleRates; /// Outgoing queues for throttled packets - private readonly LocklessQueue[] packetOutboxes = new LocklessQueue[THROTTLE_CATEGORY_COUNT]; + private readonly OpenSim.Framework.LocklessQueue[] packetOutboxes = new OpenSim.Framework.LocklessQueue[THROTTLE_CATEGORY_COUNT]; /// A container that can hold one packet for each outbox, used to store /// dequeued packets that are being held for throttling private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; /// An optimization to store the length of dequeued packets being held /// for throttling. This avoids expensive calls to Packet.Length private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT]; + /// Flags to prevent queue empty callbacks from repeatedly firing + /// before the callbacks have a chance to put packets in the queue + private readonly bool[] queueEmptySent = new bool[THROTTLE_CATEGORY_COUNT]; /// A reference to the LLUDPServer that is managing this client private readonly LLUDPServer udpServer; @@ -156,7 +159,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP defaultThrottleRates = rates; for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) - packetOutboxes[i] = new LocklessQueue(); + packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); throttle = new TokenBucket(parentThrottle, 0, 0); throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; @@ -182,6 +185,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void Shutdown() { IsConnected = false; + NeedAcks.Clear(); + for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) + { + packetOutboxes[i].Clear(); + nextPackets[i] = null; + } + OnPacketStats = null; + OnQueueEmpty = null; } /// @@ -322,7 +333,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (category >= 0 && category < packetOutboxes.Length) { - LocklessQueue queue = packetOutboxes[category]; + OpenSim.Framework.LocklessQueue queue = packetOutboxes[category]; TokenBucket bucket = throttleCategories[category]; if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength)) @@ -354,7 +365,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public bool DequeueOutgoing() { OutgoingPacket packet; - LocklessQueue queue; + OpenSim.Framework.LocklessQueue queue; TokenBucket bucket; bool packetSent = false; @@ -382,6 +393,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP queue = packetOutboxes[i]; if (queue.Dequeue(out packet)) { + // Reset the flag for firing this queue's OnQueueEmpty callback + // now that we have dequeued a packet + queueEmptySent[i] = false; + // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out if (bucket.RemoveTokens(packet.Buffer.DataLength)) @@ -397,13 +412,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP nextPackets[i] = packet; nextPacketLengths[i] = packet.Buffer.DataLength; } + + // If the queue is empty after this dequeue, fire the queue + // empty callback now so it has a chance to fill before we + // get back here + if (queue.Count == 0) + FireQueueEmpty(i); } else { // No packets in this queue. Fire the queue empty callback - QueueEmpty callback = OnQueueEmpty; - if (callback != null) - callback((ThrottleOutPacketType)i); + // if it has not been called recently + FireQueueEmpty(i); } } } @@ -432,8 +452,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Always round retransmission timeout up to two seconds RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR))); - //Logger.Debug("Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " + + //m_log.Debug("[LLUDPCLIENT]: Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " + // RTTVAR + " based on new RTT of " + r + "ms"); } + + private void FireQueueEmpty(int queueIndex) + { + if (!queueEmptySent[queueIndex]) + { + queueEmptySent[queueIndex] = true; + + QueueEmpty callback = OnQueueEmpty; + if (callback != null) + Util.FireAndForget(delegate(object o) { callback((ThrottleOutPacketType)(int)o); }, queueIndex); + } + } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 8689af3..57fee59 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -332,8 +332,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void ResendUnacked(LLUDPClient udpClient) { - if (udpClient.NeedAcks.Count > 0) + if (udpClient.IsConnected && udpClient.NeedAcks.Count > 0) { + // Disconnect an agent if no packets are received for some time + //FIXME: Make 60 an .ini setting + if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60) + { + m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); + + RemoveClient(udpClient); + return; + } + + // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO List expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO); if (expiredPackets != null) @@ -343,48 +354,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP { OutgoingPacket outgoingPacket = expiredPackets[i]; - // FIXME: Make this an .ini setting - if (outgoingPacket.ResendCount < 3) - { - //Logger.Debug(String.Format("Resending packet #{0} (attempt {1}), {2}ms have passed", - // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount)); + //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed", + // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount); - // Set the resent flag - outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); - outgoingPacket.Category = ThrottleOutPacketType.Resend; + // Set the resent flag + outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); + outgoingPacket.Category = ThrottleOutPacketType.Resend; - // The TickCount will be set to the current time when the packet - // is actually sent out again - outgoingPacket.TickCount = 0; + // The TickCount will be set to the current time when the packet + // is actually sent out again + outgoingPacket.TickCount = 0; - // Bump up the resend count on this packet - Interlocked.Increment(ref outgoingPacket.ResendCount); - //Interlocked.Increment(ref Stats.ResentPackets); + // Bump up the resend count on this packet + Interlocked.Increment(ref outgoingPacket.ResendCount); + //Interlocked.Increment(ref Stats.ResentPackets); - // Queue or (re)send the packet - if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) - SendPacketFinal(outgoingPacket); - } - else - { - m_log.DebugFormat("[LLUDPSERVER]: Dropping packet #{0} for agent {1} after {2} failed attempts", - outgoingPacket.SequenceNumber, outgoingPacket.Client.RemoteEndPoint, outgoingPacket.ResendCount); - - lock (udpClient.NeedAcks.SyncRoot) - udpClient.NeedAcks.RemoveUnsafe(outgoingPacket.SequenceNumber); - - //Interlocked.Increment(ref Stats.DroppedPackets); - - // Disconnect an agent if no packets are received for some time - //FIXME: Make 60 an .ini setting - if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60) - { - m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); - - RemoveClient(udpClient); - return; - } - } + // Requeue or resend the packet + if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) + SendPacketFinal(outgoingPacket); } } } @@ -403,6 +390,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; LLUDPClient udpClient = outgoingPacket.Client; + if (!udpClient.IsConnected) + return; + // Keep track of when this packet was sent out (right now) outgoingPacket.TickCount = Environment.TickCount; @@ -481,14 +471,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP } catch (MalformedDataException) { - m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet:\n{0}", - Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); + m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet from {0}:\n{1}", + buffer.RemoteEndPoint, Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); } // Fail-safe check if (packet == null) { - m_log.Warn("[LLUDPSERVER]: Couldn't build a message from the incoming data"); + m_log.Warn("[LLUDPSERVER]: Couldn't build a message from incoming data " + buffer.DataLength + + " bytes long from " + buffer.RemoteEndPoint); return; } @@ -513,6 +504,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP udpClient = ((LLClientView)client).UDPClient; + if (!udpClient.IsConnected) + return; + #endregion Packet to Client Mapping // Stats tracking @@ -643,7 +637,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Create the LLClientView LLClientView client = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); client.OnLogout += LogoutHandler; - client.OnConnectionClosed += ConnectionClosedHandler; // Start the IClientAPI client.Start(); @@ -745,17 +738,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP { LLUDPClient udpClient = ((LLClientView)client).UDPClient; - if (udpClient.DequeueOutgoing()) - packetSent = true; - if (resendUnacked) - ResendUnacked(udpClient); - if (sendAcks) + if (udpClient.IsConnected) { - SendAcks(udpClient); - udpClient.SendPacketStats(); + if (udpClient.DequeueOutgoing()) + packetSent = true; + if (resendUnacked) + ResendUnacked(udpClient); + if (sendAcks) + { + SendAcks(udpClient); + udpClient.SendPacketStats(); + } + if (sendPings) + SendPing(udpClient); } - if (sendPings) - SendPing(udpClient); } } ); @@ -808,14 +804,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void LogoutHandler(IClientAPI client) { - client.OnLogout -= LogoutHandler; client.SendLogoutPacket(); } - - private void ConnectionClosedHandler(IClientAPI client) - { - client.OnConnectionClosed -= ConnectionClosedHandler; - RemoveClient(((LLClientView)client).UDPClient); - } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs index 195ca57..f3242c1 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs @@ -103,6 +103,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP } /// + /// Removes all elements from the collection + /// + public void Clear() + { + lock (SyncRoot) + packets.Clear(); + } + + /// /// Gets the packet with the lowest sequence number /// /// The packet with the lowest sequence number, or null if the -- cgit v1.1 From 4135b0c4dcd142fe43b4c1b020d41a72d9df63dd Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 13 Oct 2009 19:45:38 -0700 Subject: * Split Task category into Task and State * Crude prioritization hack --- .../Region/ClientStack/LindenUDP/LLClientView.cs | 13 ++--- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 62 +++++++++++++--------- 2 files changed, 45 insertions(+), 30 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index ac558ff..3b1a0bd 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -1223,7 +1223,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP kill.ObjectData[0].ID = localID; kill.Header.Reliable = true; kill.Header.Zerocoded = true; - OutPacket(kill, ThrottleOutPacketType.Task); + OutPacket(kill, ThrottleOutPacketType.State); } /// @@ -1817,7 +1817,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP sendXfer.XferID.ID = xferID; sendXfer.XferID.Packet = packet; sendXfer.DataPacket.Data = data; - OutPacket(sendXfer, ThrottleOutPacketType.Task); + OutPacket(sendXfer, ThrottleOutPacketType.Asset); } public void SendEconomyData(float EnergyEfficiency, int ObjectCapacity, int ObjectCount, int PriceEnergyUnit, @@ -2099,7 +2099,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP packet.AgentData.SessionID = SessionId; packet.Header.Reliable = false; packet.Header.Zerocoded = true; - OutPacket(packet, ThrottleOutPacketType.Task); + OutPacket(packet, ThrottleOutPacketType.State); } public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, @@ -3122,7 +3122,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP avp.Sender.IsTrial = false; avp.Sender.ID = agentID; - OutPacket(avp, ThrottleOutPacketType.Task); + OutPacket(avp, ThrottleOutPacketType.State); } public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) @@ -3262,6 +3262,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP terse.Header.Reliable = false; terse.Header.Zerocoded = true; + // FIXME: Move this to ThrottleOutPacketType.State when the real prioritization code is committed OutPacket(terse, ThrottleOutPacketType.Task); if (m_avatarTerseUpdates.Count == 0) @@ -3506,7 +3507,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } outPacket.Header.Zerocoded = true; - OutPacket(outPacket, ThrottleOutPacketType.Task); + OutPacket(outPacket, ThrottleOutPacketType.State); if (m_primFullUpdates.Count == 0 && m_primFullUpdateTimer.Enabled) lock (m_primFullUpdateTimer) @@ -3596,7 +3597,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP outPacket.Header.Reliable = false; outPacket.Header.Zerocoded = true; - OutPacket(outPacket, ThrottleOutPacketType.Task); + OutPacket(outPacket, ThrottleOutPacketType.State); if (m_primTerseUpdates.Count == 0) lock (m_primTerseUpdateTimer) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index b27d8d6..e7707a9 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -59,9 +59,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public sealed class LLUDPClient { + // FIXME: Make this a config setting + /// Percentage of the task throttle category that is allocated to avatar and prim + /// state updates + const float STATE_TASK_PERCENTAGE = 0.8f; + /// The number of packet categories to throttle on. If a throttle category is added /// or removed, this number must also change - const int THROTTLE_CATEGORY_COUNT = 7; + const int THROTTLE_CATEGORY_COUNT = 8; /// Fired when updated networking stats are produced for this client public event PacketStats OnPacketStats; @@ -134,9 +139,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// An optimization to store the length of dequeued packets being held /// for throttling. This avoids expensive calls to Packet.Length private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT]; - /// Flags to prevent queue empty callbacks from repeatedly firing - /// before the callbacks have a chance to put packets in the queue - private readonly bool[] queueEmptySent = new bool[THROTTLE_CATEGORY_COUNT]; /// A reference to the LLUDPServer that is managing this client private readonly LLUDPServer udpServer; @@ -167,9 +169,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land); throttleCategories[(int)ThrottleOutPacketType.Wind] = new TokenBucket(throttle, rates.WindLimit, rates.Wind); throttleCategories[(int)ThrottleOutPacketType.Cloud] = new TokenBucket(throttle, rates.CloudLimit, rates.Cloud); - throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit, rates.Task); throttleCategories[(int)ThrottleOutPacketType.Texture] = new TokenBucket(throttle, rates.TextureLimit, rates.Texture); throttleCategories[(int)ThrottleOutPacketType.Asset] = new TokenBucket(throttle, rates.AssetLimit, rates.Asset); + // State and Transaction are actually sub-categories of the LLUDP generic "Task" category + TokenBucket stateBucket = new TokenBucket(throttle, (int)((float)rates.TaskLimit * STATE_TASK_PERCENTAGE), (int)((float)rates.Task * STATE_TASK_PERCENTAGE)); + throttleCategories[(int)ThrottleOutPacketType.State] = stateBucket; + throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit - stateBucket.MaxBurst, rates.Task - stateBucket.DripRate); // Set the granularity variable used for retransmission calculations to // the measured resolution of Environment.TickCount @@ -177,6 +182,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Default the retransmission timeout to three seconds RTO = 3000; + + // Initialize this to a sane value to prevent early disconnects + TickLastPacketReceived = Environment.TickCount; } /// @@ -212,7 +220,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP info.landThrottle = throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; info.windThrottle = throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; info.cloudThrottle = throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; - info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.State].DripRate + throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; info.assetThrottle = throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; info.textureThrottle = throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + @@ -309,7 +317,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Task].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)(throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + + throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; @@ -318,12 +327,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SetThrottle(ThrottleOutPacketType category, int rate) { - int i = (int)category; - if (i >= 0 && i < throttleCategories.Length) + if (category == ThrottleOutPacketType.Task) + { + TokenBucket stateBucket = throttleCategories[(int)ThrottleOutPacketType.State]; + TokenBucket taskBucket = throttleCategories[(int)ThrottleOutPacketType.Task]; + + stateBucket.MaxBurst = (int)((float)rate * STATE_TASK_PERCENTAGE); + stateBucket.DripRate = (int)((float)rate * STATE_TASK_PERCENTAGE); + + taskBucket.MaxBurst = rate - stateBucket.MaxBurst; + taskBucket.DripRate = rate - stateBucket.DripRate; + } + else { - TokenBucket bucket = throttleCategories[(int)category]; - bucket.MaxBurst = rate; - bucket.DripRate = rate; + int i = (int)category; + if (i >= 0 && i < throttleCategories.Length) + { + TokenBucket bucket = throttleCategories[(int)category]; + bucket.MaxBurst = rate; + bucket.DripRate = rate; + } } } @@ -393,10 +416,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP queue = packetOutboxes[i]; if (queue.Dequeue(out packet)) { - // Reset the flag for firing this queue's OnQueueEmpty callback - // now that we have dequeued a packet - queueEmptySent[i] = false; - // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out if (bucket.RemoveTokens(packet.Buffer.DataLength)) @@ -458,14 +477,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void FireQueueEmpty(int queueIndex) { - if (!queueEmptySent[queueIndex]) - { - queueEmptySent[queueIndex] = true; - - QueueEmpty callback = OnQueueEmpty; - if (callback != null) - Util.FireAndForget(delegate(object o) { callback((ThrottleOutPacketType)(int)o); }, queueIndex); - } + QueueEmpty callback = OnQueueEmpty; + if (callback != null) + Util.FireAndForget(delegate(object o) { callback((ThrottleOutPacketType)(int)o); }, queueIndex); } } } -- cgit v1.1 From 0d2e6463d714bce8a6a628bd647c625feeeae8f6 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Oct 2009 11:43:31 -0700 Subject: * Minimized the number of times textures are pulled off the priority queue * OnQueueEmpty is still called async, but will not be called for a given category if the previous callback for that category is still running. This is the most balanced behavior I could find, and seems to work well * Added support for the old [ClientStack.LindenUDP] settings (including setting the receive buffer size) and added the new token bucket and global throttle settings * Added the AssetLoaderEnabled config variable to optionally disable loading assets from XML every startup. This gives a dramatic improvement in startup times for those who don't need the functionality every startup --- OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs | 25 +++++---- .../Region/ClientStack/LindenUDP/LLClientView.cs | 7 ++- .../Region/ClientStack/LindenUDP/LLImageManager.cs | 48 ++++++++-------- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 65 +++++++++++++++++----- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 12 +++- .../Region/ClientStack/LindenUDP/OpenSimUDPBase.cs | 15 ++++- .../Region/ClientStack/LindenUDP/ThrottleRates.cs | 36 +++++++----- 7 files changed, 141 insertions(+), 67 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs index 5877779..9ded390 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs @@ -72,14 +72,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_imageManager = imageManager; } - public bool SendPackets(LLClientView client, int maxpack) + /// + /// Sends packets for this texture to a client until packetsToSend is + /// hit or the transfer completes + /// + /// Reference to the client that the packets are destined for + /// Maximum number of packets to send during this call + /// Number of packets sent during this call + /// True if the transfer completes at the current discard level, otherwise false + public bool SendPackets(LLClientView client, int packetsToSend, out int packetsSent) { - if (client == null) - return false; + packetsSent = 0; if (m_currentPacket <= m_stopPacket) { - int count = 0; bool sendMore = true; if (!m_sentInfo || (m_currentPacket == 0)) @@ -88,25 +94,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_sentInfo = true; ++m_currentPacket; - ++count; + ++packetsSent; } if (m_currentPacket < 2) { m_currentPacket = 2; } - while (sendMore && count < maxpack && m_currentPacket <= m_stopPacket) + while (sendMore && packetsSent < packetsToSend && m_currentPacket <= m_stopPacket) { sendMore = SendPacket(client); ++m_currentPacket; - ++count; + ++packetsSent; } - - if (m_currentPacket > m_stopPacket) - return true; } - return false; + return (m_currentPacket > m_stopPacket); } public void RunUpdate() diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index 3b1a0bd..9afff5a 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -313,10 +313,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected int m_primFullUpdatesPerPacket = 14; protected int m_primTerseUpdateRate = 10; protected int m_primFullUpdateRate = 14; - protected int m_textureSendLimit = 20; - protected int m_textureDataLimit = 10; protected int m_avatarTerseUpdateRate = 50; protected int m_avatarTerseUpdatesPerPacket = 5; + /// Number of texture packets to put on the queue each time the + /// OnQueueEmpty event is triggered for the texture category + protected int m_textureSendLimit = 20; protected IAssetService m_assetService; #endregion Class Members @@ -3453,7 +3454,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP void ProcessTextureRequests() { if (m_imageManager != null) - m_imageManager.ProcessImageQueue(m_textureSendLimit, m_textureDataLimit); + m_imageManager.ProcessImageQueue(m_textureSendLimit); } void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e) diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 8410ee9..9d36cff 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -162,32 +162,38 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public bool ProcessImageQueue(int count, int maxpack) + public bool ProcessImageQueue(int packetsToSend) { - J2KImage imagereq; - int numCollected = 0; - m_lastloopprocessed = DateTime.Now.Ticks; + int packetsSent = 0; - // This can happen during Close() - if (m_client == null) - return false; - - while ((imagereq = GetHighestPriorityImage()) != null) + while (packetsSent < packetsToSend) { - if (imagereq.IsDecoded == true) + J2KImage image = GetHighestPriorityImage(); + + // If null was returned, the texture priority queue is currently empty + if (image == null) + return false; + + if (image.IsDecoded) { - ++numCollected; + int sent; + bool imageDone = image.SendPackets(m_client, packetsToSend - packetsSent, out sent); - if (imagereq.SendPackets(m_client, maxpack)) - { - // Send complete. Destroy any knowledge of this transfer - RemoveImageFromQueue(imagereq); - } - } + packetsSent += sent; - if (numCollected == count) - break; + // If the send is complete, destroy any knowledge of this transfer + if (imageDone) + RemoveImageFromQueue(image); + } + else + { + // TODO: This is a limitation of how LLImageManager is currently + // written. Undecoded textures should not be going into the priority + // queue, because a high priority undecoded texture will clog up the + // pipeline for a client + return true; + } } return m_priorityQueue.Count > 0; @@ -199,10 +205,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void Close() { m_shuttingdown = true; - m_priorityQueue = null; - m_j2kDecodeModule = null; - m_assetCache = null; - m_client = null; } #region Priority Queue Helpers diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index e7707a9..39472cb 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; using System.Net; +using log4net; using OpenSim.Framework; using OpenMetaverse; @@ -59,6 +60,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public sealed class LLUDPClient { + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + // FIXME: Make this a config setting /// Percentage of the task throttle category that is allocated to avatar and prim /// state updates @@ -136,9 +139,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// A container that can hold one packet for each outbox, used to store /// dequeued packets that are being held for throttling private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; - /// An optimization to store the length of dequeued packets being held - /// for throttling. This avoids expensive calls to Packet.Length - private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT]; + /// Flags to prevent queue empty callbacks from stacking up on + /// top of each other + private readonly bool[] onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT]; /// A reference to the LLUDPServer that is managing this client private readonly LLUDPServer udpServer; @@ -163,7 +166,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); - throttle = new TokenBucket(parentThrottle, 0, 0); + throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; throttleCategories[(int)ThrottleOutPacketType.Resend] = new TokenBucket(throttle, rates.ResendLimit, rates.Resend); throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land); @@ -401,10 +404,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP // This bucket was empty the last time we tried to send a packet, // leaving a dequeued packet still waiting to be sent out. Try to // send it again - if (bucket.RemoveTokens(nextPacketLengths[i])) + OutgoingPacket nextPacket = nextPackets[i]; + if (bucket.RemoveTokens(nextPacket.Buffer.DataLength)) { // Send the packet - udpServer.SendPacketFinal(nextPackets[i]); + udpServer.SendPacketFinal(nextPacket); nextPackets[i] = null; packetSent = true; } @@ -426,23 +430,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - // Save the dequeued packet and the length calculation for - // the next iteration + // Save the dequeued packet for the next iteration nextPackets[i] = packet; - nextPacketLengths[i] = packet.Buffer.DataLength; } // If the queue is empty after this dequeue, fire the queue // empty callback now so it has a chance to fill before we // get back here if (queue.Count == 0) - FireQueueEmpty(i); + BeginFireQueueEmpty(i); } else { // No packets in this queue. Fire the queue empty callback // if it has not been called recently - FireQueueEmpty(i); + BeginFireQueueEmpty(i); } } } @@ -450,6 +452,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP return packetSent; } + /// + /// Called when an ACK packet is received and a round-trip time for a + /// packet is calculated. This is used to calculate the smoothed + /// round-trip time, round trip time variance, and finally the + /// retransmission timeout + /// + /// Round-trip time of a single packet and its + /// acknowledgement public void UpdateRoundTrip(float r) { const float ALPHA = 0.125f; @@ -475,11 +485,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP // RTTVAR + " based on new RTT of " + r + "ms"); } - private void FireQueueEmpty(int queueIndex) + /// + /// Does an early check to see if this queue empty callback is already + /// running, then asynchronously firing the event + /// + /// Throttle category to fire the callback + /// for + private void BeginFireQueueEmpty(int throttleIndex) { + if (!onQueueEmptyRunning[throttleIndex]) + Util.FireAndForget(FireQueueEmpty, throttleIndex); + } + + /// + /// Checks to see if this queue empty callback is already running, + /// then firing the event + /// + /// Throttle category to fire the callback for, stored + /// as an object to match the WaitCallback delegate signature + private void FireQueueEmpty(object o) + { + int i = (int)o; + ThrottleOutPacketType type = (ThrottleOutPacketType)i; QueueEmpty callback = OnQueueEmpty; + if (callback != null) - Util.FireAndForget(delegate(object o) { callback((ThrottleOutPacketType)(int)o); }, queueIndex); + { + if (!onQueueEmptyRunning[i]) + { + onQueueEmptyRunning[i] = true; + try { callback(type); } + catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); } + onQueueEmptyRunning[i] = false; + } + } } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 57fee59..1cfde91 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -109,6 +109,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP private Location m_location; /// The measured resolution of Environment.TickCount private float m_tickCountResolution; + /// The size of the receive buffer for the UDP socket. This value + /// is passed up to the operating system and used in the system networking + /// stack. Use zero to leave this value as the default + private int m_recvBufferSize; /// The measured resolution of Environment.TickCount public float TickCountResolution { get { return m_tickCountResolution; } } @@ -135,6 +139,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_circuitManager = circuitManager; + IConfig config = configSource.Configs["ClientStack.LindenUDP"]; + if (config != null) + { + m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0); + } + // TODO: Config support for throttling the entire connection m_throttle = new TokenBucket(null, 0, 0); m_throttleRates = new ThrottleRates(configSource); @@ -145,7 +155,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_scene == null) throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); - base.Start(); + base.Start(m_recvBufferSize); // Start the incoming packet processing thread Thread incomingThread = new Thread(IncomingPacketHandler); diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs index fad2ea8..44a6ed6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs @@ -73,6 +73,7 @@ namespace OpenMetaverse /// /// Local IP address to bind the server to /// Port to listening for incoming UDP packets on + /// public OpenSimUDPBase(IPAddress bindAddress, int port) { m_localBindAddress = bindAddress; @@ -82,25 +83,31 @@ namespace OpenMetaverse /// /// Start the UDP server /// + /// The size of the receive buffer for + /// the UDP socket. This value is passed up to the operating system + /// and used in the system networking stack. Use zero to leave this + /// value as the default /// This method will attempt to set the SIO_UDP_CONNRESET flag /// on the socket to get newer versions of Windows to behave in a sane /// manner (not throwing an exception when the remote side resets the /// connection). This call is ignored on Mono where the flag is not /// necessary - public void Start() + public void Start(int recvBufferSize) { if (m_shutdownFlag) { const int SIO_UDP_CONNRESET = -1744830452; IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort); + m_udpSocket = new Socket( AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); + try { - // this udp socket flag is not supported under mono, + // This udp socket flag is not supported under mono, // so we'll catch the exception and continue m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag set"); @@ -109,6 +116,10 @@ namespace OpenMetaverse { m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring"); } + + if (recvBufferSize != 0) + m_udpSocket.ReceiveBufferSize = recvBufferSize; + m_udpSocket.Bind(ipep); // we're not shutting down, we're starting up diff --git a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs index 858a03c..adad4c3 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs @@ -51,6 +51,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public int Texture; /// Drip rate for asset packets public int Asset; + /// Drip rate for the parent token bucket + public int Total; /// Maximum burst rate for resent packets public int ResendLimit; @@ -66,6 +68,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public int TextureLimit; /// Maximum burst rate for asset packets public int AssetLimit; + /// Burst rate for the parent token bucket + public int TotalLimit; /// /// Default constructor @@ -77,21 +81,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP { IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"]; - Resend = throttleConfig.GetInt("ResendDefault", 12500); - Land = throttleConfig.GetInt("LandDefault", 500); - Wind = throttleConfig.GetInt("WindDefault", 500); - Cloud = throttleConfig.GetInt("CloudDefault", 500); - Task = throttleConfig.GetInt("TaskDefault", 500); - Texture = throttleConfig.GetInt("TextureDefault", 500); - Asset = throttleConfig.GetInt("AssetDefault", 500); + Resend = throttleConfig.GetInt("resend_default", 12500); + Land = throttleConfig.GetInt("land_default", 500); + Wind = throttleConfig.GetInt("wind_default", 500); + Cloud = throttleConfig.GetInt("cloud_default", 500); + Task = throttleConfig.GetInt("task_default", 500); + Texture = throttleConfig.GetInt("texture_default", 500); + Asset = throttleConfig.GetInt("asset_default", 500); - ResendLimit = throttleConfig.GetInt("ResendLimit", 18750); - LandLimit = throttleConfig.GetInt("LandLimit", 29750); - WindLimit = throttleConfig.GetInt("WindLimit", 18750); - CloudLimit = throttleConfig.GetInt("CloudLimit", 18750); - TaskLimit = throttleConfig.GetInt("TaskLimit", 55750); - TextureLimit = throttleConfig.GetInt("TextureLimit", 55750); - AssetLimit = throttleConfig.GetInt("AssetLimit", 27500); + Total = throttleConfig.GetInt("client_throttle_max_bps", 0); + + ResendLimit = throttleConfig.GetInt("resend_limit", 18750); + LandLimit = throttleConfig.GetInt("land_limit", 29750); + WindLimit = throttleConfig.GetInt("wind_limit", 18750); + CloudLimit = throttleConfig.GetInt("cloud_limit", 18750); + TaskLimit = throttleConfig.GetInt("task_limit", 55750); + TextureLimit = throttleConfig.GetInt("texture_limit", 55750); + AssetLimit = throttleConfig.GetInt("asset_limit", 27500); + + TotalLimit = throttleConfig.GetInt("client_throttle_max_bps", 0); } catch (Exception) { } } -- cgit v1.1 From c033477d2faf1449685d471db243651f132a7632 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Oct 2009 11:52:48 -0700 Subject: * Read scene_throttle_bps from the config file and use it * Minor formatting cleanup --- OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs | 11 ++--------- OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 4 +++- 2 files changed, 5 insertions(+), 10 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs index 25542ab..d25bf95 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs @@ -184,7 +184,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP { int sent; bool imageDone = image.SendPackets(m_client, packetsToSend - packetsSent, out sent); - packetsSent += sent; // If the send is complete, destroy any knowledge of this transfer @@ -234,20 +233,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP image.PriorityQueueHandle = null; lock (m_syncRoot) - try - { - m_priorityQueue.Add(ref image.PriorityQueueHandle, image); - } + try { m_priorityQueue.Add(ref image.PriorityQueueHandle, image); } catch (Exception) { } } void RemoveImageFromQueue(J2KImage image) { lock (m_syncRoot) - try - { - m_priorityQueue.Delete(image.PriorityQueueHandle); - } + try { m_priorityQueue.Delete(image.PriorityQueueHandle); } catch (Exception) { } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 1cfde91..384eda7 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -138,15 +138,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Environment.TickCount Measurement m_circuitManager = circuitManager; + int sceneThrottleBps = 0; IConfig config = configSource.Configs["ClientStack.LindenUDP"]; if (config != null) { m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0); + sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0); } // TODO: Config support for throttling the entire connection - m_throttle = new TokenBucket(null, 0, 0); + m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); m_throttleRates = new ThrottleRates(configSource); } -- cgit v1.1 From 1e9e9df0b3c2c6fad5e94db96c799bb31c193af1 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Oct 2009 14:25:58 -0700 Subject: * Switched to a plain lock for the ClientManager collections and protected the TryGetValues with try/catch instead of a lock * Added ClientManager.ForEachSync() for operations that need to run synchronously, such as "show connections" --- OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 384eda7..09845d6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -152,7 +152,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_throttleRates = new ThrottleRates(configSource); } - public new void Start() + public void Start() { if (m_scene == null) throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); @@ -817,6 +817,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void LogoutHandler(IClientAPI client) { client.SendLogoutPacket(); + if (client.IsActive) + RemoveClient(((LLClientView)client).UDPClient); } } } -- cgit v1.1 From 82012ec4e3c441021795c66112a66e002d459e73 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Oct 2009 16:21:48 -0700 Subject: * Clean up the SetThrottle() code and add a maxBurstRate parameter to allow more tweaking in the future --- .../Region/ClientStack/LindenUDP/LLUDPClient.cs | 188 ++++++++++----------- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 1 - .../Region/ClientStack/LindenUDP/ThrottleRates.cs | 63 ++++++- 3 files changed, 155 insertions(+), 97 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs index 39472cb..4eee6b6 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs @@ -31,6 +31,7 @@ using System.Net; using log4net; using OpenSim.Framework; using OpenMetaverse; +using OpenMetaverse.Packets; namespace OpenSim.Region.ClientStack.LindenUDP { @@ -60,13 +61,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public sealed class LLUDPClient { - private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - - // FIXME: Make this a config setting + // TODO: Make this a config setting /// Percentage of the task throttle category that is allocated to avatar and prim /// state updates const float STATE_TASK_PERCENTAGE = 0.8f; + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + /// The number of packet categories to throttle on. If a throttle category is added /// or removed, this number must also change const int THROTTLE_CATEGORY_COUNT = 8; @@ -129,21 +130,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_packetsSentReported; /// Throttle bucket for this agent's connection - private readonly TokenBucket throttle; + private readonly TokenBucket m_throttle; /// Throttle buckets for each packet category - private readonly TokenBucket[] throttleCategories; + private readonly TokenBucket[] m_throttleCategories; /// Throttle rate defaults and limits - private readonly ThrottleRates defaultThrottleRates; + private readonly ThrottleRates m_defaultThrottleRates; /// Outgoing queues for throttled packets - private readonly OpenSim.Framework.LocklessQueue[] packetOutboxes = new OpenSim.Framework.LocklessQueue[THROTTLE_CATEGORY_COUNT]; + private readonly OpenSim.Framework.LocklessQueue[] m_packetOutboxes = new OpenSim.Framework.LocklessQueue[THROTTLE_CATEGORY_COUNT]; /// A container that can hold one packet for each outbox, used to store /// dequeued packets that are being held for throttling - private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; + private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; /// Flags to prevent queue empty callbacks from stacking up on /// top of each other - private readonly bool[] onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT]; + private readonly bool[] m_onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT]; /// A reference to the LLUDPServer that is managing this client - private readonly LLUDPServer udpServer; + private readonly LLUDPServer m_udpServer; /// /// Default constructor @@ -157,27 +158,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Remote endpoint for this connection public LLUDPClient(LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint) { - udpServer = server; AgentID = agentID; RemoteEndPoint = remoteEndPoint; CircuitCode = circuitCode; - defaultThrottleRates = rates; + m_udpServer = server; + m_defaultThrottleRates = rates; + m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); + m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) - packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); - - throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total); - throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; - throttleCategories[(int)ThrottleOutPacketType.Resend] = new TokenBucket(throttle, rates.ResendLimit, rates.Resend); - throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land); - throttleCategories[(int)ThrottleOutPacketType.Wind] = new TokenBucket(throttle, rates.WindLimit, rates.Wind); - throttleCategories[(int)ThrottleOutPacketType.Cloud] = new TokenBucket(throttle, rates.CloudLimit, rates.Cloud); - throttleCategories[(int)ThrottleOutPacketType.Texture] = new TokenBucket(throttle, rates.TextureLimit, rates.Texture); - throttleCategories[(int)ThrottleOutPacketType.Asset] = new TokenBucket(throttle, rates.AssetLimit, rates.Asset); - // State and Transaction are actually sub-categories of the LLUDP generic "Task" category - TokenBucket stateBucket = new TokenBucket(throttle, (int)((float)rates.TaskLimit * STATE_TASK_PERCENTAGE), (int)((float)rates.Task * STATE_TASK_PERCENTAGE)); - throttleCategories[(int)ThrottleOutPacketType.State] = stateBucket; - throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit - stateBucket.MaxBurst, rates.Task - stateBucket.DripRate); + { + ThrottleOutPacketType type = (ThrottleOutPacketType)i; + + m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue(); + m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type)); + } // Set the granularity variable used for retransmission calculations to // the measured resolution of Environment.TickCount @@ -199,8 +194,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP NeedAcks.Clear(); for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) { - packetOutboxes[i].Clear(); - nextPackets[i] = null; + m_packetOutboxes[i].Clear(); + m_nextPackets[i] = null; } OnPacketStats = null; OnQueueEmpty = null; @@ -219,13 +214,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP info.pendingAcks = new Dictionary(); info.needAck = new Dictionary(); - info.resendThrottle = throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; - info.landThrottle = throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; - info.windThrottle = throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; - info.cloudThrottle = throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; - info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.State].DripRate + throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; - info.assetThrottle = throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; - info.textureThrottle = throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; + info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; + info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; + info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; + info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; + info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; + info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; + info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + info.taskThrottle + info.assetThrottle + info.textureThrottle; @@ -286,6 +281,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP adjData = throttleData; } + // 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; @@ -293,22 +289,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP int task = (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); + // State is a subcategory of task that we allocate a percentage to + int state = (int)((float)task * STATE_TASK_PERCENTAGE); + task -= state; - resend = (resend <= defaultThrottleRates.ResendLimit) ? resend : defaultThrottleRates.ResendLimit; - land = (land <= defaultThrottleRates.LandLimit) ? land : defaultThrottleRates.LandLimit; - wind = (wind <= defaultThrottleRates.WindLimit) ? wind : defaultThrottleRates.WindLimit; - cloud = (cloud <= defaultThrottleRates.CloudLimit) ? cloud : defaultThrottleRates.CloudLimit; - task = (task <= defaultThrottleRates.TaskLimit) ? task : defaultThrottleRates.TaskLimit; - texture = (texture <= defaultThrottleRates.TextureLimit) ? texture : defaultThrottleRates.TextureLimit; - asset = (asset <= defaultThrottleRates.AssetLimit) ? asset : defaultThrottleRates.AssetLimit; - - SetThrottle(ThrottleOutPacketType.Resend, resend); - SetThrottle(ThrottleOutPacketType.Land, land); - SetThrottle(ThrottleOutPacketType.Wind, wind); - SetThrottle(ThrottleOutPacketType.Cloud, cloud); - SetThrottle(ThrottleOutPacketType.Task, task); - SetThrottle(ThrottleOutPacketType.Texture, texture); - SetThrottle(ThrottleOutPacketType.Asset, asset); + int ceiling = Int32.MaxValue; + if (m_defaultThrottleRates.Total != 0) + { + ceiling = m_defaultThrottleRates.Total; + if (ceiling < Packet.MTU) ceiling = Packet.MTU; + } + + resend = Utils.Clamp(resend, Packet.MTU, ceiling); + land = Utils.Clamp(land, Packet.MTU, ceiling); + wind = Utils.Clamp(wind, Packet.MTU, ceiling); + cloud = Utils.Clamp(cloud, Packet.MTU, ceiling); + task = Utils.Clamp(task, Packet.MTU, ceiling); + texture = Utils.Clamp(texture, Packet.MTU, ceiling); + asset = Utils.Clamp(asset, Packet.MTU, ceiling); + state = Utils.Clamp(state, Packet.MTU, ceiling); + + int total = resend + land + wind + cloud + task + texture + asset + state; + int taskTotal = task + state; + + m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}", + AgentID, resend, land, wind, cloud, task, texture, asset, state, total); + + SetThrottle(ThrottleOutPacketType.Resend, resend, resend); + SetThrottle(ThrottleOutPacketType.Land, land, land); + SetThrottle(ThrottleOutPacketType.Wind, wind, wind); + SetThrottle(ThrottleOutPacketType.Cloud, cloud, cloud); + SetThrottle(ThrottleOutPacketType.Task, task, taskTotal); + SetThrottle(ThrottleOutPacketType.Texture, texture, texture); + SetThrottle(ThrottleOutPacketType.Asset, asset, asset); + SetThrottle(ThrottleOutPacketType.State, state, taskTotal); } public byte[] GetThrottlesPacked() @@ -316,40 +330,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte[] data = new byte[7 * 4]; int i = 0; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)(throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + - throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; - Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) + + m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; + Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; return data; } - public void SetThrottle(ThrottleOutPacketType category, int rate) + public void SetThrottle(ThrottleOutPacketType category, int rate, int maxBurst) { - if (category == ThrottleOutPacketType.Task) - { - TokenBucket stateBucket = throttleCategories[(int)ThrottleOutPacketType.State]; - TokenBucket taskBucket = throttleCategories[(int)ThrottleOutPacketType.Task]; - - stateBucket.MaxBurst = (int)((float)rate * STATE_TASK_PERCENTAGE); - stateBucket.DripRate = (int)((float)rate * STATE_TASK_PERCENTAGE); - - taskBucket.MaxBurst = rate - stateBucket.MaxBurst; - taskBucket.DripRate = rate - stateBucket.DripRate; - } - else + int i = (int)category; + if (i >= 0 && i < m_throttleCategories.Length) { - int i = (int)category; - if (i >= 0 && i < throttleCategories.Length) - { - TokenBucket bucket = throttleCategories[(int)category]; - bucket.MaxBurst = rate; - bucket.DripRate = rate; - } + TokenBucket bucket = m_throttleCategories[(int)category]; + bucket.DripRate = rate; + bucket.MaxBurst = maxBurst; } } @@ -357,12 +357,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP { int category = (int)packet.Category; - if (category >= 0 && category < packetOutboxes.Length) + if (category >= 0 && category < m_packetOutboxes.Length) { - OpenSim.Framework.LocklessQueue queue = packetOutboxes[category]; - TokenBucket bucket = throttleCategories[category]; + OpenSim.Framework.LocklessQueue queue = m_packetOutboxes[category]; + TokenBucket bucket = m_throttleCategories[category]; - if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength)) + if (m_throttleCategories[category].RemoveTokens(packet.Buffer.DataLength)) { // Enough tokens were removed from the bucket, the packet will not be queued return false; @@ -397,19 +397,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) { - bucket = throttleCategories[i]; + bucket = m_throttleCategories[i]; - if (nextPackets[i] != null) + if (m_nextPackets[i] != null) { // This bucket was empty the last time we tried to send a packet, // leaving a dequeued packet still waiting to be sent out. Try to // send it again - OutgoingPacket nextPacket = nextPackets[i]; + OutgoingPacket nextPacket = m_nextPackets[i]; if (bucket.RemoveTokens(nextPacket.Buffer.DataLength)) { // Send the packet - udpServer.SendPacketFinal(nextPacket); - nextPackets[i] = null; + m_udpServer.SendPacketFinal(nextPacket); + m_nextPackets[i] = null; packetSent = true; } } @@ -417,7 +417,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // No dequeued packet waiting to be sent, try to pull one off // this queue - queue = packetOutboxes[i]; + queue = m_packetOutboxes[i]; if (queue.Dequeue(out packet)) { // A packet was pulled off the queue. See if we have @@ -425,13 +425,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (bucket.RemoveTokens(packet.Buffer.DataLength)) { // Send the packet - udpServer.SendPacketFinal(packet); + m_udpServer.SendPacketFinal(packet); packetSent = true; } else { // Save the dequeued packet for the next iteration - nextPackets[i] = packet; + m_nextPackets[i] = packet; } // If the queue is empty after this dequeue, fire the queue @@ -493,7 +493,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// for private void BeginFireQueueEmpty(int throttleIndex) { - if (!onQueueEmptyRunning[throttleIndex]) + if (!m_onQueueEmptyRunning[throttleIndex]) Util.FireAndForget(FireQueueEmpty, throttleIndex); } @@ -511,12 +511,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (callback != null) { - if (!onQueueEmptyRunning[i]) + if (!m_onQueueEmptyRunning[i]) { - onQueueEmptyRunning[i] = true; + m_onQueueEmptyRunning[i] = true; try { callback(type); } catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); } - onQueueEmptyRunning[i] = false; + m_onQueueEmptyRunning[i] = false; } } } diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 09845d6..890f701 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -147,7 +147,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0); } - // TODO: Config support for throttling the entire connection m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps); m_throttleRates = new ThrottleRates(configSource); } diff --git a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs index adad4c3..008d827 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs @@ -26,6 +26,7 @@ */ using System; +using OpenSim.Framework; using Nini.Config; namespace OpenSim.Region.ClientStack.LindenUDP @@ -45,12 +46,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP public int Wind; /// Drip rate for cloud packets public int Cloud; - /// Drip rate for task (state and transaction) packets + /// Drip rate for task packets public int Task; /// Drip rate for texture packets public int Texture; /// Drip rate for asset packets public int Asset; + /// Drip rate for state packets + public int State; /// Drip rate for the parent token bucket public int Total; @@ -68,6 +71,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public int TextureLimit; /// Maximum burst rate for asset packets public int AssetLimit; + /// Maximum burst rate for state packets + public int StateLimit; /// Burst rate for the parent token bucket public int TotalLimit; @@ -88,6 +93,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP Task = throttleConfig.GetInt("task_default", 500); Texture = throttleConfig.GetInt("texture_default", 500); Asset = throttleConfig.GetInt("asset_default", 500); + State = throttleConfig.GetInt("state_default", 500); Total = throttleConfig.GetInt("client_throttle_max_bps", 0); @@ -95,13 +101,66 @@ namespace OpenSim.Region.ClientStack.LindenUDP LandLimit = throttleConfig.GetInt("land_limit", 29750); WindLimit = throttleConfig.GetInt("wind_limit", 18750); CloudLimit = throttleConfig.GetInt("cloud_limit", 18750); - TaskLimit = throttleConfig.GetInt("task_limit", 55750); + TaskLimit = throttleConfig.GetInt("task_limit", 18750); TextureLimit = throttleConfig.GetInt("texture_limit", 55750); AssetLimit = throttleConfig.GetInt("asset_limit", 27500); + State = throttleConfig.GetInt("state_limit", 37000); TotalLimit = throttleConfig.GetInt("client_throttle_max_bps", 0); } catch (Exception) { } } + + public int GetRate(ThrottleOutPacketType type) + { + switch (type) + { + case ThrottleOutPacketType.Resend: + return Resend; + case ThrottleOutPacketType.Land: + return Land; + case ThrottleOutPacketType.Wind: + return Wind; + case ThrottleOutPacketType.Cloud: + return Cloud; + case ThrottleOutPacketType.Task: + return Task; + case ThrottleOutPacketType.Texture: + return Texture; + case ThrottleOutPacketType.Asset: + return Asset; + case ThrottleOutPacketType.State: + return State; + case ThrottleOutPacketType.Unknown: + default: + return 0; + } + } + + public int GetLimit(ThrottleOutPacketType type) + { + switch (type) + { + case ThrottleOutPacketType.Resend: + return ResendLimit; + case ThrottleOutPacketType.Land: + return LandLimit; + case ThrottleOutPacketType.Wind: + return WindLimit; + case ThrottleOutPacketType.Cloud: + return CloudLimit; + case ThrottleOutPacketType.Task: + return TaskLimit; + case ThrottleOutPacketType.Texture: + return TextureLimit; + case ThrottleOutPacketType.Asset: + return AssetLimit; + case ThrottleOutPacketType.State: + return StateLimit; + case ThrottleOutPacketType.Unknown: + default: + return 0; + } + } } } -- cgit v1.1 From 06990b074c17c2201ed379bf1ae4c7191ab3187f Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Wed, 14 Oct 2009 16:48:27 -0700 Subject: Allow the LLUDP server to run in either synchronous or asynchronous mode with a config setting, defaulting to synchronous mode --- .../Region/ClientStack/LindenUDP/LLUDPServer.cs | 7 +++++- .../Region/ClientStack/LindenUDP/OpenSimUDPBase.cs | 28 ++++++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) (limited to 'OpenSim/Region/ClientStack') diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs index 890f701..545a0bc 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs @@ -113,6 +113,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// is passed up to the operating system and used in the system networking /// stack. Use zero to leave this value as the default private int m_recvBufferSize; + /// Flag to process packets asynchronously or synchronously + private bool m_asyncPacketHandling; /// The measured resolution of Environment.TickCount public float TickCountResolution { get { return m_tickCountResolution; } } @@ -143,6 +145,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP IConfig config = configSource.Configs["ClientStack.LindenUDP"]; if (config != null) { + m_asyncPacketHandling = config.GetBoolean("async_packet_handling", false); m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0); sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0); } @@ -156,7 +159,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (m_scene == null) throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); - base.Start(m_recvBufferSize); + m_log.Info("[LLUDPSERVER]: Starting the LLUDP server in " + (m_asyncPacketHandling ? "asynchronous" : "synchronous") + " mode"); + + base.Start(m_recvBufferSize, m_asyncPacketHandling); // Start the incoming packet processing thread Thread incomingThread = new Thread(IncomingPacketHandler); diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs index 44a6ed6..d16837d 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs @@ -62,6 +62,9 @@ namespace OpenMetaverse /// UDP socket, used in either client or server mode private Socket m_udpSocket; + /// Flag to process packets asynchronously or synchronously + private bool m_asyncPacketHandling; + /// The all important shutdown flag private volatile bool m_shutdownFlag = true; @@ -73,7 +76,6 @@ namespace OpenMetaverse /// /// Local IP address to bind the server to /// Port to listening for incoming UDP packets on - /// public OpenSimUDPBase(IPAddress bindAddress, int port) { m_localBindAddress = bindAddress; @@ -87,13 +89,19 @@ namespace OpenMetaverse /// the UDP socket. This value is passed up to the operating system /// and used in the system networking stack. Use zero to leave this /// value as the default + /// Set this to true to start + /// receiving more packets while current packet handler callbacks are + /// still running. Setting this to false will complete each packet + /// callback before the next packet is processed /// This method will attempt to set the SIO_UDP_CONNRESET flag /// on the socket to get newer versions of Windows to behave in a sane /// manner (not throwing an exception when the remote side resets the /// connection). This call is ignored on Mono where the flag is not /// necessary - public void Start(int recvBufferSize) + public void Start(int recvBufferSize, bool asyncPacketHandling) { + m_asyncPacketHandling = asyncPacketHandling; + if (m_shutdownFlag) { const int SIO_UDP_CONNRESET = -1744830452; @@ -209,8 +217,10 @@ namespace OpenMetaverse // to AsyncBeginReceive if (!m_shutdownFlag) { - // start another receive - this keeps the server going! - AsyncBeginReceive(); + // Asynchronous mode will start another receive before the + // callback for this packet is even fired. Very parallel :-) + if (m_asyncPacketHandling) + AsyncBeginReceive(); // get the buffer that was created in AsyncBeginReceive // this is the received data @@ -230,7 +240,15 @@ namespace OpenMetaverse } catch (SocketException) { } catch (ObjectDisposedException) { } - //finally { wrappedBuffer.Dispose(); } + finally + { + //wrappedBuffer.Dispose(); + + // Synchronous mode waits until the packet callback completes + // before starting the receive to fetch another packet + if (!m_asyncPacketHandling) + AsyncBeginReceive(); + } } } -- cgit v1.1