diff --git a/OpenSim/Framework/Pool.cs b/OpenSim/Framework/Pool.cs
new file mode 100644
index 0000000000..1ca06c3065
--- /dev/null
+++ b/OpenSim/Framework/Pool.cs
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+namespace OpenSim.Framework
+{
+ ///
+ /// Naive pool implementation.
+ ///
+ ///
+ /// Currently assumes that objects are in a useable state when returned.
+ ///
+ public class Pool
+ {
+ private Stack m_pool;
+
+ private int m_maxPoolSize;
+
+ private Func m_createFunction;
+
+ public Pool(Func createFunction, int maxSize)
+ {
+ m_maxPoolSize = maxSize;
+ m_createFunction = createFunction;
+ m_pool = new Stack(m_maxPoolSize);
+ }
+
+ public T GetObject()
+ {
+ lock (m_pool)
+ {
+ if (m_pool.Count > 0)
+ return m_pool.Pop();
+ else
+ return m_createFunction();
+ }
+ }
+
+ public void ReturnObject(T obj)
+ {
+ lock (m_pool)
+ {
+ if (m_pool.Count >= m_maxPoolSize)
+ return;
+ else
+ m_pool.Push(obj);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
index fc6dd4dabe..42247ca0f3 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
@@ -188,7 +188,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
///
private IClientAPI m_currentIncomingClient;
- public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
+ public LLUDPServer(
+ IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port,
+ IConfigSource configSource, AgentCircuitManager circuitManager)
: base(listenIP, (int)port)
{
#region Environment.TickCount Measurement
@@ -242,6 +244,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true);
PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true);
+ UsePools = packetConfig.GetBoolean("RecycleBaseUDPPackets", false);
}
#region BinaryStats
@@ -284,8 +287,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void StartInbound()
{
m_log.InfoFormat(
- "[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode",
- m_asyncPacketHandling ? "asynchronous" : "synchronous");
+ "[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}",
+ m_asyncPacketHandling ? "asynchronous" : "synchronous", UsePools);
base.StartInbound(m_recvBufferSize, m_asyncPacketHandling);
@@ -300,7 +303,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
}
- private void StartOutbound()
+ private new void StartOutbound()
{
m_log.Info("[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server");
@@ -317,7 +320,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS);
}
- public new void Stop()
+ public void Stop()
{
m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
base.StopOutbound();
@@ -806,7 +809,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
LLUDPClient udpClient = null;
Packet packet = null;
int packetEnd = buffer.DataLength - 1;
- IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint;
+ IPEndPoint endPoint = (IPEndPoint)buffer.RemoteEndPoint;
#region Decoding
@@ -816,7 +819,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}",
// buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
- return; // Drop undersizd packet
+ return; // Drop undersized packet
}
int headerLen = 7;
@@ -842,6 +845,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// packet = Packet.BuildPacket(buffer.Data, ref packetEnd,
// // Only allocate a buffer for zerodecoding if the packet is zerocoded
// ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
+ // If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we
+ // assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all
+ // bytes are copied out).
packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd,
// Only allocate a buffer for zerodecoding if the packet is zerocoded
((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
@@ -884,7 +890,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// UseCircuitCode handling
if (packet.Type == PacketType.UseCircuitCode)
{
- object[] array = new object[] { buffer, packet };
+ // We need to copy the endpoint so that it doesn't get changed when another thread reuses the
+ // buffer.
+ object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet };
Util.FireAndForget(HandleUseCircuitCode, array);
@@ -893,7 +901,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Determine which agent this packet came from
IClientAPI client;
- if (!m_scene.TryGetClient(address, out client) || !(client is LLClientView))
+ if (!m_scene.TryGetClient(endPoint, out client) || !(client is LLClientView))
{
//m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName);
return;
@@ -1091,21 +1099,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void HandleUseCircuitCode(object o)
{
- IPEndPoint remoteEndPoint = null;
+ IPEndPoint endPoint = null;
IClientAPI client = null;
try
{
// DateTime startTime = DateTime.Now;
object[] array = (object[])o;
- UDPPacketBuffer buffer = (UDPPacketBuffer)array[0];
+ endPoint = (IPEndPoint)array[0];
UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1];
m_log.DebugFormat(
"[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}",
- uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, buffer.RemoteEndPoint);
-
- remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint;
+ uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, endPoint);
AuthenticateResponse sessionInfo;
if (IsClientAuthorized(uccp, out sessionInfo))
@@ -1116,13 +1122,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
uccp.CircuitCode.Code,
uccp.CircuitCode.ID,
uccp.CircuitCode.SessionID,
- remoteEndPoint,
+ endPoint,
sessionInfo);
// Send ack straight away to let the viewer know that the connection is active.
// The client will be null if it already exists (e.g. if on a region crossing the client sends a use
// circuit code to the existing child agent. This is not particularly obvious.
- SendAckImmediate(remoteEndPoint, uccp.Header.Sequence);
+ SendAckImmediate(endPoint, uccp.Header.Sequence);
// We only want to send initial data to new clients, not ones which are being converted from child to root.
if (client != null)
@@ -1133,7 +1139,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Don't create clients for unauthorized requesters.
m_log.WarnFormat(
"[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}",
- uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, remoteEndPoint);
+ uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, endPoint);
}
// m_log.DebugFormat(
@@ -1145,7 +1151,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
m_log.ErrorFormat(
"[LLUDPSERVER]: UseCircuitCode handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}",
- remoteEndPoint != null ? remoteEndPoint.ToString() : "n/a",
+ endPoint != null ? endPoint.ToString() : "n/a",
client != null ? client.Name : "unknown",
client != null ? client.AgentId.ToString() : "unknown",
e.Message,
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
index 828c23cb28..6e6b3ef1b5 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs
@@ -30,6 +30,7 @@ using System.Net;
using System.Net.Sockets;
using System.Threading;
using log4net;
+using OpenSim.Framework;
namespace OpenMetaverse
{
@@ -58,6 +59,16 @@ namespace OpenMetaverse
/// Flag to process packets asynchronously or synchronously
private bool m_asyncPacketHandling;
+ ///
+ /// Pool to use for handling data. May be null if UsePools = false;
+ ///
+ protected OpenSim.Framework.Pool m_pool;
+
+ ///
+ /// Are we to use object pool(s) to reduce memory churn when receiving data?
+ ///
+ public bool UsePools { get; protected set; }
+
/// Returns true if the server is currently listening for inbound packets, otherwise false
public bool IsRunningInbound { get; private set; }
@@ -70,6 +81,7 @@ namespace OpenMetaverse
///
/// Local IP address to bind the server to
/// Port to listening for incoming UDP packets on
+ /// /// Are we to use an object pool to get objects for handing inbound data?
public OpenSimUDPBase(IPAddress bindAddress, int port)
{
m_localBindAddress = bindAddress;
@@ -94,6 +106,11 @@ namespace OpenMetaverse
/// necessary
public void StartInbound(int recvBufferSize, bool asyncPacketHandling)
{
+ if (UsePools)
+ m_pool = new Pool(() => new UDPPacketBuffer(), 500);
+ else
+ m_pool = null;
+
m_asyncPacketHandling = asyncPacketHandling;
if (!IsRunningInbound)
@@ -165,9 +182,12 @@ namespace OpenMetaverse
private void AsyncBeginReceive()
{
- // allocate a packet buffer
- //WrappedObject wrappedBuffer = Pool.CheckOut();
- UDPPacketBuffer buf = new UDPPacketBuffer();
+ UDPPacketBuffer buf;
+
+ if (UsePools)
+ buf = m_pool.GetObject();
+ else
+ buf = new UDPPacketBuffer();
if (IsRunningInbound)
{
@@ -231,8 +251,6 @@ namespace OpenMetaverse
// get the buffer that was created in AsyncBeginReceive
// this is the received data
- //WrappedObject wrappedBuffer = (WrappedObject)iar.AsyncState;
- //UDPPacketBuffer buffer = wrappedBuffer.Instance;
UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
try
@@ -249,7 +267,8 @@ namespace OpenMetaverse
catch (ObjectDisposedException) { }
finally
{
- //wrappedBuffer.Dispose();
+ if (UsePools)
+ m_pool.ReturnObject(buffer);
// Synchronous mode waits until the packet callback completes
// before starting the receive to fetch another packet
diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini
index 2fca6cedc9..b99a02eff9 100644
--- a/bin/OpenSimDefaults.ini
+++ b/bin/OpenSimDefaults.ini
@@ -1602,10 +1602,14 @@
[PacketPool]
- ; Enables the experimental packet pool. Yes, we've been here before.
;RecyclePackets = true;
;RecycleDataBlocks = true;
+ ; If true, then the basic packet objects used to receive data are also recycled, not just the LLUDP packets.
+ ; This reduces data churn
+ ; This setting is currently experimental and defaults to false.
+ RecycleBaseUDPPackets = false;
+
[InterestManagement]
; This section controls how state updates are prioritized for each client