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 ++++++++++++++++++----
 bin/OpenSim.ini.example                            |  8 ++++++-
 3 files changed, 36 insertions(+), 7 deletions(-)

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</summary>
         private int m_recvBufferSize;
+        /// <summary>Flag to process packets asynchronously or synchronously</summary>
+        private bool m_asyncPacketHandling;
 
         /// <summary>The measured resolution of Environment.TickCount</summary>
         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
         /// <summary>UDP socket, used in either client or server mode</summary>
         private Socket m_udpSocket;
 
+        /// <summary>Flag to process packets asynchronously or synchronously</summary>
+        private bool m_asyncPacketHandling;
+
         /// <summary>The all important shutdown flag</summary>
         private volatile bool m_shutdownFlag = true;
 
@@ -73,7 +76,6 @@ namespace OpenMetaverse
         /// </summary>
         /// <param name="bindAddress">Local IP address to bind the server to</param>
         /// <param name="port">Port to listening for incoming UDP packets on</param>
-        /// 
         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</param>
+        /// <param name="asyncPacketHandling">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</param>
         /// <remarks>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</remarks>
-        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();
+                }
 
             }
         }
diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example
index 983a7e5..002745d 100644
--- a/bin/OpenSim.ini.example
+++ b/bin/OpenSim.ini.example
@@ -341,7 +341,13 @@
 
 
 [ClientStack.LindenUDP]
-    ; the client socket receive buffer size determines how many
+    ; Set this to true to process incoming packets asynchronously. Networking is
+    ; already separated from packet handling with a queue, so this will only
+    ; affect whether networking internals such as packet decoding and 
+    ; acknowledgement accounting are done synchronously or asynchronously
+    async_packet_handling = false
+
+    ; The client socket receive buffer size determines how many
     ; incoming requests we can process; the default on .NET is 8192
     ; which is about 2 4k-sized UDP datagrams. On mono this is
     ; whatever the underlying operating system has as default; for
-- 
cgit v1.1