aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/Client/MXP/PacketHandler/MXPPacketServer.cs1
-rw-r--r--OpenSim/Data/MySQL/MySQLAssetData.cs6
-rw-r--r--OpenSim/Framework/ConfigSettings.cs4
-rw-r--r--OpenSim/Framework/InventoryConfig.cs6
-rw-r--r--OpenSim/Framework/Parallel.cs207
-rw-r--r--OpenSim/Framework/Servers/BaseOpenSimServer.cs27
-rw-r--r--OpenSim/Framework/Tests/ThreadTrackerTests.cs140
-rw-r--r--OpenSim/Framework/ThreadTracker.cs127
-rw-r--r--OpenSim/Framework/ThrottleOutPacketType.cs8
-rw-r--r--OpenSim/Framework/Util.cs40
-rw-r--r--OpenSim/Region/Application/Application.cs12
-rw-r--r--OpenSim/Region/Application/OpenSimBase.cs2
-rw-r--r--OpenSim/Region/ClientStack/IClientNetworkServer.cs2
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/ILLClientStackNetworkHandler.cs38
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs83
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/IncomingPacket.cs (renamed from OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs)34
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/IncomingPacketHistoryCollection.cs (renamed from OpenSim/Region/ClientStack/LindenUDP/KillPacket.cs)60
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs31
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs2
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs870
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs742
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketServer.cs206
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketThrottle.cs128
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs442
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs185
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs1055
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs (renamed from OpenSim/Region/ClientStack/LindenUDP/LLUtil.cs)47
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/Tests/PacketHandlerTests.cs2
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/Tests/TestLLPacketServer.cs2
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs99
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs160
-rw-r--r--OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs1
-rw-r--r--OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs1
-rw-r--r--OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.cs5
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs1
-rw-r--r--OpenSim/Region/OptionalModules/ContentManagementSystem/CMController.cs1
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs1
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/MaintenanceThread.cs1
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs8
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs1
-rw-r--r--OpenSim/TestSuite/BotManager.cs1
-rw-r--r--OpenSim/Tests/Clients/Grid/GridClient.cs2
-rw-r--r--OpenSim/Tools/pCampBot/BotManager.cs1
-rw-r--r--prebuild.xml4
45 files changed, 1888 insertions, 2910 deletions
diff --git a/OpenSim/Client/MXP/PacketHandler/MXPPacketServer.cs b/OpenSim/Client/MXP/PacketHandler/MXPPacketServer.cs
index 332ff70..4910ab1 100644
--- a/OpenSim/Client/MXP/PacketHandler/MXPPacketServer.cs
+++ b/OpenSim/Client/MXP/PacketHandler/MXPPacketServer.cs
@@ -89,7 +89,6 @@ namespace OpenSim.Client.MXP.PacketHandler
89 m_clientThread.Name = "MXPThread"; 89 m_clientThread.Name = "MXPThread";
90 m_clientThread.IsBackground = true; 90 m_clientThread.IsBackground = true;
91 m_clientThread.Start(); 91 m_clientThread.Start();
92 ThreadTracker.Add(m_clientThread);
93 } 92 }
94 93
95 public void StartListener() 94 public void StartListener()
diff --git a/OpenSim/Data/MySQL/MySQLAssetData.cs b/OpenSim/Data/MySQL/MySQLAssetData.cs
index 8f97440..259e186 100644
--- a/OpenSim/Data/MySQL/MySQLAssetData.cs
+++ b/OpenSim/Data/MySQL/MySQLAssetData.cs
@@ -239,10 +239,8 @@ namespace OpenSim.Data.MySQL
239 } 239 }
240 catch (Exception e) 240 catch (Exception e)
241 { 241 {
242 m_log.ErrorFormat( 242 m_log.ErrorFormat("[ASSET DB]: MySQL failure creating asset {0} with name \"{1}\". Attempting reconnect. Error: {2}",
243 "[ASSETS DB]: " + 243 asset.FullID, asset.Name, e.Message);
244 "MySql failure creating asset {0} with name {1}" + Environment.NewLine + e.ToString()
245 + Environment.NewLine + "Attempting reconnection", asset.FullID, asset.Name);
246 _dbConnection.Reconnect(); 244 _dbConnection.Reconnect();
247 } 245 }
248 } 246 }
diff --git a/OpenSim/Framework/ConfigSettings.cs b/OpenSim/Framework/ConfigSettings.cs
index 32415e0..93efffa 100644
--- a/OpenSim/Framework/ConfigSettings.cs
+++ b/OpenSim/Framework/ConfigSettings.cs
@@ -168,7 +168,7 @@ namespace OpenSim.Framework
168 public const bool DefaultUserServerHttpSSL = false; 168 public const bool DefaultUserServerHttpSSL = false;
169 public const uint DefaultMessageServerHttpPort = 8006; 169 public const uint DefaultMessageServerHttpPort = 8006;
170 public const bool DefaultMessageServerHttpSSL = false; 170 public const bool DefaultMessageServerHttpSSL = false;
171 public const uint DefaultGridServerHttpPort = 8003; 171 public const uint DefaultGridServerHttpPort = 8001;
172 public const uint DefaultInventoryServerHttpPort = 8003; 172 public const uint DefaultInventoryServerHttpPort = 8004;
173 } 173 }
174} 174}
diff --git a/OpenSim/Framework/InventoryConfig.cs b/OpenSim/Framework/InventoryConfig.cs
index dd207ad..f539d55 100644
--- a/OpenSim/Framework/InventoryConfig.cs
+++ b/OpenSim/Framework/InventoryConfig.cs
@@ -56,15 +56,15 @@ namespace OpenSim.Framework
56 m_configMember.addConfigurationOption("default_inventory_server", 56 m_configMember.addConfigurationOption("default_inventory_server",
57 ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, 57 ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY,
58 "Default Inventory Server URI (this server's external name)", 58 "Default Inventory Server URI (this server's external name)",
59 "http://127.0.0.1:" + ConfigSettings.DefaultInventoryServerHttpPort, false); 59 "http://127.0.0.1:8004", false);
60 m_configMember.addConfigurationOption("default_user_server", 60 m_configMember.addConfigurationOption("default_user_server",
61 ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, 61 ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY,
62 "Default User Server URI", 62 "Default User Server URI",
63 "http://127.0.0.1:" + ConfigSettings.DefaultUserServerHttpPort, false); 63 "http://127.0.0.1:8002", false);
64 m_configMember.addConfigurationOption("default_asset_server", 64 m_configMember.addConfigurationOption("default_asset_server",
65 ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, 65 ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY,
66 "Default Asset Server URI", 66 "Default Asset Server URI",
67 "http://127.0.0.1:" + ConfigSettings.DefaultAssetServerHttpPort, false); 67 "http://127.0.0.1:8003", false);
68 m_configMember.addConfigurationOption("database_provider", ConfigurationOption.ConfigurationTypes.TYPE_STRING, 68 m_configMember.addConfigurationOption("database_provider", ConfigurationOption.ConfigurationTypes.TYPE_STRING,
69 "DLL for database provider", "OpenSim.Data.MySQL.dll", false); 69 "DLL for database provider", "OpenSim.Data.MySQL.dll", false);
70 m_configMember.addConfigurationOption("database_connect", ConfigurationOption.ConfigurationTypes.TYPE_STRING, 70 m_configMember.addConfigurationOption("database_connect", ConfigurationOption.ConfigurationTypes.TYPE_STRING,
diff --git a/OpenSim/Framework/Parallel.cs b/OpenSim/Framework/Parallel.cs
new file mode 100644
index 0000000..74537ba
--- /dev/null
+++ b/OpenSim/Framework/Parallel.cs
@@ -0,0 +1,207 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Threading;
31
32namespace OpenSim.Framework
33{
34 /// <summary>
35 /// Provides helper methods for parallelizing loops
36 /// </summary>
37 public static class Parallel
38 {
39 private static readonly int processorCount = System.Environment.ProcessorCount;
40
41 /// <summary>
42 /// Executes a for loop in which iterations may run in parallel
43 /// </summary>
44 /// <param name="fromInclusive">The loop will be started at this index</param>
45 /// <param name="toExclusive">The loop will be terminated before this index is reached</param>
46 /// <param name="body">Method body to run for each iteration of the loop</param>
47 public static void For(int fromInclusive, int toExclusive, Action<int> body)
48 {
49 For(processorCount, fromInclusive, toExclusive, body);
50 }
51
52 /// <summary>
53 /// Executes a for loop in which iterations may run in parallel
54 /// </summary>
55 /// <param name="threadCount">The number of concurrent execution threads to run</param>
56 /// <param name="fromInclusive">The loop will be started at this index</param>
57 /// <param name="toExclusive">The loop will be terminated before this index is reached</param>
58 /// <param name="body">Method body to run for each iteration of the loop</param>
59 public static void For(int threadCount, int fromInclusive, int toExclusive, Action<int> body)
60 {
61 int counter = threadCount;
62 AutoResetEvent threadFinishEvent = new AutoResetEvent(false);
63 Exception exception = null;
64
65 --fromInclusive;
66
67 for (int i = 0; i < threadCount; i++)
68 {
69 ThreadPool.QueueUserWorkItem(
70 delegate(object o)
71 {
72 int threadIndex = (int)o;
73
74 while (exception == null)
75 {
76 int currentIndex = Interlocked.Increment(ref fromInclusive);
77
78 if (currentIndex >= toExclusive)
79 break;
80
81 try { body(currentIndex); }
82 catch (Exception ex) { exception = ex; break; }
83 }
84
85 if (Interlocked.Decrement(ref counter) == 0)
86 threadFinishEvent.Set();
87 }, i
88 );
89 }
90
91 threadFinishEvent.WaitOne();
92
93 if (exception != null)
94 throw new Exception(exception.Message, exception);
95 }
96
97 /// <summary>
98 /// Executes a foreach loop in which iterations may run in parallel
99 /// </summary>
100 /// <typeparam name="T">Object type that the collection wraps</typeparam>
101 /// <param name="enumerable">An enumerable collection to iterate over</param>
102 /// <param name="body">Method body to run for each object in the collection</param>
103 public static void ForEach<T>(IEnumerable<T> enumerable, Action<T> body)
104 {
105 ForEach<T>(processorCount, enumerable, body);
106 }
107
108 /// <summary>
109 /// Executes a foreach loop in which iterations may run in parallel
110 /// </summary>
111 /// <typeparam name="T">Object type that the collection wraps</typeparam>
112 /// <param name="threadCount">The number of concurrent execution threads to run</param>
113 /// <param name="enumerable">An enumerable collection to iterate over</param>
114 /// <param name="body">Method body to run for each object in the collection</param>
115 public static void ForEach<T>(int threadCount, IEnumerable<T> enumerable, Action<T> body)
116 {
117 int counter = threadCount;
118 AutoResetEvent threadFinishEvent = new AutoResetEvent(false);
119 IEnumerator<T> enumerator = enumerable.GetEnumerator();
120 Exception exception = null;
121
122 for (int i = 0; i < threadCount; i++)
123 {
124 ThreadPool.QueueUserWorkItem(
125 delegate(object o)
126 {
127 int threadIndex = (int)o;
128
129 while (exception == null)
130 {
131 T entry;
132
133 lock (enumerator)
134 {
135 if (!enumerator.MoveNext())
136 break;
137 entry = (T)enumerator.Current; // Explicit typecast for Mono's sake
138 }
139
140 try { body(entry); }
141 catch (Exception ex) { exception = ex; break; }
142 }
143
144 if (Interlocked.Decrement(ref counter) == 0)
145 threadFinishEvent.Set();
146 }, i
147 );
148 }
149
150 threadFinishEvent.WaitOne();
151
152 if (exception != null)
153 throw new Exception(exception.Message, exception);
154 }
155
156 /// <summary>
157 /// Executes a series of tasks in parallel
158 /// </summary>
159 /// <param name="actions">A series of method bodies to execute</param>
160 public static void Invoke(params Action[] actions)
161 {
162 Invoke(processorCount, actions);
163 }
164
165 /// <summary>
166 /// Executes a series of tasks in parallel
167 /// </summary>
168 /// <param name="threadCount">The number of concurrent execution threads to run</param>
169 /// <param name="actions">A series of method bodies to execute</param>
170 public static void Invoke(int threadCount, params Action[] actions)
171 {
172 int counter = threadCount;
173 AutoResetEvent threadFinishEvent = new AutoResetEvent(false);
174 int index = -1;
175 Exception exception = null;
176
177 for (int i = 0; i < threadCount; i++)
178 {
179 ThreadPool.QueueUserWorkItem(
180 delegate(object o)
181 {
182 int threadIndex = (int)o;
183
184 while (exception == null)
185 {
186 int currentIndex = Interlocked.Increment(ref index);
187
188 if (currentIndex >= actions.Length)
189 break;
190
191 try { actions[currentIndex](); }
192 catch (Exception ex) { exception = ex; break; }
193 }
194
195 if (Interlocked.Decrement(ref counter) == 0)
196 threadFinishEvent.Set();
197 }, i
198 );
199 }
200
201 threadFinishEvent.WaitOne();
202
203 if (exception != null)
204 throw new Exception(exception.Message, exception);
205 }
206 }
207}
diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
index 56155dd..8e58980 100644
--- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs
+++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
@@ -27,6 +27,7 @@
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Diagnostics;
30using System.IO; 31using System.IO;
31using System.Reflection; 32using System.Reflection;
32using System.Text; 33using System.Text;
@@ -109,9 +110,8 @@ namespace OpenSim.Framework.Servers
109 m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics); 110 m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics);
110 m_periodicDiagnosticsTimer.Enabled = true; 111 m_periodicDiagnosticsTimer.Enabled = true;
111 112
112 // Add ourselves to thread monitoring. This thread will go on to become the console listening thread 113 // This thread will go on to become the console listening thread
113 Thread.CurrentThread.Name = "ConsoleThread"; 114 Thread.CurrentThread.Name = "ConsoleThread";
114 ThreadTracker.Add(Thread.CurrentThread);
115 115
116 ILoggerRepository repository = LogManager.GetRepository(); 116 ILoggerRepository repository = LogManager.GetRepository();
117 IAppender[] appenders = repository.GetAppenders(); 117 IAppender[] appenders = repository.GetAppenders();
@@ -235,7 +235,7 @@ namespace OpenSim.Framework.Servers
235 { 235 {
236 StringBuilder sb = new StringBuilder(); 236 StringBuilder sb = new StringBuilder();
237 237
238 List<Thread> threads = ThreadTracker.GetThreads(); 238 ProcessThreadCollection threads = ThreadTracker.GetThreads();
239 if (threads == null) 239 if (threads == null)
240 { 240 {
241 sb.Append("OpenSim thread tracking is only enabled in DEBUG mode."); 241 sb.Append("OpenSim thread tracking is only enabled in DEBUG mode.");
@@ -243,25 +243,10 @@ namespace OpenSim.Framework.Servers
243 else 243 else
244 { 244 {
245 sb.Append(threads.Count + " threads are being tracked:" + Environment.NewLine); 245 sb.Append(threads.Count + " threads are being tracked:" + Environment.NewLine);
246 foreach (Thread t in threads) 246 foreach (ProcessThread t in threads)
247 { 247 {
248 if (t.IsAlive) 248 sb.Append("ID: " + t.Id + ", TotalProcessorTime: " + t.TotalProcessorTime + ", TimeRunning: " +
249 { 249 (DateTime.Now - t.StartTime) + ", Pri: " + t.CurrentPriority + ", State: " + t.ThreadState + Environment.NewLine);
250 sb.Append(
251 "ID: " + t.ManagedThreadId + ", Name: " + t.Name + ", Alive: " + t.IsAlive
252 + ", Pri: " + t.Priority + ", State: " + t.ThreadState + Environment.NewLine);
253 }
254 else
255 {
256 try
257 {
258 sb.Append("ID: " + t.ManagedThreadId + ", Name: " + t.Name + ", DEAD" + Environment.NewLine);
259 }
260 catch
261 {
262 sb.Append("THREAD ERROR" + Environment.NewLine);
263 }
264 }
265 } 250 }
266 } 251 }
267 int workers = 0, ports = 0, maxWorkers = 0, maxPorts = 0; 252 int workers = 0, ports = 0, maxWorkers = 0, maxPorts = 0;
diff --git a/OpenSim/Framework/Tests/ThreadTrackerTests.cs b/OpenSim/Framework/Tests/ThreadTrackerTests.cs
index 15d5b73..7eb83e6 100644
--- a/OpenSim/Framework/Tests/ThreadTrackerTests.cs
+++ b/OpenSim/Framework/Tests/ThreadTrackerTests.cs
@@ -41,7 +41,7 @@ namespace OpenSim.Framework.Tests
41 [Test] 41 [Test]
42 public void DefaultThreadTrackerTest() 42 public void DefaultThreadTrackerTest()
43 { 43 {
44 List<Thread> lThread = ThreadTracker.GetThreads(); 44 System.Diagnostics.ProcessThreadCollection lThread = ThreadTracker.GetThreads();
45 45
46 /* 46 /*
47 foreach (Thread t in lThread) 47 foreach (Thread t in lThread)
@@ -50,143 +50,7 @@ namespace OpenSim.Framework.Tests
50 } 50 }
51 */ 51 */
52 52
53 Assert.That(lThread.Count == 1); 53 Assert.That(lThread.Count > 0);
54 Assert.That(lThread[0].Name == "ThreadTrackerThread");
55 } 54 }
56
57 /// <summary>
58 /// Validate that adding a thread to the thread tracker works
59 /// Validate that removing a thread from the thread tracker also works.
60 /// </summary>
61 [Test]
62 public void AddThreadToThreadTrackerTestAndRemoveTest()
63 {
64 Thread t = new Thread(run);
65 t.Name = "TestThread";
66 t.Priority = ThreadPriority.BelowNormal;
67 t.IsBackground = true;
68 t.SetApartmentState(ApartmentState.MTA);
69 t.Start();
70 ThreadTracker.Add(t);
71
72 List<Thread> lThread = ThreadTracker.GetThreads();
73
74 Assert.That(lThread.Count == 2);
75
76 foreach (Thread tr in lThread)
77 {
78 Assert.That((tr.Name == "ThreadTrackerThread" || tr.Name == "TestThread"));
79 }
80 running = false;
81 ThreadTracker.Remove(t);
82
83 lThread = ThreadTracker.GetThreads();
84
85 Assert.That(lThread.Count == 1);
86
87 foreach (Thread tr in lThread)
88 {
89 Assert.That((tr.Name == "ThreadTrackerThread"));
90 }
91
92
93 }
94
95 /// <summary>
96 /// Test a dead thread removal by aborting it and setting it's last seen active date to 50 seconds
97 /// </summary>
98 [Test]
99 public void DeadThreadTest()
100 {
101 Thread t = new Thread(run2);
102 t.Name = "TestThread";
103 t.Priority = ThreadPriority.BelowNormal;
104 t.IsBackground = true;
105 t.SetApartmentState(ApartmentState.MTA);
106 t.Start();
107 ThreadTracker.Add(t);
108 t.Abort();
109 Thread.Sleep(5000);
110 ThreadTracker.m_Threads[1].LastSeenActive = DateTime.Now.Ticks - (50*10000000);
111 ThreadTracker.CleanUp();
112 List<Thread> lThread = ThreadTracker.GetThreads();
113
114 Assert.That(lThread.Count == 1);
115
116 foreach (Thread tr in lThread)
117 {
118 Assert.That((tr.Name == "ThreadTrackerThread"));
119 }
120 }
121
122 [Test]
123 public void UnstartedThreadTest()
124 {
125 Thread t = new Thread(run2);
126 t.Name = "TestThread";
127 t.Priority = ThreadPriority.BelowNormal;
128 t.IsBackground = true;
129 t.SetApartmentState(ApartmentState.MTA);
130 ThreadTracker.Add(t);
131 ThreadTracker.m_Threads[1].LastSeenActive = DateTime.Now.Ticks - (50 * 10000000);
132 ThreadTracker.CleanUp();
133 List<Thread> lThread = ThreadTracker.GetThreads();
134
135 Assert.That(lThread.Count == 1);
136
137 foreach (Thread tr in lThread)
138 {
139 Assert.That((tr.Name == "ThreadTrackerThread"));
140 }
141 }
142
143 [Test]
144 public void NullThreadTest()
145 {
146 Thread t = null;
147 ThreadTracker.Add(t);
148
149 List<Thread> lThread = ThreadTracker.GetThreads();
150
151 Assert.That(lThread.Count == 1);
152
153 foreach (Thread tr in lThread)
154 {
155 Assert.That((tr.Name == "ThreadTrackerThread"));
156 }
157 }
158
159
160 /// <summary>
161 /// Worker thread 0
162 /// </summary>
163 /// <param name="o"></param>
164 public void run(object o)
165 {
166 while (running)
167 {
168 Thread.Sleep(5000);
169 }
170 }
171
172 /// <summary>
173 /// Worker thread 1
174 /// </summary>
175 /// <param name="o"></param>
176 public void run2(object o)
177 {
178 try
179 {
180 while (running2)
181 {
182 Thread.Sleep(5000);
183 }
184
185 }
186 catch (ThreadAbortException)
187 {
188 }
189 }
190
191 } 55 }
192} 56}
diff --git a/OpenSim/Framework/ThreadTracker.cs b/OpenSim/Framework/ThreadTracker.cs
index d3a239d..b68d9b3 100644
--- a/OpenSim/Framework/ThreadTracker.cs
+++ b/OpenSim/Framework/ThreadTracker.cs
@@ -26,138 +26,21 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections;
30using System.Collections.Generic; 29using System.Collections.Generic;
31using System.Reflection; 30using System.Reflection;
32using System.Threading; 31using System.Diagnostics;
33using log4net; 32using log4net;
34 33
35namespace OpenSim.Framework 34namespace OpenSim.Framework
36{ 35{
37 public static class ThreadTracker 36 public static class ThreadTracker
38 { 37 {
39 private static readonly ILog m_log 38 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
40 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
41
42 private static readonly long ThreadTimeout = 30 * 10000000;
43 public static List<ThreadTrackerItem> m_Threads;
44 public static Thread ThreadTrackerThread;
45 39
46 static ThreadTracker() 40 public static ProcessThreadCollection GetThreads()
47 { 41 {
48#if DEBUG 42 Process thisProc = Process.GetCurrentProcess();
49 m_Threads = new List<ThreadTrackerItem>(); 43 return thisProc.Threads;
50 ThreadTrackerThread = new Thread(ThreadTrackerThreadLoop);
51 ThreadTrackerThread.Name = "ThreadTrackerThread";
52 ThreadTrackerThread.IsBackground = true;
53 ThreadTrackerThread.Priority = ThreadPriority.BelowNormal;
54 ThreadTrackerThread.Start();
55 Add(ThreadTrackerThread);
56#endif
57 } 44 }
58
59 private static void ThreadTrackerThreadLoop()
60 {
61 try
62 {
63 while (true)
64 {
65 Thread.Sleep(5000);
66 CleanUp();
67 }
68 }
69 catch (Exception e)
70 {
71 m_log.ErrorFormat(
72 "[THREAD TRACKER]: Thread tracker cleanup thread terminating with exception. Please report this error. Exception is {0}",
73 e);
74 }
75 }
76
77 public static void Add(Thread thread)
78 {
79#if DEBUG
80 if (thread != null)
81 {
82 lock (m_Threads)
83 {
84 ThreadTrackerItem tti = new ThreadTrackerItem();
85 tti.Thread = thread;
86 tti.LastSeenActive = DateTime.Now.Ticks;
87 m_Threads.Add(tti);
88 }
89 }
90#endif
91 }
92
93 public static void Remove(Thread thread)
94 {
95#if DEBUG
96 lock (m_Threads)
97 {
98 foreach (ThreadTrackerItem tti in new ArrayList(m_Threads))
99 {
100 if (tti.Thread == thread)
101 m_Threads.Remove(tti);
102 }
103 }
104#endif
105 }
106
107 public static void CleanUp()
108 {
109 lock (m_Threads)
110 {
111 foreach (ThreadTrackerItem tti in new ArrayList(m_Threads))
112 {
113 try
114 {
115
116
117 if (tti.Thread.IsAlive)
118 {
119 // Its active
120 tti.LastSeenActive = DateTime.Now.Ticks;
121 }
122 else
123 {
124 // Its not active -- if its expired then remove it
125 if (tti.LastSeenActive + ThreadTimeout < DateTime.Now.Ticks)
126 m_Threads.Remove(tti);
127 }
128 }
129 catch (NullReferenceException)
130 {
131 m_Threads.Remove(tti);
132 }
133 }
134 }
135 }
136
137 public static List<Thread> GetThreads()
138 {
139 if (m_Threads == null)
140 return null;
141
142 List<Thread> threads = new List<Thread>();
143 lock (m_Threads)
144 {
145 foreach (ThreadTrackerItem tti in new ArrayList(m_Threads))
146 {
147 threads.Add(tti.Thread);
148 }
149 }
150 return threads;
151 }
152
153 #region Nested type: ThreadTrackerItem
154
155 public class ThreadTrackerItem
156 {
157 public long LastSeenActive;
158 public Thread Thread;
159 }
160
161 #endregion
162 } 45 }
163} 46}
diff --git a/OpenSim/Framework/ThrottleOutPacketType.cs b/OpenSim/Framework/ThrottleOutPacketType.cs
index 0843757..fd490a5 100644
--- a/OpenSim/Framework/ThrottleOutPacketType.cs
+++ b/OpenSim/Framework/ThrottleOutPacketType.cs
@@ -29,9 +29,9 @@ using System;
29 29
30namespace OpenSim.Framework 30namespace OpenSim.Framework
31{ 31{
32 [Flags]
33 public enum ThrottleOutPacketType : int 32 public enum ThrottleOutPacketType : int
34 { 33 {
34 Unknown = -1, // Also doubles as 'do not throttle'
35 Resend = 0, 35 Resend = 0,
36 Land = 1, 36 Land = 1,
37 Wind = 2, 37 Wind = 2,
@@ -39,11 +39,5 @@ namespace OpenSim.Framework
39 Task = 4, 39 Task = 4,
40 Texture = 5, 40 Texture = 5,
41 Asset = 6, 41 Asset = 6,
42 Unknown = 7, // Also doubles as 'do not throttle'
43 Back = 8,
44
45 TypeMask = 15, // The mask to mask off the flags
46
47 LowPriority = 128 // Additional flags
48 } 42 }
49} 43}
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index 0851d26..38729c6 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -1231,6 +1231,42 @@ namespace OpenSim.Framework
1231 return (ipaddr1 != null) ? "http://" + ipaddr1.ToString() + ":" + port1 : uri; 1231 return (ipaddr1 != null) ? "http://" + ipaddr1.ToString() + ":" + port1 : uri;
1232 } 1232 }
1233 1233
1234 public static byte[] StringToBytes256(string str)
1235 {
1236 if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; }
1237 if (str.Length > 254) str = str.Remove(254);
1238 if (!str.EndsWith("\0")) { str += "\0"; }
1239
1240 // Because this is UTF-8 encoding and not ASCII, it's possible we
1241 // might have gotten an oversized array even after the string trim
1242 byte[] data = UTF8.GetBytes(str);
1243 if (data.Length > 256)
1244 {
1245 Array.Resize<byte>(ref data, 256);
1246 data[255] = 0;
1247 }
1248
1249 return data;
1250 }
1251
1252 public static byte[] StringToBytes1024(string str)
1253 {
1254 if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; }
1255 if (str.Length > 1023) str = str.Remove(1023);
1256 if (!str.EndsWith("\0")) { str += "\0"; }
1257
1258 // Because this is UTF-8 encoding and not ASCII, it's possible we
1259 // might have gotten an oversized array even after the string trim
1260 byte[] data = UTF8.GetBytes(str);
1261 if (data.Length > 1024)
1262 {
1263 Array.Resize<byte>(ref data, 1024);
1264 data[1023] = 0;
1265 }
1266
1267 return data;
1268 }
1269
1234 #region FireAndForget Threading Pattern 1270 #region FireAndForget Threading Pattern
1235 1271
1236 public static void FireAndForget(System.Threading.WaitCallback callback) 1272 public static void FireAndForget(System.Threading.WaitCallback callback)
@@ -1247,7 +1283,9 @@ namespace OpenSim.Framework
1247 { 1283 {
1248 System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState; 1284 System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState;
1249 1285
1250 callback.EndInvoke(ar); 1286 try { callback.EndInvoke(ar); }
1287 catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); }
1288
1251 ar.AsyncWaitHandle.Close(); 1289 ar.AsyncWaitHandle.Close();
1252 } 1290 }
1253 1291
diff --git a/OpenSim/Region/Application/Application.cs b/OpenSim/Region/Application/Application.cs
index 33b01e5..555baa4 100644
--- a/OpenSim/Region/Application/Application.cs
+++ b/OpenSim/Region/Application/Application.cs
@@ -91,6 +91,18 @@ namespace OpenSim
91 m_log.Info("[OPENSIM MAIN]: configured log4net using default OpenSim.exe.config"); 91 m_log.Info("[OPENSIM MAIN]: configured log4net using default OpenSim.exe.config");
92 } 92 }
93 93
94 // Increase the number of IOCP threads available. Mono defaults to a tragically low number
95 int workerThreads, iocpThreads;
96 System.Threading.ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads);
97 m_log.InfoFormat("[OPENSIM MAIN]: Runtime gave us {0} worker threads and {1} IOCP threads", workerThreads, iocpThreads);
98 if (workerThreads < 500 || iocpThreads < 1000)
99 {
100 workerThreads = 500;
101 iocpThreads = 1000;
102 m_log.Info("[OPENSIM MAIN]: Bumping up to 500 worker threads and 1000 IOCP threads");
103 System.Threading.ThreadPool.SetMaxThreads(workerThreads, iocpThreads);
104 }
105
94 // Check if the system is compatible with OpenSimulator. 106 // Check if the system is compatible with OpenSimulator.
95 // Ensures that the minimum system requirements are met 107 // Ensures that the minimum system requirements are met
96 m_log.Info("Performing compatibility checks... "); 108 m_log.Info("Performing compatibility checks... ");
diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs
index 6e7a2a0..4592c31 100644
--- a/OpenSim/Region/Application/OpenSimBase.cs
+++ b/OpenSim/Region/Application/OpenSimBase.cs
@@ -675,7 +675,7 @@ namespace OpenSim
675 675
676 if (foundClientServer) 676 if (foundClientServer)
677 { 677 {
678 m_clientServers[clientServerElement].Server.Close(); 678 m_clientServers[clientServerElement].NetworkStop();
679 m_clientServers.RemoveAt(clientServerElement); 679 m_clientServers.RemoveAt(clientServerElement);
680 } 680 }
681 IScene scene; 681 IScene scene;
diff --git a/OpenSim/Region/ClientStack/IClientNetworkServer.cs b/OpenSim/Region/ClientStack/IClientNetworkServer.cs
index a71ad4d..54a441b 100644
--- a/OpenSim/Region/ClientStack/IClientNetworkServer.cs
+++ b/OpenSim/Region/ClientStack/IClientNetworkServer.cs
@@ -38,7 +38,7 @@ namespace OpenSim.Region.ClientStack
38 IPAddress _listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, 38 IPAddress _listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource,
39 AgentCircuitManager authenticateClass); 39 AgentCircuitManager authenticateClass);
40 40
41 Socket Server { get; } 41 void NetworkStop();
42 bool HandlesRegion(Location x); 42 bool HandlesRegion(Location x);
43 void AddScene(IScene x); 43 void AddScene(IScene x);
44 44
diff --git a/OpenSim/Region/ClientStack/LindenUDP/ILLClientStackNetworkHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/ILLClientStackNetworkHandler.cs
deleted file mode 100644
index ee15171..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/ILLClientStackNetworkHandler.cs
+++ /dev/null
@@ -1,38 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System.Net.Sockets;
29
30namespace OpenSim.Region.ClientStack.LindenUDP
31{
32 public interface ILLClientStackNetworkHandler
33 {
34 void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode); // EndPoint packetSender);
35 void RemoveClientCircuit(uint circuitcode);
36 void RegisterPacketServer(LLPacketServer server);
37 }
38}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs
deleted file mode 100644
index 31f9580..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/ILLPacketHandler.cs
+++ /dev/null
@@ -1,83 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using OpenMetaverse;
30using OpenMetaverse.Packets;
31using OpenSim.Framework;
32
33namespace OpenSim.Region.ClientStack.LindenUDP
34{
35 public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
36 public delegate void PacketDrop(Packet pack, Object id);
37 public delegate void QueueEmpty(ThrottleOutPacketType queue);
38 public delegate bool SynchronizeClientHandler(IScene scene, Packet packet, UUID agentID, ThrottleOutPacketType throttlePacketType);
39
40 /// <summary>
41 /// Interface to a class that handles all the activity involved with maintaining the client circuit (handling acks,
42 /// resends, pings, etc.)
43 /// </summary>
44 public interface ILLPacketHandler : IDisposable
45 {
46 event PacketStats OnPacketStats;
47 event PacketDrop OnPacketDrop;
48 event QueueEmpty OnQueueEmpty;
49 SynchronizeClientHandler SynchronizeClient { set; }
50
51 int PacketsReceived { get; }
52 int PacketsReceivedReported { get; }
53 uint ResendTimeout { get; set; }
54 bool ReliableIsImportant { get; set; }
55 int MaxReliableResends { get; set; }
56
57 /// <summary>
58 /// Initial handling of a received packet. It will be processed later in ProcessInPacket()
59 /// </summary>
60 /// <param name="packet"></param>
61 void InPacket(Packet packet);
62
63 /// <summary>
64 /// Take action depending on the type and contents of an received packet.
65 /// </summary>
66 /// <param name="item"></param>
67 void ProcessInPacket(LLQueItem item);
68
69 void ProcessOutPacket(LLQueItem item);
70 void OutPacket(Packet NewPack,
71 ThrottleOutPacketType throttlePacketType);
72 void OutPacket(Packet NewPack,
73 ThrottleOutPacketType throttlePacketType, Object id);
74 LLPacketQueue PacketQueue { get; }
75 void Flush();
76 void Clear();
77 ClientInfo GetClientInfo();
78 void SetClientInfo(ClientInfo info);
79 void AddImportantPacket(PacketType type);
80 void RemoveImportantPacket(PacketType type);
81 int GetQueueCount(ThrottleOutPacketType queue);
82 }
83}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs b/OpenSim/Region/ClientStack/LindenUDP/IncomingPacket.cs
index 0ed2bc1..90b3ede 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/IncomingPacket.cs
@@ -1,4 +1,4 @@
1/* 1/*
2 * Copyright (c) Contributors, http://opensimulator.org/ 2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders. 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 * 4 *
@@ -26,24 +26,32 @@
26 */ 26 */
27 27
28using System; 28using System;
29using OpenMetaverse.Packets;
30using OpenSim.Framework; 29using OpenSim.Framework;
30using OpenMetaverse;
31using OpenMetaverse.Packets;
31 32
32namespace OpenSim.Region.ClientStack.LindenUDP 33namespace OpenSim.Region.ClientStack.LindenUDP
33{ 34{
34 public class LLQueItem 35 /// <summary>
36 /// Holds a reference to a <seealso cref="LLUDPClient"/> and a <seealso cref="Packet"/>
37 /// for incoming packets
38 /// </summary>
39 public sealed class IncomingPacket
35 { 40 {
36 public LLQueItem() 41 /// <summary>Client this packet came from</summary>
42 public LLUDPClient Client;
43 /// <summary>Packet data that has been received</summary>
44 public Packet Packet;
45
46 /// <summary>
47 /// Default constructor
48 /// </summary>
49 /// <param name="client">Reference to the client this packet came from</param>
50 /// <param name="packet">Packet data</param>
51 public IncomingPacket(LLUDPClient client, Packet packet)
37 { 52 {
53 Client = client;
54 Packet = packet;
38 } 55 }
39
40 public Packet Packet;
41 public bool Incoming;
42 public ThrottleOutPacketType throttleType;
43 public int TickCount;
44 public Object Identifier;
45 public int Resends;
46 public int Length;
47 public uint Sequence;
48 } 56 }
49} 57}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/KillPacket.cs b/OpenSim/Region/ClientStack/LindenUDP/IncomingPacketHistoryCollection.cs
index a80c1f0..1f73a1d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/KillPacket.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/IncomingPacketHistoryCollection.cs
@@ -1,4 +1,4 @@
1/* 1/*
2 * Copyright (c) Contributors, http://opensimulator.org/ 2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders. 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 * 4 *
@@ -25,47 +25,49 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using OpenMetaverse.Packets; 28using System;
29using System.Collections.Generic;
29 30
30namespace OpenSim.Region.ClientStack.LindenUDP 31namespace OpenSim.Region.ClientStack.LindenUDP
31{ 32{
32 /// <summary> 33 /// <summary>
33 /// When packetqueue dequeues this packet in the outgoing stream, it thread aborts 34 /// A circular buffer and hashset for tracking incoming packet sequence
34 /// Ensures that the thread abort happens from within the client thread 35 /// numbers
35 /// regardless of where the close method is called
36 /// </summary> 36 /// </summary>
37 class KillPacket : Packet 37 public sealed class IncomingPacketHistoryCollection
38 { 38 {
39 public override int Length 39 private readonly uint[] m_items;
40 { 40 private HashSet<uint> m_hashSet;
41 get { return 0; } 41 private int m_first;
42 } 42 private int m_next;
43 private int m_capacity;
43 44
44 public override void FromBytes(Header header, byte[] bytes, ref int i, ref int packetEnd) 45 public IncomingPacketHistoryCollection(int capacity)
45 { 46 {
47 this.m_capacity = capacity;
48 m_items = new uint[capacity];
49 m_hashSet = new HashSet<uint>();
46 } 50 }
47 51
48 public override void FromBytes(byte[] bytes, ref int i, ref int packetEnd, byte[] zeroBuffer) 52 public bool TryEnqueue(uint ack)
49 { 53 {
50 } 54 lock (m_hashSet)
55 {
56 if (m_hashSet.Add(ack))
57 {
58 m_items[m_next] = ack;
59 m_next = (m_next + 1) % m_capacity;
60 if (m_next == m_first)
61 {
62 m_hashSet.Remove(m_items[m_first]);
63 m_first = (m_first + 1) % m_capacity;
64 }
51 65
52 public override byte[] ToBytes() 66 return true;
53 { 67 }
54 return new byte[0]; 68 }
55 }
56 69
57 public override byte[][] ToBytesMultiple() 70 return false;
58 {
59 return new byte[][] { new byte[0] };
60 }
61
62 public KillPacket()
63 {
64 Type = PacketType.UseCircuitCode;
65 Header = new Header();
66 Header.Frequency = OpenMetaverse.PacketFrequency.Low;
67 Header.ID = 65531;
68 Header.Reliable = true;
69 } 71 }
70 } 72 }
71} 73}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
index 19ad0b4..2f1face 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
@@ -76,27 +76,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP
76 { 76 {
77 if (m_currentPacket <= m_stopPacket) 77 if (m_currentPacket <= m_stopPacket)
78 { 78 {
79 bool SendMore = true; 79 int count = 0;
80 bool sendMore = true;
81
80 if (!m_sentInfo || (m_currentPacket == 0)) 82 if (!m_sentInfo || (m_currentPacket == 0))
81 { 83 {
82 if (SendFirstPacket(client)) 84 sendMore = !SendFirstPacket(client);
83 { 85
84 SendMore = false;
85 }
86 m_sentInfo = true; 86 m_sentInfo = true;
87 m_currentPacket++; 87 ++m_currentPacket;
88 ++count;
88 } 89 }
89 if (m_currentPacket < 2) 90 if (m_currentPacket < 2)
90 { 91 {
91 m_currentPacket = 2; 92 m_currentPacket = 2;
92 } 93 }
93 94
94 int count = 0; 95 while (sendMore && count < maxpack && m_currentPacket <= m_stopPacket)
95 while (SendMore && count < maxpack && m_currentPacket <= m_stopPacket)
96 { 96 {
97 count++; 97 sendMore = SendPacket(client);
98 SendMore = SendPacket(client); 98 ++m_currentPacket;
99 m_currentPacket++; 99 ++count;
100 } 100 }
101 101
102 if (m_currentPacket > m_stopPacket) 102 if (m_currentPacket > m_stopPacket)
@@ -196,13 +196,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
196 196
197 m_currentPacket = StartPacket; 197 m_currentPacket = StartPacket;
198 } 198 }
199
200 if ((m_imageManager != null) && (m_imageManager.Client != null) && (m_imageManager.Client.PacketHandler != null))
201 if (m_imageManager.Client.PacketHandler.GetQueueCount(ThrottleOutPacketType.Texture) == 0)
202 {
203 //m_log.Debug("No textures queued, sending one packet to kickstart it");
204 SendPacket(m_imageManager.Client);
205 }
206 } 199 }
207 } 200 }
208 } 201 }
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
index cc290ed..343f537 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
@@ -172,7 +172,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
172 m_lastloopprocessed = DateTime.Now.Ticks; 172 m_lastloopprocessed = DateTime.Now.Ticks;
173 173
174 // This can happen during Close() 174 // This can happen during Close()
175 if (m_client == null || m_client.PacketHandler == null || m_client.PacketHandler.PacketQueue == null) 175 if (m_client == null)
176 return false; 176 return false;
177 177
178 while ((imagereq = GetHighestPriorityImage()) != null) 178 while ((imagereq = GetHighestPriorityImage()) != null)
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
deleted file mode 100644
index e98a360..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs
+++ /dev/null
@@ -1,870 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Reflection;
30using System.Collections.Generic;
31using System.Net.Sockets;
32using System.Threading;
33using System.Timers;
34using OpenMetaverse;
35using OpenMetaverse.Packets;
36using log4net;
37using OpenSim.Framework;
38using Timer=System.Timers.Timer;
39
40namespace OpenSim.Region.ClientStack.LindenUDP
41{
42 public class LLPacketHandler : ILLPacketHandler
43 {
44 private static readonly ILog m_log
45 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46
47 //private int m_resentCount;
48
49 // Packet queues
50 //
51 LLPacketQueue m_PacketQueue;
52
53 public LLPacketQueue PacketQueue
54 {
55 get { return m_PacketQueue; }
56 }
57
58 // Timer to run stats and acks on
59 //
60 private Timer m_AckTimer = new Timer(250);
61
62 // A list of the packets we haven't acked yet
63 //
64 private List<uint> m_PendingAcks = new List<uint>();
65 private Dictionary<uint, uint> m_PendingAcksMap = new Dictionary<uint, uint>();
66
67 private Dictionary<uint, LLQueItem> m_NeedAck =
68 new Dictionary<uint, LLQueItem>();
69
70 /// <summary>
71 /// The number of milliseconds that can pass before a packet that needs an ack is resent.
72 /// </param>
73 private uint m_ResendTimeout = 4000;
74
75 public uint ResendTimeout
76 {
77 get { return m_ResendTimeout; }
78 set { m_ResendTimeout = value; }
79 }
80
81 private int m_MaxReliableResends = 3;
82
83 public int MaxReliableResends
84 {
85 get { return m_MaxReliableResends; }
86 set { m_MaxReliableResends = value; }
87 }
88
89 // Track duplicated packets. This uses a Dictionary. Both insertion
90 // and lookup are common operations and need to take advantage of
91 // the hashing. Expiration is less common and can be allowed the
92 // time for a linear scan.
93 //
94 private List<uint> m_alreadySeenList = new List<uint>();
95 private Dictionary<uint, int>m_alreadySeenTracker = new Dictionary<uint, int>();
96 private int m_alreadySeenWindow = 30000;
97 private int m_lastAlreadySeenCheck = Environment.TickCount & Int32.MaxValue;
98
99 // private Dictionary<uint, int> m_DupeTracker =
100 // new Dictionary<uint, int>();
101 // private uint m_DupeTrackerWindow = 30;
102 // private int m_DupeTrackerLastCheck = Environment.TickCount;
103
104 // Values for the SimStatsReporter
105 //
106 private int m_PacketsReceived = 0;
107 private int m_PacketsReceivedReported = 0;
108 private int m_PacketsSent = 0;
109 private int m_PacketsSentReported = 0;
110 private int m_UnackedBytes = 0;
111
112 private int m_LastResend = 0;
113
114 public int PacketsReceived
115 {
116 get { return m_PacketsReceived; }
117 }
118
119 public int PacketsReceivedReported
120 {
121 get { return m_PacketsReceivedReported; }
122 }
123
124 // The client we are working for
125 //
126 private IClientAPI m_Client;
127
128 // Some events
129 //
130 public event PacketStats OnPacketStats;
131 public event PacketDrop OnPacketDrop;
132 public event QueueEmpty OnQueueEmpty;
133
134
135 //private SynchronizeClientHandler m_SynchronizeClient = null;
136
137 public SynchronizeClientHandler SynchronizeClient
138 {
139 set { /* m_SynchronizeClient = value; */ }
140 }
141
142 // Packet sequencing
143 //
144 private uint m_Sequence = 0;
145 private object m_SequenceLock = new object();
146 private const int MAX_SEQUENCE = 0xFFFFFF;
147
148 // Packet dropping
149 //
150 List<PacketType> m_ImportantPackets = new List<PacketType>();
151 private bool m_ReliableIsImportant = false;
152
153 public bool ReliableIsImportant
154 {
155 get { return m_ReliableIsImportant; }
156 set { m_ReliableIsImportant = value; }
157 }
158
159 private int m_DropSafeTimeout;
160
161 LLPacketServer m_PacketServer;
162 private byte[] m_ZeroOutBuffer = new byte[4096];
163
164 ////////////////////////////////////////////////////////////////////
165
166 // Constructors
167 //
168 public LLPacketHandler(IClientAPI client, LLPacketServer server, ClientStackUserSettings userSettings)
169 {
170 m_Client = client;
171 m_PacketServer = server;
172 m_DropSafeTimeout = Environment.TickCount + 15000;
173
174 m_PacketQueue = new LLPacketQueue(client.AgentId, userSettings);
175
176 m_PacketQueue.OnQueueEmpty += TriggerOnQueueEmpty;
177
178 m_AckTimer.Elapsed += AckTimerElapsed;
179 m_AckTimer.Start();
180 }
181
182 public void Dispose()
183 {
184 m_AckTimer.Stop();
185 m_AckTimer.Close();
186
187 m_PacketQueue.Enqueue(null);
188 m_PacketQueue.Close();
189 m_Client = null;
190 }
191
192 // Send one packet. This actually doesn't send anything, it queues
193 // it. Designed to be fire-and-forget, but there is an optional
194 // notifier.
195 //
196 public void OutPacket(
197 Packet packet, ThrottleOutPacketType throttlePacketType)
198 {
199 OutPacket(packet, throttlePacketType, null);
200 }
201
202 public void OutPacket(
203 Packet packet, ThrottleOutPacketType throttlePacketType,
204 Object id)
205 {
206 // Call the load balancer's hook. If this is not active here
207 // we defer to the sim server this client is actually connected
208 // to. Packet drop notifies will not be triggered in this
209 // configuration!
210 //
211
212 packet.Header.Sequence = 0;
213
214 lock (m_NeedAck)
215 {
216 DropResend(id);
217
218 AddAcks(ref packet);
219 QueuePacket(packet, throttlePacketType, id);
220 }
221 }
222
223 private void AddAcks(ref Packet packet)
224 {
225 // These packet types have shown to have issues with
226 // acks being appended to the payload, just don't send
227 // any with them until libsl is fixed.
228 //
229 if (packet is ViewerEffectPacket)
230 return;
231 if (packet is SimStatsPacket)
232 return;
233
234 // Add acks to outgoing packets
235 //
236 if (m_PendingAcks.Count > 0)
237 {
238 int count = m_PendingAcks.Count;
239 if (count > 10)
240 count = 10;
241 packet.Header.AckList = new uint[count];
242 packet.Header.AppendedAcks = true;
243
244 for (int i = 0; i < count; i++)
245 {
246 packet.Header.AckList[i] = m_PendingAcks[i];
247 m_PendingAcksMap.Remove(m_PendingAcks[i]);
248 }
249 m_PendingAcks.RemoveRange(0, count);
250 }
251 }
252
253 private void QueuePacket(
254 Packet packet, ThrottleOutPacketType throttlePacketType,
255 Object id)
256 {
257 LLQueItem item = new LLQueItem();
258 item.Packet = packet;
259 item.Incoming = false;
260 item.throttleType = throttlePacketType;
261 item.TickCount = Environment.TickCount;
262 item.Identifier = id;
263 item.Resends = 0;
264 item.Length = packet.Length;
265 item.Sequence = packet.Header.Sequence;
266
267 m_PacketQueue.Enqueue(item);
268 m_PacketsSent++;
269 }
270
271 private void ResendUnacked()
272 {
273 int now = Environment.TickCount;
274
275 int intervalMs = 250;
276
277 if (m_LastResend != 0)
278 intervalMs = now - m_LastResend;
279
280 lock (m_NeedAck)
281 {
282 if (m_DropSafeTimeout > now ||
283 intervalMs > 500) // We were frozen!
284 {
285 foreach (LLQueItem data in m_NeedAck.Values)
286 {
287 if (m_DropSafeTimeout > now)
288 {
289 m_NeedAck[data.Packet.Header.Sequence].TickCount = now;
290 }
291 else
292 {
293 m_NeedAck[data.Packet.Header.Sequence].TickCount += intervalMs;
294 }
295 }
296 }
297
298 m_LastResend = now;
299
300 // Unless we have received at least one ack, don't bother resending
301 // anything. There may not be a client there, don't clog up the
302 // pipes.
303
304
305 // Nothing to do
306 //
307 if (m_NeedAck.Count == 0)
308 return;
309
310 int resent = 0;
311 long dueDate = now - m_ResendTimeout;
312
313 List<LLQueItem> dropped = new List<LLQueItem>();
314 foreach (LLQueItem data in m_NeedAck.Values)
315 {
316 Packet packet = data.Packet;
317
318 // Packets this old get resent
319 //
320 if (data.TickCount < dueDate && data.Sequence != 0 && !m_PacketQueue.Contains(data.Sequence))
321 {
322 if (resent < 20) // Was 20 (= Max 117kbit/sec resends)
323 {
324 m_NeedAck[packet.Header.Sequence].Resends++;
325
326 // The client needs to be told that a packet is being resent, otherwise it appears to believe
327 // that it should reset its sequence to that packet number.
328 packet.Header.Resent = true;
329
330 if ((m_NeedAck[packet.Header.Sequence].Resends >= m_MaxReliableResends) &&
331 (!m_ReliableIsImportant))
332 {
333 dropped.Add(data);
334 continue;
335 }
336
337 m_NeedAck[packet.Header.Sequence].TickCount = Environment.TickCount;
338 QueuePacket(packet, ThrottleOutPacketType.Resend, data.Identifier);
339 resent++;
340 }
341 else
342 {
343 m_NeedAck[packet.Header.Sequence].TickCount += intervalMs;
344 }
345 }
346 }
347
348 foreach (LLQueItem data in dropped)
349 {
350 m_NeedAck.Remove(data.Packet.Header.Sequence);
351 TriggerOnPacketDrop(data.Packet, data.Identifier);
352 m_PacketQueue.Cancel(data.Packet.Header.Sequence);
353 PacketPool.Instance.ReturnPacket(data.Packet);
354 }
355 }
356 }
357
358 // Send the pending packet acks to the client
359 // Will send blocks of acks for up to 250 packets
360 //
361 private void SendAcks()
362 {
363 lock (m_NeedAck)
364 {
365 if (m_PendingAcks.Count == 0)
366 return;
367
368 PacketAckPacket acks = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck);
369
370 // The case of equality is more common than one might think,
371 // because this function will be called unconditionally when
372 // the counter reaches 250. So there is a good chance another
373 // packet with 250 blocks exists.
374 //
375 if (acks.Packets == null ||
376 acks.Packets.Length != m_PendingAcks.Count)
377 acks.Packets = new PacketAckPacket.PacketsBlock[m_PendingAcks.Count];
378
379 for (int i = 0; i < m_PendingAcks.Count; i++)
380 {
381 acks.Packets[i] = new PacketAckPacket.PacketsBlock();
382 acks.Packets[i].ID = m_PendingAcks[i];
383
384 }
385 m_PendingAcks.Clear();
386 m_PendingAcksMap.Clear();
387
388 acks.Header.Reliable = false;
389 OutPacket(acks, ThrottleOutPacketType.Unknown);
390 }
391 }
392
393 // Queue a packet ack. It will be sent either after 250 acks are
394 // queued, or when the timer fires.
395 //
396 private void AckPacket(Packet packet)
397 {
398 lock (m_NeedAck)
399 {
400 if (m_PendingAcks.Count < 250)
401 {
402 if (!m_PendingAcksMap.ContainsKey(packet.Header.Sequence))
403 {
404 m_PendingAcks.Add(packet.Header.Sequence);
405 m_PendingAcksMap.Add(packet.Header.Sequence,
406 packet.Header.Sequence);
407 }
408 return;
409 }
410 }
411
412 SendAcks();
413
414 lock (m_NeedAck)
415 {
416 // If this is still full we have a truly exceptional
417 // condition (means, can't happen)
418 //
419 if (m_PendingAcks.Count < 250)
420 {
421 if (!m_PendingAcksMap.ContainsKey(packet.Header.Sequence))
422 {
423 m_PendingAcks.Add(packet.Header.Sequence);
424 m_PendingAcksMap.Add(packet.Header.Sequence,
425 packet.Header.Sequence);
426 }
427 return;
428 }
429 }
430 }
431
432 // When the timer elapses, send the pending acks, trigger resends
433 // and report all the stats.
434 //
435 private void AckTimerElapsed(object sender, ElapsedEventArgs ea)
436 {
437 SendAcks();
438 ResendUnacked();
439 SendPacketStats();
440 }
441
442 // Push out pachet counts for the sim status reporter
443 //
444 private void SendPacketStats()
445 {
446 PacketStats handlerPacketStats = OnPacketStats;
447 if (handlerPacketStats != null)
448 {
449 handlerPacketStats(
450 m_PacketsReceived - m_PacketsReceivedReported,
451 m_PacketsSent - m_PacketsSentReported,
452 m_UnackedBytes);
453
454 m_PacketsReceivedReported = m_PacketsReceived;
455 m_PacketsSentReported = m_PacketsSent;
456 }
457 }
458
459 // We can't keep an unlimited record of dupes. This will prune the
460 // dictionary by age.
461 //
462 // NOTE: this needs to be called from within lock
463 // (m_alreadySeenTracker) context!
464 private void ExpireSeenPackets()
465 {
466 if (m_alreadySeenList.Count < 1024)
467 return;
468
469 int ticks = 0;
470 int tc = Environment.TickCount & Int32.MaxValue;
471 if (tc >= m_lastAlreadySeenCheck)
472 ticks = tc - m_lastAlreadySeenCheck;
473 else
474 ticks = Int32.MaxValue - m_lastAlreadySeenCheck + tc;
475
476 if (ticks < 2000) return;
477 m_lastAlreadySeenCheck = tc;
478
479 // we calculate the drop dead tick count here instead of
480 // in the loop: any packet with a timestamp before
481 // dropDeadTC can be expired
482 int dropDeadTC = tc - m_alreadySeenWindow;
483 int i = 0;
484 while (i < m_alreadySeenList.Count && m_alreadySeenTracker[m_alreadySeenList[i]] < dropDeadTC)
485 {
486 m_alreadySeenTracker.Remove(m_alreadySeenList[i]);
487 i++;
488 }
489 // if we dropped packet from m_alreadySeenTracker we need
490 // to drop them from m_alreadySeenList as well, let's do
491 // that in one go: the list is ordered after all.
492 if (i > 0)
493 {
494 m_alreadySeenList.RemoveRange(0, i);
495 // m_log.DebugFormat("[CLIENT]: expired {0} packets, {1}:{2} left", i, m_alreadySeenList.Count, m_alreadySeenTracker.Count);
496 }
497 }
498
499 public void InPacket(Packet packet)
500 {
501 if (packet == null)
502 return;
503
504 // When too many acks are needed to be sent, the client sends
505 // a packet consisting of acks only
506 //
507 if (packet.Type == PacketType.PacketAck)
508 {
509 PacketAckPacket ackPacket = (PacketAckPacket)packet;
510
511 foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets)
512 {
513 ProcessAck(block.ID);
514 }
515
516 PacketPool.Instance.ReturnPacket(packet);
517 return;
518 }
519
520 // Any packet can have some packet acks in the header.
521 // Process them here
522 //
523 if (packet.Header.AppendedAcks)
524 {
525 foreach (uint id in packet.Header.AckList)
526 {
527 ProcessAck(id);
528 }
529 }
530
531 // If this client is on another partial instance, no need
532 // to handle packets
533 //
534 if (!m_Client.IsActive && packet.Type != PacketType.LogoutRequest)
535 {
536 PacketPool.Instance.ReturnPacket(packet);
537 return;
538 }
539
540 if (packet.Type == PacketType.StartPingCheck)
541 {
542 StartPingCheckPacket startPing = (StartPingCheckPacket)packet;
543 CompletePingCheckPacket endPing
544 = (CompletePingCheckPacket)PacketPool.Instance.GetPacket(PacketType.CompletePingCheck);
545
546 endPing.PingID.PingID = startPing.PingID.PingID;
547 OutPacket(endPing, ThrottleOutPacketType.Task);
548 }
549 else
550 {
551 LLQueItem item = new LLQueItem();
552 item.Packet = packet;
553 item.Incoming = true;
554 m_PacketQueue.Enqueue(item);
555 }
556 }
557
558 public void ProcessInPacket(LLQueItem item)
559 {
560 Packet packet = item.Packet;
561
562 // Always ack the packet!
563 //
564 if (packet.Header.Reliable)
565 AckPacket(packet);
566
567 if (packet.Type != PacketType.AgentUpdate)
568 m_PacketsReceived++;
569
570 // Check for duplicate packets.. packets that the client is
571 // resending because it didn't receive our ack
572 //
573 lock (m_alreadySeenTracker)
574 {
575 ExpireSeenPackets();
576
577 if (m_alreadySeenTracker.ContainsKey(packet.Header.Sequence))
578 return;
579
580 m_alreadySeenTracker.Add(packet.Header.Sequence, Environment.TickCount & Int32.MaxValue);
581 m_alreadySeenList.Add(packet.Header.Sequence);
582 }
583
584 m_Client.ProcessInPacket(packet);
585 }
586
587 public void Flush()
588 {
589 m_PacketQueue.Flush();
590 m_UnackedBytes = (-1 * m_UnackedBytes);
591 SendPacketStats();
592 }
593
594 public void Clear()
595 {
596 m_UnackedBytes = (-1 * m_UnackedBytes);
597 SendPacketStats();
598 lock (m_NeedAck)
599 {
600 m_NeedAck.Clear();
601 m_PendingAcks.Clear();
602 m_PendingAcksMap.Clear();
603 }
604 m_Sequence += 1000000;
605 }
606
607 private void ProcessAck(uint id)
608 {
609 LLQueItem data;
610
611 lock (m_NeedAck)
612 {
613 //m_log.DebugFormat("[CLIENT]: In {0} received ack for packet {1}", m_Client.Scene.RegionInfo.ExternalEndPoint.Port, id);
614
615 if (!m_NeedAck.TryGetValue(id, out data))
616 return;
617
618 m_NeedAck.Remove(id);
619 m_PacketQueue.Cancel(data.Sequence);
620 PacketPool.Instance.ReturnPacket(data.Packet);
621 m_UnackedBytes -= data.Length;
622 }
623 }
624
625 // Allocate packet sequence numbers in a threadsave manner
626 //
627 protected uint NextPacketSequenceNumber()
628 {
629 // Set the sequence number
630 uint seq = 1;
631 lock (m_SequenceLock)
632 {
633 if (m_Sequence >= MAX_SEQUENCE)
634 {
635 m_Sequence = 1;
636 }
637 else
638 {
639 m_Sequence++;
640 }
641 seq = m_Sequence;
642 }
643 return seq;
644 }
645
646 public ClientInfo GetClientInfo()
647 {
648 ClientInfo info = new ClientInfo();
649
650 info.pendingAcks = m_PendingAcksMap;
651 info.needAck = new Dictionary<uint, byte[]>();
652
653 lock (m_NeedAck)
654 {
655 foreach (uint key in m_NeedAck.Keys)
656 info.needAck.Add(key, m_NeedAck[key].Packet.ToBytes());
657 }
658
659 LLQueItem[] queitems = m_PacketQueue.GetQueueArray();
660
661 for (int i = 0; i < queitems.Length; i++)
662 {
663 if (queitems[i].Incoming == false)
664 info.out_packets.Add(queitems[i].Packet.ToBytes());
665 }
666
667 info.sequence = m_Sequence;
668
669 float multiplier = m_PacketQueue.ThrottleMultiplier;
670 info.resendThrottle = (int) (m_PacketQueue.ResendThrottle.Throttle / multiplier);
671 info.landThrottle = (int) (m_PacketQueue.LandThrottle.Throttle / multiplier);
672 info.windThrottle = (int) (m_PacketQueue.WindThrottle.Throttle / multiplier);
673 info.cloudThrottle = (int) (m_PacketQueue.CloudThrottle.Throttle / multiplier);
674 info.taskThrottle = (int) (m_PacketQueue.TaskThrottle.Throttle / multiplier);
675 info.assetThrottle = (int) (m_PacketQueue.AssetThrottle.Throttle / multiplier);
676 info.textureThrottle = (int) (m_PacketQueue.TextureThrottle.Throttle / multiplier);
677 info.totalThrottle = (int) (m_PacketQueue.TotalThrottle.Throttle / multiplier);
678
679 return info;
680 }
681
682 public void SetClientInfo(ClientInfo info)
683 {
684 m_PendingAcksMap = info.pendingAcks;
685 m_PendingAcks = new List<uint>(m_PendingAcksMap.Keys);
686 m_NeedAck = new Dictionary<uint, LLQueItem>();
687
688 Packet packet = null;
689 int packetEnd = 0;
690 byte[] zero = new byte[3000];
691
692 foreach (uint key in info.needAck.Keys)
693 {
694 byte[] buff = info.needAck[key];
695 packetEnd = buff.Length - 1;
696
697 try
698 {
699 packet = PacketPool.Instance.GetPacket(buff, ref packetEnd, zero);
700 }
701 catch (Exception)
702 {
703 }
704
705 LLQueItem item = new LLQueItem();
706 item.Packet = packet;
707 item.Incoming = false;
708 item.throttleType = 0;
709 item.TickCount = Environment.TickCount;
710 item.Identifier = 0;
711 item.Resends = 0;
712 item.Length = packet.Length;
713 item.Sequence = packet.Header.Sequence;
714 m_NeedAck.Add(key, item);
715 }
716
717 m_Sequence = info.sequence;
718
719 m_PacketQueue.ResendThrottle.Throttle = info.resendThrottle;
720 m_PacketQueue.LandThrottle.Throttle = info.landThrottle;
721 m_PacketQueue.WindThrottle.Throttle = info.windThrottle;
722 m_PacketQueue.CloudThrottle.Throttle = info.cloudThrottle;
723 m_PacketQueue.TaskThrottle.Throttle = info.taskThrottle;
724 m_PacketQueue.AssetThrottle.Throttle = info.assetThrottle;
725 m_PacketQueue.TextureThrottle.Throttle = info.textureThrottle;
726 m_PacketQueue.TotalThrottle.Throttle = info.totalThrottle;
727 }
728
729 public void AddImportantPacket(PacketType type)
730 {
731 if (m_ImportantPackets.Contains(type))
732 return;
733
734 m_ImportantPackets.Add(type);
735 }
736
737 public void RemoveImportantPacket(PacketType type)
738 {
739 if (!m_ImportantPackets.Contains(type))
740 return;
741
742 m_ImportantPackets.Remove(type);
743 }
744
745 private void DropResend(Object id)
746 {
747 LLQueItem d = null;
748
749 foreach (LLQueItem data in m_NeedAck.Values)
750 {
751 if (data.Identifier != null && data.Identifier == id)
752 {
753 d = data;
754 break;
755 }
756 }
757
758 if (null == d) return;
759
760 m_NeedAck.Remove(d.Packet.Header.Sequence);
761 m_PacketQueue.Cancel(d.Sequence);
762 PacketPool.Instance.ReturnPacket(d.Packet);
763 }
764
765 private void TriggerOnPacketDrop(Packet packet, Object id)
766 {
767 PacketDrop handlerPacketDrop = OnPacketDrop;
768
769 if (handlerPacketDrop == null)
770 return;
771
772 handlerPacketDrop(packet, id);
773 }
774
775 private void TriggerOnQueueEmpty(ThrottleOutPacketType queue)
776 {
777 QueueEmpty handlerQueueEmpty = OnQueueEmpty;
778
779 if (handlerQueueEmpty != null)
780 handlerQueueEmpty(queue);
781 }
782
783 // Convert the packet to bytes and stuff it onto the send queue
784 //
785 public void ProcessOutPacket(LLQueItem item)
786 {
787 Packet packet = item.Packet;
788
789 // Assign sequence number here to prevent out of order packets
790 if (packet.Header.Sequence == 0)
791 {
792 lock (m_NeedAck)
793 {
794 packet.Header.Sequence = NextPacketSequenceNumber();
795 item.Sequence = packet.Header.Sequence;
796 item.TickCount = Environment.TickCount;
797
798 // We want to see that packet arrive if it's reliable
799 if (packet.Header.Reliable)
800 {
801 m_UnackedBytes += item.Length;
802
803 // Keep track of when this packet was sent out
804 item.TickCount = Environment.TickCount;
805
806 m_NeedAck[packet.Header.Sequence] = item;
807 }
808 }
809 }
810
811 // If we sent a killpacket
812 if (packet is KillPacket)
813 Abort();
814
815 try
816 {
817 // If this packet has been reused/returned, the ToBytes
818 // will blow up in our face.
819 // Fail gracefully.
820 //
821
822 // Actually make the byte array and send it
823 byte[] sendbuffer = item.Packet.ToBytes();
824
825 if (packet.Header.Zerocoded)
826 {
827 int packetsize = Helpers.ZeroEncode(sendbuffer,
828 sendbuffer.Length, m_ZeroOutBuffer);
829 m_PacketServer.SendPacketTo(m_ZeroOutBuffer, packetsize,
830 SocketFlags.None, m_Client.CircuitCode);
831 }
832 else
833 {
834 // Need some extra space in case we need to add proxy
835 // information to the message later
836 Buffer.BlockCopy(sendbuffer, 0, m_ZeroOutBuffer, 0,
837 sendbuffer.Length);
838 m_PacketServer.SendPacketTo(m_ZeroOutBuffer,
839 sendbuffer.Length, SocketFlags.None, m_Client.CircuitCode);
840 }
841 }
842 catch (NullReferenceException)
843 {
844 m_log.Error("[PACKET]: Detected reuse of a returned packet");
845 m_PacketQueue.Cancel(item.Sequence);
846 return;
847 }
848
849 // If this is a reliable packet, we are still holding a ref
850 // Dont't return in that case
851 //
852 if (!packet.Header.Reliable)
853 {
854 m_PacketQueue.Cancel(item.Sequence);
855 PacketPool.Instance.ReturnPacket(packet);
856 }
857 }
858
859 private void Abort()
860 {
861 m_PacketQueue.Close();
862 Thread.CurrentThread.Abort();
863 }
864
865 public int GetQueueCount(ThrottleOutPacketType queue)
866 {
867 return m_PacketQueue.GetQueueCount(queue);
868 }
869 }
870}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
deleted file mode 100644
index 3eed2e0..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs
+++ /dev/null
@@ -1,742 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using System.Threading;
32using System.Timers;
33using log4net;
34using OpenMetaverse;
35using OpenSim.Framework;
36using OpenSim.Framework.Statistics;
37using OpenSim.Framework.Statistics.Interfaces;
38using Timer=System.Timers.Timer;
39
40namespace OpenSim.Region.ClientStack.LindenUDP
41{
42 public class LLPacketQueue : IPullStatsProvider, IDisposable
43 {
44 private static readonly ILog m_log
45 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46
47 /// <summary>
48 /// Is queueing enabled at all?
49 /// </summary>
50 private bool m_enabled = true;
51
52 private OpenSim.Framework.BlockingQueue<LLQueItem> SendQueue;
53
54 private Queue<LLQueItem> IncomingPacketQueue;
55 private Queue<LLQueItem> OutgoingPacketQueue;
56 private Queue<LLQueItem> ResendOutgoingPacketQueue;
57 private Queue<LLQueItem> LandOutgoingPacketQueue;
58 private Queue<LLQueItem> WindOutgoingPacketQueue;
59 private Queue<LLQueItem> CloudOutgoingPacketQueue;
60 private Queue<LLQueItem> TaskOutgoingPacketQueue;
61 private Queue<LLQueItem> TaskLowpriorityPacketQueue;
62 private Queue<LLQueItem> TextureOutgoingPacketQueue;
63 private Queue<LLQueItem> AssetOutgoingPacketQueue;
64
65 private List<ThrottleOutPacketType> Empty = new List<ThrottleOutPacketType>();
66 // m_log.Info("[THROTTLE]: Entering Throttle");
67 // private Dictionary<uint, uint> PendingAcks = new Dictionary<uint, uint>();
68 // private Dictionary<uint, Packet> NeedAck = new Dictionary<uint, Packet>();
69
70 // All throttle times and number of bytes are calculated by dividing by this value
71 // This value also determines how many times per throttletimems the timer will run
72 // If throttleimems is 1000 ms, then the timer will fire every 1000/7 milliseconds
73
74 private float throttleMultiplier = 2.0f; // Default value really doesn't matter.
75 private int throttleTimeDivisor = 7;
76
77 private int throttletimems = 1000;
78
79 internal LLPacketThrottle ResendThrottle;
80 internal LLPacketThrottle LandThrottle;
81 internal LLPacketThrottle WindThrottle;
82 internal LLPacketThrottle CloudThrottle;
83 internal LLPacketThrottle TaskThrottle;
84 internal LLPacketThrottle AssetThrottle;
85 internal LLPacketThrottle TextureThrottle;
86 internal LLPacketThrottle TotalThrottle;
87
88 private Dictionary<uint,int> contents = new Dictionary<uint, int>();
89
90 // private long LastThrottle;
91 // private long ThrottleInterval;
92 private Timer throttleTimer;
93
94 private UUID m_agentId;
95
96 public event QueueEmpty OnQueueEmpty;
97
98 public LLPacketQueue(UUID agentId, ClientStackUserSettings userSettings)
99 {
100 // While working on this, the BlockingQueue had me fooled for a bit.
101 // The Blocking queue causes the thread to stop until there's something
102 // in it to process. it's an on-purpose threadlock though because
103 // without it, the clientloop will suck up all sim resources.
104
105 SendQueue = new OpenSim.Framework.BlockingQueue<LLQueItem>();
106
107 IncomingPacketQueue = new Queue<LLQueItem>();
108 OutgoingPacketQueue = new Queue<LLQueItem>();
109 ResendOutgoingPacketQueue = new Queue<LLQueItem>();
110 LandOutgoingPacketQueue = new Queue<LLQueItem>();
111 WindOutgoingPacketQueue = new Queue<LLQueItem>();
112 CloudOutgoingPacketQueue = new Queue<LLQueItem>();
113 TaskOutgoingPacketQueue = new Queue<LLQueItem>();
114 TaskLowpriorityPacketQueue = new Queue<LLQueItem>();
115 TextureOutgoingPacketQueue = new Queue<LLQueItem>();
116 AssetOutgoingPacketQueue = new Queue<LLQueItem>();
117
118 // Store the throttle multiplier for posterity.
119 throttleMultiplier = userSettings.ClientThrottleMultipler;
120
121
122 int throttleMaxBPS = 1500000;
123 if (userSettings.TotalThrottleSettings != null)
124 throttleMaxBPS = userSettings.TotalThrottleSettings.Max;
125
126 // Set up the throttle classes (min, max, current) in bits per second
127 ResendThrottle = new LLPacketThrottle(5000, throttleMaxBPS / 15, 16000, userSettings.ClientThrottleMultipler);
128 LandThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 15, 2000, userSettings.ClientThrottleMultipler);
129 WindThrottle = new LLPacketThrottle(0, throttleMaxBPS / 15, 0, userSettings.ClientThrottleMultipler);
130 CloudThrottle = new LLPacketThrottle(0, throttleMaxBPS / 15, 0, userSettings.ClientThrottleMultipler);
131 TaskThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 3000, userSettings.ClientThrottleMultipler);
132 AssetThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 1000, userSettings.ClientThrottleMultipler);
133 TextureThrottle = new LLPacketThrottle(1000, throttleMaxBPS / 2, 4000, userSettings.ClientThrottleMultipler);
134
135
136 // Total Throttle trumps all - it is the number of bits in total that are allowed to go out per second.
137
138
139 ThrottleSettings totalThrottleSettings = userSettings.TotalThrottleSettings;
140 if (null == totalThrottleSettings)
141 {
142 totalThrottleSettings = new ThrottleSettings(0, throttleMaxBPS, 28000);
143 }
144
145 TotalThrottle
146 = new LLPacketThrottle(
147 totalThrottleSettings.Min, totalThrottleSettings.Max, totalThrottleSettings.Current,
148 userSettings.ClientThrottleMultipler);
149
150 throttleTimer = new Timer((int)(throttletimems / throttleTimeDivisor));
151 throttleTimer.Elapsed += ThrottleTimerElapsed;
152 throttleTimer.Start();
153
154 // TIMERS needed for this
155 // LastThrottle = DateTime.Now.Ticks;
156 // ThrottleInterval = (long)(throttletimems/throttleTimeDivisor);
157
158 m_agentId = agentId;
159
160 if (StatsManager.SimExtraStats != null)
161 {
162 StatsManager.SimExtraStats.RegisterPacketQueueStatsProvider(m_agentId, this);
163 }
164 }
165
166 /* STANDARD QUEUE MANIPULATION INTERFACES */
167
168 public void Enqueue(LLQueItem item)
169 {
170 if (!m_enabled)
171 {
172 return;
173 }
174 // We could micro lock, but that will tend to actually
175 // probably be worse than just synchronizing on SendQueue
176
177 if (item == null)
178 {
179 SendQueue.Enqueue(item);
180 return;
181 }
182
183 if (item.Incoming)
184 {
185 SendQueue.PriorityEnqueue(item);
186 return;
187 }
188
189 if (item.Sequence != 0)
190 lock (contents)
191 {
192 if (contents.ContainsKey(item.Sequence))
193 contents[item.Sequence] += 1;
194 else
195 contents.Add(item.Sequence, 1);
196 }
197
198 lock (this)
199 {
200 switch (item.throttleType & ThrottleOutPacketType.TypeMask)
201 {
202 case ThrottleOutPacketType.Resend:
203 ThrottleCheck(ref ResendThrottle, ref ResendOutgoingPacketQueue, item, ThrottleOutPacketType.Resend);
204 break;
205 case ThrottleOutPacketType.Texture:
206 ThrottleCheck(ref TextureThrottle, ref TextureOutgoingPacketQueue, item, ThrottleOutPacketType.Texture);
207 break;
208 case ThrottleOutPacketType.Task:
209 if ((item.throttleType & ThrottleOutPacketType.LowPriority) != 0)
210 ThrottleCheck(ref TaskThrottle, ref TaskLowpriorityPacketQueue, item, ThrottleOutPacketType.Task);
211 else
212 ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item, ThrottleOutPacketType.Task);
213 break;
214 case ThrottleOutPacketType.Land:
215 ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item, ThrottleOutPacketType.Land);
216 break;
217 case ThrottleOutPacketType.Asset:
218 ThrottleCheck(ref AssetThrottle, ref AssetOutgoingPacketQueue, item, ThrottleOutPacketType.Asset);
219 break;
220 case ThrottleOutPacketType.Cloud:
221 ThrottleCheck(ref CloudThrottle, ref CloudOutgoingPacketQueue, item, ThrottleOutPacketType.Cloud);
222 break;
223 case ThrottleOutPacketType.Wind:
224 ThrottleCheck(ref WindThrottle, ref WindOutgoingPacketQueue, item, ThrottleOutPacketType.Wind);
225 break;
226
227 default:
228 // Acknowledgements and other such stuff should go directly to the blocking Queue
229 // Throttling them may and likely 'will' be problematic
230 SendQueue.PriorityEnqueue(item);
231 break;
232 }
233 }
234 }
235
236 public LLQueItem Dequeue()
237 {
238 while (true)
239 {
240 LLQueItem item = SendQueue.Dequeue();
241 if (item == null)
242 return null;
243 if (item.Incoming)
244 return item;
245 item.TickCount = System.Environment.TickCount;
246 if (item.Sequence == 0)
247 return item;
248 lock (contents)
249 {
250 if (contents.ContainsKey(item.Sequence))
251 {
252 if (contents[item.Sequence] == 1)
253 contents.Remove(item.Sequence);
254 else
255 contents[item.Sequence] -= 1;
256 return item;
257 }
258 }
259 }
260 }
261
262 public void Cancel(uint sequence)
263 {
264 lock (contents) contents.Remove(sequence);
265 }
266
267 public bool Contains(uint sequence)
268 {
269 lock (contents) return contents.ContainsKey(sequence);
270 }
271
272 public void Flush()
273 {
274 lock (this)
275 {
276 // These categories do not contain transactional packets so we can safely drop any pending data in them
277 LandOutgoingPacketQueue.Clear();
278 WindOutgoingPacketQueue.Clear();
279 CloudOutgoingPacketQueue.Clear();
280 TextureOutgoingPacketQueue.Clear();
281 AssetOutgoingPacketQueue.Clear();
282
283 // Now comes the fun part.. we dump all remaining resend and task packets into the send queue
284 while (ResendOutgoingPacketQueue.Count > 0 || TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0)
285 {
286 if (ResendOutgoingPacketQueue.Count > 0)
287 SendQueue.Enqueue(ResendOutgoingPacketQueue.Dequeue());
288
289 if (TaskOutgoingPacketQueue.Count > 0)
290 SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue());
291
292 if (TaskLowpriorityPacketQueue.Count > 0)
293 SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue());
294 }
295 }
296 }
297
298 public void WipeClean()
299 {
300 lock (this)
301 {
302 ResendOutgoingPacketQueue.Clear();
303 LandOutgoingPacketQueue.Clear();
304 WindOutgoingPacketQueue.Clear();
305 CloudOutgoingPacketQueue.Clear();
306 TaskOutgoingPacketQueue.Clear();
307 TaskLowpriorityPacketQueue.Clear();
308 TextureOutgoingPacketQueue.Clear();
309 AssetOutgoingPacketQueue.Clear();
310 SendQueue.Clear();
311 lock (contents) contents.Clear();
312 }
313 }
314
315 public void Close()
316 {
317 Dispose();
318 }
319
320 public void Dispose()
321 {
322 Flush();
323 WipeClean(); // I'm sure there's a dirty joke in here somewhere. -AFrisby
324
325 m_enabled = false;
326 throttleTimer.Stop();
327 throttleTimer.Close();
328
329 if (StatsManager.SimExtraStats != null)
330 {
331 StatsManager.SimExtraStats.DeregisterPacketQueueStatsProvider(m_agentId);
332 }
333 }
334
335 private void ResetCounters()
336 {
337 ResendThrottle.Reset();
338 LandThrottle.Reset();
339 WindThrottle.Reset();
340 CloudThrottle.Reset();
341 TaskThrottle.Reset();
342 AssetThrottle.Reset();
343 TextureThrottle.Reset();
344 TotalThrottle.Reset();
345 }
346
347 private bool PacketsWaiting()
348 {
349 return (ResendOutgoingPacketQueue.Count > 0 ||
350 LandOutgoingPacketQueue.Count > 0 ||
351 WindOutgoingPacketQueue.Count > 0 ||
352 CloudOutgoingPacketQueue.Count > 0 ||
353 TaskOutgoingPacketQueue.Count > 0 ||
354 TaskLowpriorityPacketQueue.Count > 0 ||
355 AssetOutgoingPacketQueue.Count > 0 ||
356 TextureOutgoingPacketQueue.Count > 0);
357 }
358
359 public void ProcessThrottle()
360 {
361 // I was considering this.. Will an event fire if the thread it's on is blocked?
362
363 // Then I figured out.. it doesn't really matter.. because this thread won't be blocked for long
364 // The General overhead of the UDP protocol gets sent to the queue un-throttled by this
365 // so This'll pick up about around the right time.
366
367 int MaxThrottleLoops = 4550; // 50*7 packets can be dequeued at once.
368 int throttleLoops = 0;
369 List<ThrottleOutPacketType> e;
370
371 // We're going to dequeue all of the saved up packets until
372 // we've hit the throttle limit or there's no more packets to send
373 lock (this)
374 {
375 // this variable will be true if there was work done in the last execution of the
376 // loop, since each pass through the loop checks the queue length, we no longer
377 // need the check on entering the loop
378 bool qchanged = true;
379
380 ResetCounters();
381
382 while (TotalThrottle.UnderLimit() && qchanged && throttleLoops <= MaxThrottleLoops)
383 {
384 qchanged = false; // We will break out of the loop if no work was accomplished
385
386 throttleLoops++;
387 //Now comes the fun part.. we dump all our elements into m_packetQueue that we've saved up.
388 if ((ResendOutgoingPacketQueue.Count > 0) && ResendThrottle.UnderLimit())
389 {
390 LLQueItem qpack = ResendOutgoingPacketQueue.Dequeue();
391
392 SendQueue.Enqueue(qpack);
393 TotalThrottle.AddBytes(qpack.Length);
394 ResendThrottle.AddBytes(qpack.Length);
395
396 qchanged = true;
397 }
398
399 if ((LandOutgoingPacketQueue.Count > 0) && LandThrottle.UnderLimit())
400 {
401 LLQueItem qpack = LandOutgoingPacketQueue.Dequeue();
402
403 SendQueue.Enqueue(qpack);
404 TotalThrottle.AddBytes(qpack.Length);
405 LandThrottle.AddBytes(qpack.Length);
406 qchanged = true;
407
408 if (LandOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Land))
409 Empty.Add(ThrottleOutPacketType.Land);
410 }
411
412 if ((WindOutgoingPacketQueue.Count > 0) && WindThrottle.UnderLimit())
413 {
414 LLQueItem qpack = WindOutgoingPacketQueue.Dequeue();
415
416 SendQueue.Enqueue(qpack);
417 TotalThrottle.AddBytes(qpack.Length);
418 WindThrottle.AddBytes(qpack.Length);
419 qchanged = true;
420
421 if (WindOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Wind))
422 Empty.Add(ThrottleOutPacketType.Wind);
423 }
424
425 if ((CloudOutgoingPacketQueue.Count > 0) && CloudThrottle.UnderLimit())
426 {
427 LLQueItem qpack = CloudOutgoingPacketQueue.Dequeue();
428
429 SendQueue.Enqueue(qpack);
430 TotalThrottle.AddBytes(qpack.Length);
431 CloudThrottle.AddBytes(qpack.Length);
432 qchanged = true;
433
434 if (CloudOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Cloud))
435 Empty.Add(ThrottleOutPacketType.Cloud);
436 }
437
438 if ((TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0) && TaskThrottle.UnderLimit())
439 {
440 LLQueItem qpack;
441 if (TaskOutgoingPacketQueue.Count > 0)
442 {
443 qpack = TaskOutgoingPacketQueue.Dequeue();
444 SendQueue.PriorityEnqueue(qpack);
445 }
446 else
447 {
448 qpack = TaskLowpriorityPacketQueue.Dequeue();
449 SendQueue.Enqueue(qpack);
450 }
451
452 TotalThrottle.AddBytes(qpack.Length);
453 TaskThrottle.AddBytes(qpack.Length);
454 qchanged = true;
455
456 if (TaskOutgoingPacketQueue.Count == 0 && TaskLowpriorityPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Task))
457 Empty.Add(ThrottleOutPacketType.Task);
458 }
459
460 if ((TextureOutgoingPacketQueue.Count > 0) && TextureThrottle.UnderLimit())
461 {
462 LLQueItem qpack = TextureOutgoingPacketQueue.Dequeue();
463
464 SendQueue.Enqueue(qpack);
465 TotalThrottle.AddBytes(qpack.Length);
466 TextureThrottle.AddBytes(qpack.Length);
467 qchanged = true;
468
469 if (TextureOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Texture))
470 Empty.Add(ThrottleOutPacketType.Texture);
471 }
472
473 if ((AssetOutgoingPacketQueue.Count > 0) && AssetThrottle.UnderLimit())
474 {
475 LLQueItem qpack = AssetOutgoingPacketQueue.Dequeue();
476
477 SendQueue.Enqueue(qpack);
478 TotalThrottle.AddBytes(qpack.Length);
479 AssetThrottle.AddBytes(qpack.Length);
480 qchanged = true;
481
482 if (AssetOutgoingPacketQueue.Count == 0 && !Empty.Contains(ThrottleOutPacketType.Asset))
483 Empty.Add(ThrottleOutPacketType.Asset);
484 }
485 }
486 // m_log.Info("[THROTTLE]: Processed " + throttleLoops + " packets");
487
488 e = new List<ThrottleOutPacketType>(Empty);
489 Empty.Clear();
490 }
491
492 foreach (ThrottleOutPacketType t in e)
493 {
494 if (GetQueueCount(t) == 0)
495 TriggerOnQueueEmpty(t);
496 }
497 }
498
499 private void TriggerOnQueueEmpty(ThrottleOutPacketType queue)
500 {
501 QueueEmpty handlerQueueEmpty = OnQueueEmpty;
502
503 if (handlerQueueEmpty != null)
504 handlerQueueEmpty(queue);
505 }
506
507 private void ThrottleTimerElapsed(object sender, ElapsedEventArgs e)
508 {
509 // just to change the signature, and that ProcessThrottle
510 // will be used elsewhere possibly
511 ProcessThrottle();
512 }
513
514 private void ThrottleCheck(ref LLPacketThrottle throttle, ref Queue<LLQueItem> q, LLQueItem item, ThrottleOutPacketType itemType)
515 {
516 // The idea.. is if the packet throttle queues are empty
517 // and the client is under throttle for the type. Queue
518 // it up directly. This basically short cuts having to
519 // wait for the timer to fire to put things into the
520 // output queue
521
522 if ((q.Count == 0) && (throttle.UnderLimit()))
523 {
524 try
525 {
526 Monitor.Enter(this);
527 throttle.AddBytes(item.Length);
528 TotalThrottle.AddBytes(item.Length);
529 SendQueue.Enqueue(item);
530 lock (this)
531 {
532 if (!Empty.Contains(itemType))
533 Empty.Add(itemType);
534 }
535 }
536 catch (Exception e)
537 {
538 // Probably a serialization exception
539 m_log.WarnFormat("ThrottleCheck: {0}", e.ToString());
540 }
541 finally
542 {
543 Monitor.Pulse(this);
544 Monitor.Exit(this);
545 }
546 }
547 else
548 {
549 q.Enqueue(item);
550 }
551 }
552
553 private static int ScaleThrottle(int value, int curmax, int newmax)
554 {
555 return (int)((value / (float)curmax) * newmax);
556 }
557
558 public byte[] GetThrottlesPacked(float multiplier)
559 {
560 int singlefloat = 4;
561 float tResend = ResendThrottle.Throttle*multiplier;
562 float tLand = LandThrottle.Throttle*multiplier;
563 float tWind = WindThrottle.Throttle*multiplier;
564 float tCloud = CloudThrottle.Throttle*multiplier;
565 float tTask = TaskThrottle.Throttle*multiplier;
566 float tTexture = TextureThrottle.Throttle*multiplier;
567 float tAsset = AssetThrottle.Throttle*multiplier;
568
569 byte[] throttles = new byte[singlefloat*7];
570 int i = 0;
571 Buffer.BlockCopy(BitConverter.GetBytes(tResend), 0, throttles, singlefloat*i, singlefloat);
572 i++;
573 Buffer.BlockCopy(BitConverter.GetBytes(tLand), 0, throttles, singlefloat*i, singlefloat);
574 i++;
575 Buffer.BlockCopy(BitConverter.GetBytes(tWind), 0, throttles, singlefloat*i, singlefloat);
576 i++;
577 Buffer.BlockCopy(BitConverter.GetBytes(tCloud), 0, throttles, singlefloat*i, singlefloat);
578 i++;
579 Buffer.BlockCopy(BitConverter.GetBytes(tTask), 0, throttles, singlefloat*i, singlefloat);
580 i++;
581 Buffer.BlockCopy(BitConverter.GetBytes(tTexture), 0, throttles, singlefloat*i, singlefloat);
582 i++;
583 Buffer.BlockCopy(BitConverter.GetBytes(tAsset), 0, throttles, singlefloat*i, singlefloat);
584
585 return throttles;
586 }
587
588 public void SetThrottleFromClient(byte[] throttle)
589 {
590 // From mantis http://opensimulator.org/mantis/view.php?id=1374
591 // it appears that sometimes we are receiving empty throttle byte arrays.
592 // TODO: Investigate this behaviour
593 if (throttle.Length == 0)
594 {
595 m_log.Warn("[PACKET QUEUE]: SetThrottleFromClient unexpectedly received a throttle byte array containing no elements!");
596 return;
597 }
598
599 int tResend = -1;
600 int tLand = -1;
601 int tWind = -1;
602 int tCloud = -1;
603 int tTask = -1;
604 int tTexture = -1;
605 int tAsset = -1;
606 int tall = -1;
607 int singlefloat = 4;
608
609 //Agent Throttle Block contains 7 single floatingpoint values.
610 int j = 0;
611
612 // Some Systems may be big endian...
613 // it might be smart to do this check more often...
614 if (!BitConverter.IsLittleEndian)
615 for (int i = 0; i < 7; i++)
616 Array.Reverse(throttle, j + i*singlefloat, singlefloat);
617
618 // values gotten from OpenMetaverse.org/wiki/Throttle. Thanks MW_
619 // bytes
620 // Convert to integer, since.. the full fp space isn't used.
621 tResend = (int) BitConverter.ToSingle(throttle, j);
622 j += singlefloat;
623 tLand = (int) BitConverter.ToSingle(throttle, j);
624 j += singlefloat;
625 tWind = (int) BitConverter.ToSingle(throttle, j);
626 j += singlefloat;
627 tCloud = (int) BitConverter.ToSingle(throttle, j);
628 j += singlefloat;
629 tTask = (int) BitConverter.ToSingle(throttle, j);
630 j += singlefloat;
631 tTexture = (int) BitConverter.ToSingle(throttle, j);
632 j += singlefloat;
633 tAsset = (int) BitConverter.ToSingle(throttle, j);
634
635 tall = tResend + tLand + tWind + tCloud + tTask + tTexture + tAsset;
636
637 /*
638 m_log.Info("[CLIENT]: Client AgentThrottle - Got throttle:resendbits=" + tResend +
639 " landbits=" + tLand +
640 " windbits=" + tWind +
641 " cloudbits=" + tCloud +
642 " taskbits=" + tTask +
643 " texturebits=" + tTexture +
644 " Assetbits=" + tAsset +
645 " Allbits=" + tall);
646 */
647
648
649 // Total Sanity
650 // Make sure that the client sent sane total values.
651
652 // If the client didn't send acceptable values....
653 // Scale the clients values down until they are acceptable.
654
655 if (tall <= TotalThrottle.Max)
656 {
657 ResendThrottle.Throttle = tResend;
658 LandThrottle.Throttle = tLand;
659 WindThrottle.Throttle = tWind;
660 CloudThrottle.Throttle = tCloud;
661 TaskThrottle.Throttle = tTask;
662 TextureThrottle.Throttle = tTexture;
663 AssetThrottle.Throttle = tAsset;
664 TotalThrottle.Throttle = tall;
665 }
666// else if (tall < 1)
667// {
668// // client is stupid, penalize him by minning everything
669// ResendThrottle.Throttle = ResendThrottle.Min;
670// LandThrottle.Throttle = LandThrottle.Min;
671// WindThrottle.Throttle = WindThrottle.Min;
672// CloudThrottle.Throttle = CloudThrottle.Min;
673// TaskThrottle.Throttle = TaskThrottle.Min;
674// TextureThrottle.Throttle = TextureThrottle.Min;
675// AssetThrottle.Throttle = AssetThrottle.Min;
676// TotalThrottle.Throttle = TotalThrottle.Min;
677// }
678 else
679 {
680 // we're over so figure out percentages and use those
681 ResendThrottle.Throttle = tResend;
682
683 LandThrottle.Throttle = ScaleThrottle(tLand, tall, TotalThrottle.Max);
684 WindThrottle.Throttle = ScaleThrottle(tWind, tall, TotalThrottle.Max);
685 CloudThrottle.Throttle = ScaleThrottle(tCloud, tall, TotalThrottle.Max);
686 TaskThrottle.Throttle = ScaleThrottle(tTask, tall, TotalThrottle.Max);
687 TextureThrottle.Throttle = ScaleThrottle(tTexture, tall, TotalThrottle.Max);
688 AssetThrottle.Throttle = ScaleThrottle(tAsset, tall, TotalThrottle.Max);
689 TotalThrottle.Throttle = TotalThrottle.Max;
690 }
691 // effectively wiggling the slider causes things reset
692// ResetCounters(); // DO NOT reset, better to send less for one period than more
693 }
694
695 // See IPullStatsProvider
696 public string GetStats()
697 {
698 return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}",
699 SendQueue.Count(),
700 IncomingPacketQueue.Count,
701 OutgoingPacketQueue.Count,
702 ResendOutgoingPacketQueue.Count,
703 LandOutgoingPacketQueue.Count,
704 WindOutgoingPacketQueue.Count,
705 CloudOutgoingPacketQueue.Count,
706 TaskOutgoingPacketQueue.Count,
707 TextureOutgoingPacketQueue.Count,
708 AssetOutgoingPacketQueue.Count);
709 }
710
711 public LLQueItem[] GetQueueArray()
712 {
713 return SendQueue.GetQueueArray();
714 }
715
716 public float ThrottleMultiplier
717 {
718 get { return throttleMultiplier; }
719 }
720
721 public int GetQueueCount(ThrottleOutPacketType queue)
722 {
723 switch (queue)
724 {
725 case ThrottleOutPacketType.Land:
726 return LandOutgoingPacketQueue.Count;
727 case ThrottleOutPacketType.Wind:
728 return WindOutgoingPacketQueue.Count;
729 case ThrottleOutPacketType.Cloud:
730 return CloudOutgoingPacketQueue.Count;
731 case ThrottleOutPacketType.Task:
732 return TaskOutgoingPacketQueue.Count;
733 case ThrottleOutPacketType.Texture:
734 return TextureOutgoingPacketQueue.Count;
735 case ThrottleOutPacketType.Asset:
736 return AssetOutgoingPacketQueue.Count;
737 }
738
739 return 0;
740 }
741 }
742}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketServer.cs
deleted file mode 100644
index 70d94e7..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketServer.cs
+++ /dev/null
@@ -1,206 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System.Net;
29using System.Net.Sockets;
30using OpenMetaverse;
31using OpenMetaverse.Packets;
32using OpenSim.Framework;
33
34namespace OpenSim.Region.ClientStack.LindenUDP
35{
36 /// <summary>
37 /// This class sets up new client stacks. It also handles the immediate distribution of incoming packets to
38 /// client stacks
39 /// </summary>
40 public class LLPacketServer
41 {
42// private static readonly log4net.ILog m_log
43// = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
44
45 protected readonly ILLClientStackNetworkHandler m_networkHandler;
46 protected IScene m_scene;
47
48 /// <summary>
49 /// Tweakable user settings
50 /// </summary>
51 private ClientStackUserSettings m_userSettings;
52
53 public LLPacketServer(ILLClientStackNetworkHandler networkHandler, ClientStackUserSettings userSettings)
54 {
55 m_userSettings = userSettings;
56 m_networkHandler = networkHandler;
57
58 m_networkHandler.RegisterPacketServer(this);
59 }
60
61 public IScene LocalScene
62 {
63 set { m_scene = value; }
64 }
65
66 /// <summary>
67 /// Process an incoming packet.
68 /// </summary>
69 /// <param name="circuitCode"></param>
70 /// <param name="packet"></param>
71 public virtual void InPacket(uint circuitCode, Packet packet)
72 {
73 m_scene.ClientManager.InPacket(circuitCode, packet);
74 }
75
76 /// <summary>
77 /// Create a new client circuit
78 /// </summary>
79 /// <param name="remoteEP"></param>
80 /// <param name="scene"></param>
81 /// <param name="assetCache"></param>
82 /// <param name="packServer"></param>
83 /// <param name="sessionInfo"></param>
84 /// <param name="agentId"></param>
85 /// <param name="sessionId"></param>
86 /// <param name="circuitCode"></param>
87 /// <param name="proxyEP"></param>
88 /// <returns></returns>
89 protected virtual IClientAPI CreateNewCircuit(
90 EndPoint remoteEP, IScene scene,
91 LLPacketServer packServer, AuthenticateResponse sessionInfo,
92 UUID agentId, UUID sessionId, uint circuitCode, EndPoint proxyEP)
93 {
94 return
95 new LLClientView(
96 remoteEP, scene, packServer, sessionInfo, agentId, sessionId, circuitCode, proxyEP,
97 m_userSettings);
98 }
99
100 /// <summary>
101 /// Check whether a given client is authorized to connect.
102 /// </summary>
103 /// <param name="useCircuit"></param>
104 /// <param name="circuitManager"></param>
105 /// <param name="sessionInfo"></param>
106 /// <returns></returns>
107 public virtual bool IsClientAuthorized(
108 UseCircuitCodePacket useCircuit, AgentCircuitManager circuitManager, out AuthenticateResponse sessionInfo)
109 {
110 UUID agentId = useCircuit.CircuitCode.ID;
111 UUID sessionId = useCircuit.CircuitCode.SessionID;
112 uint circuitCode = useCircuit.CircuitCode.Code;
113
114 sessionInfo = circuitManager.AuthenticateSession(sessionId, agentId, circuitCode);
115
116 if (!sessionInfo.Authorised)
117 return false;
118
119 return true;
120 }
121
122 /// <summary>
123 /// Add a new client circuit. We assume that is has already passed an authorization check
124 /// </summary>
125 /// <param name="epSender"></param>
126 /// <param name="useCircuit"></param>
127 /// <param name="assetCache"></param>
128 /// <param name="sessionInfo"></param>
129 /// <param name="proxyEP"></param>
130 /// <returns>
131 /// true if a new circuit was created, false if a circuit with the given circuit code already existed
132 /// </returns>
133 public virtual bool AddNewClient(
134 EndPoint epSender, UseCircuitCodePacket useCircuit,
135 AuthenticateResponse sessionInfo, EndPoint proxyEP)
136 {
137 IClientAPI newuser;
138 uint circuitCode = useCircuit.CircuitCode.Code;
139
140 if (m_scene.ClientManager.TryGetClient(circuitCode, out newuser))
141 {
142 // The circuit is already known to the scene. This not actually a problem since this will currently
143 // occur if a client is crossing borders (hence upgrading its circuit). However, we shouldn't
144 // really by trying to add a new client if this is the case.
145 return false;
146 }
147
148 UUID agentId = useCircuit.CircuitCode.ID;
149 UUID sessionId = useCircuit.CircuitCode.SessionID;
150
151 newuser
152 = CreateNewCircuit(
153 epSender, m_scene, this, sessionInfo, agentId, sessionId, circuitCode, proxyEP);
154
155 m_scene.ClientManager.Add(circuitCode, newuser);
156
157 newuser.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler;
158 newuser.OnLogout += LogoutHandler;
159 newuser.OnConnectionClosed += CloseClient;
160
161 newuser.Start();
162
163 return true;
164 }
165
166 public void LogoutHandler(IClientAPI client)
167 {
168 client.SendLogoutPacket();
169 CloseClient(client);
170 }
171
172 /// <summary>
173 /// Send a packet to the given circuit
174 /// </summary>
175 /// <param name="buffer"></param>
176 /// <param name="size"></param>
177 /// <param name="flags"></param>
178 /// <param name="circuitcode"></param>
179 public virtual void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode)
180 {
181 m_networkHandler.SendPacketTo(buffer, size, flags, circuitcode);
182 }
183
184 /// <summary>
185 /// Close a client circuit only
186 /// </summary>
187 /// <param name="circuitcode"></param>
188 public virtual void CloseCircuit(uint circuitcode)
189 {
190 m_networkHandler.RemoveClientCircuit(circuitcode);
191 }
192
193 /// <summary>
194 /// Completely close down the given client.
195 /// </summary>
196 /// <param name="client"></param>
197 public virtual void CloseClient(IClientAPI client)
198 {
199 //m_log.Info("PacketServer:CloseClient()");
200
201 CloseCircuit(client.CircuitCode);
202 m_scene.ClientManager.Remove(client.CircuitCode);
203 client.Close(false);
204 }
205 }
206}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketThrottle.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketThrottle.cs
deleted file mode 100644
index 52effc5..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketThrottle.cs
+++ /dev/null
@@ -1,128 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28namespace OpenSim.Region.ClientStack.LindenUDP
29{
30 public class LLPacketThrottle
31 {
32 private readonly int m_maxAllowableThrottle;
33 private readonly int m_minAllowableThrottle;
34 private int m_currentThrottle;
35 private const int m_throttleTimeDivisor = 7;
36 private int m_currentBitsSent;
37 private int m_throttleBits;
38
39 /// <value>
40 /// Value with which to multiply all the throttle fields
41 /// </value>
42 private float m_throttleMultiplier;
43
44 public int Max
45 {
46 get { return m_maxAllowableThrottle; }
47 }
48
49 public int Min
50 {
51 get { return m_minAllowableThrottle; }
52 }
53
54 public int Current
55 {
56 get { return m_currentThrottle; }
57 }
58
59 /// <summary>
60 /// Constructor.
61 /// </summary>
62 /// <param name="min"></param>
63 /// <param name="max"></param>
64 /// <param name="throttle"></param>
65 /// <param name="throttleMultiplier">
66 /// A parameter that's ends up multiplying all throttle settings. An alternative solution would have been
67 /// to multiply all the parameters by this before giving them to the constructor. But doing it this way
68 /// represents the fact that the multiplier is a hack that pumps data to clients much faster than the actual
69 /// settings that we are given.
70 /// </param>
71 public LLPacketThrottle(int min, int max, int throttle, float throttleMultiplier)
72 {
73 m_throttleMultiplier = throttleMultiplier;
74 m_maxAllowableThrottle = max;
75 m_minAllowableThrottle = min;
76 m_currentThrottle = throttle;
77 m_currentBitsSent = 0;
78
79 CalcBits();
80 }
81
82 /// <summary>
83 /// Calculate the actual throttle required.
84 /// </summary>
85 private void CalcBits()
86 {
87 m_throttleBits = (int)((float)m_currentThrottle * m_throttleMultiplier / (float)m_throttleTimeDivisor);
88 }
89
90 public void Reset()
91 {
92 m_currentBitsSent = 0;
93 }
94
95 public bool UnderLimit()
96 {
97 return m_currentBitsSent < m_throttleBits;
98 }
99
100 public int AddBytes(int bytes)
101 {
102 m_currentBitsSent += bytes * 8;
103 return m_currentBitsSent;
104 }
105
106 public int Throttle
107 {
108 get { return m_currentThrottle; }
109 set
110 {
111 if (value < m_minAllowableThrottle)
112 {
113 m_currentThrottle = m_minAllowableThrottle;
114 }
115 else if (value > m_maxAllowableThrottle)
116 {
117 m_currentThrottle = m_maxAllowableThrottle;
118 }
119 else
120 {
121 m_currentThrottle = value;
122 }
123
124 CalcBits();
125 }
126 }
127 }
128}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
new file mode 100644
index 0000000..871e8e8
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -0,0 +1,442 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Net;
31using OpenSim.Framework;
32using OpenMetaverse;
33
34namespace OpenSim.Region.ClientStack.LindenUDP
35{
36 #region Delegates
37
38 /// <summary>
39 /// Fired when updated networking stats are produced for this client
40 /// </summary>
41 /// <param name="inPackets">Number of incoming packets received since this
42 /// event was last fired</param>
43 /// <param name="outPackets">Number of outgoing packets sent since this
44 /// event was last fired</param>
45 /// <param name="unAckedBytes">Current total number of bytes in packets we
46 /// are waiting on ACKs for</param>
47 public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes);
48 /// <summary>
49 /// Fired when the queue for a packet category is empty. This event can be
50 /// hooked to put more data on the empty queue
51 /// </summary>
52 /// <param name="category">Category of the packet queue that is empty</param>
53 public delegate void QueueEmpty(ThrottleOutPacketType category);
54
55 #endregion Delegates
56
57 /// <summary>
58 /// Tracks state for a client UDP connection and provides client-specific methods
59 /// </summary>
60 public sealed class LLUDPClient
61 {
62 /// <summary>The number of packet categories to throttle on. If a throttle category is added
63 /// or removed, this number must also change</summary>
64 const int THROTTLE_CATEGORY_COUNT = 7;
65
66 /// <summary>Fired when updated networking stats are produced for this client</summary>
67 public event PacketStats OnPacketStats;
68 /// <summary>Fired when the queue for a packet category is empty. This event can be
69 /// hooked to put more data on the empty queue</summary>
70 public event QueueEmpty OnQueueEmpty;
71
72 /// <summary>AgentID for this client</summary>
73 public readonly UUID AgentID;
74 /// <summary>The remote address of the connected client</summary>
75 public readonly IPEndPoint RemoteEndPoint;
76 /// <summary>Circuit code that this client is connected on</summary>
77 public readonly uint CircuitCode;
78 /// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary>
79 public readonly IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200);
80 /// <summary>Packets we have sent that need to be ACKed by the client</summary>
81 public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection();
82 /// <summary>ACKs that are queued up, waiting to be sent to the client</summary>
83 public readonly LocklessQueue<uint> PendingAcks = new LocklessQueue<uint>();
84
85 /// <summary>Reference to the IClientAPI for this client</summary>
86 public LLClientView ClientAPI;
87 /// <summary>Current packet sequence number</summary>
88 public int CurrentSequence;
89 /// <summary>Current ping sequence number</summary>
90 public byte CurrentPingSequence;
91 /// <summary>True when this connection is alive, otherwise false</summary>
92 public bool IsConnected = true;
93 /// <summary>True when this connection is paused, otherwise false</summary>
94 public bool IsPaused = true;
95 /// <summary>Environment.TickCount when the last packet was received for this client</summary>
96 public int TickLastPacketReceived;
97
98 /// <summary>Timer granularity. This is set to the measured resolution of Environment.TickCount</summary>
99 public readonly float G;
100 /// <summary>Smoothed round-trip time. A smoothed average of the round-trip time for sending a
101 /// reliable packet to the client and receiving an ACK</summary>
102 public float SRTT;
103 /// <summary>Round-trip time variance. Measures the consistency of round-trip times</summary>
104 public float RTTVAR;
105 /// <summary>Retransmission timeout. Packets that have not been acknowledged in this number of
106 /// milliseconds or longer will be resent</summary>
107 /// <remarks>Calculated from <seealso cref="SRTT"/> and <seealso cref="RTTVAR"/> using the
108 /// guidelines in RFC 2988</remarks>
109 public int RTO;
110 /// <summary>Number of bytes received since the last acknowledgement was sent out. This is used
111 /// to loosely follow the TCP delayed ACK algorithm in RFC 1122 (4.2.3.2)</summary>
112 public int BytesSinceLastACK;
113 /// <summary>Number of packets received from this client</summary>
114 public int PacketsReceived;
115 /// <summary>Number of packets sent to this client</summary>
116 public int PacketsSent;
117 /// <summary>Total byte count of unacked packets sent to this client</summary>
118 public int UnackedBytes;
119
120 /// <summary>Total number of received packets that we have reported to the OnPacketStats event(s)</summary>
121 private int m_packetsReceivedReported;
122 /// <summary>Total number of sent packets that we have reported to the OnPacketStats event(s)</summary>
123 private int m_packetsSentReported;
124
125 /// <summary>Throttle bucket for this agent's connection</summary>
126 private readonly TokenBucket throttle;
127 /// <summary>Throttle buckets for each packet category</summary>
128 private readonly TokenBucket[] throttleCategories;
129 /// <summary>Throttle rate defaults and limits</summary>
130 private readonly ThrottleRates defaultThrottleRates;
131 /// <summary>Outgoing queues for throttled packets</summary>
132 private readonly LocklessQueue<OutgoingPacket>[] packetOutboxes = new LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT];
133 /// <summary>A container that can hold one packet for each outbox, used to store
134 /// dequeued packets that are being held for throttling</summary>
135 private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
136 /// <summary>An optimization to store the length of dequeued packets being held
137 /// for throttling. This avoids expensive calls to Packet.Length</summary>
138 private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT];
139 /// <summary>A reference to the LLUDPServer that is managing this client</summary>
140 private readonly LLUDPServer udpServer;
141
142 /// <summary>
143 /// Default constructor
144 /// </summary>
145 /// <param name="server">Reference to the UDP server this client is connected to</param>
146 /// <param name="rates">Default throttling rates and maximum throttle limits</param>
147 /// <param name="parentThrottle">Parent HTB (hierarchical token bucket)
148 /// that the child throttles will be governed by</param>
149 /// <param name="circuitCode">Circuit code for this connection</param>
150 /// <param name="agentID">AgentID for the connected agent</param>
151 /// <param name="remoteEndPoint">Remote endpoint for this connection</param>
152 public LLUDPClient(LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint)
153 {
154 udpServer = server;
155 AgentID = agentID;
156 RemoteEndPoint = remoteEndPoint;
157 CircuitCode = circuitCode;
158 defaultThrottleRates = rates;
159
160 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
161 packetOutboxes[i] = new LocklessQueue<OutgoingPacket>();
162
163 throttle = new TokenBucket(parentThrottle, 0, 0);
164 throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
165 throttleCategories[(int)ThrottleOutPacketType.Resend] = new TokenBucket(throttle, rates.ResendLimit, rates.Resend);
166 throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land);
167 throttleCategories[(int)ThrottleOutPacketType.Wind] = new TokenBucket(throttle, rates.WindLimit, rates.Wind);
168 throttleCategories[(int)ThrottleOutPacketType.Cloud] = new TokenBucket(throttle, rates.CloudLimit, rates.Cloud);
169 throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit, rates.Task);
170 throttleCategories[(int)ThrottleOutPacketType.Texture] = new TokenBucket(throttle, rates.TextureLimit, rates.Texture);
171 throttleCategories[(int)ThrottleOutPacketType.Asset] = new TokenBucket(throttle, rates.AssetLimit, rates.Asset);
172
173 // Set the granularity variable used for retransmission calculations to
174 // the measured resolution of Environment.TickCount
175 G = server.TickCountResolution;
176
177 // Default the retransmission timeout to three seconds
178 RTO = 3000;
179 }
180
181 /// <summary>
182 /// Shuts down this client connection
183 /// </summary>
184 public void Shutdown()
185 {
186 // TODO: Do we need to invalidate the circuit?
187 IsConnected = false;
188 }
189
190 /// <summary>
191 /// Gets information about this client connection
192 /// </summary>
193 /// <returns>Information about the client connection</returns>
194 public ClientInfo GetClientInfo()
195 {
196 // TODO: This data structure is wrong in so many ways. Locking and copying the entire lists
197 // of pending and needed ACKs for every client every time some method wants information about
198 // this connection is a recipe for poor performance
199 ClientInfo info = new ClientInfo();
200 info.pendingAcks = new Dictionary<uint, uint>();
201 info.needAck = new Dictionary<uint, byte[]>();
202
203 info.resendThrottle = throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
204 info.landThrottle = throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
205 info.windThrottle = throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
206 info.cloudThrottle = throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
207 info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
208 info.assetThrottle = throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
209 info.textureThrottle = throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
210 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle +
211 info.taskThrottle + info.assetThrottle + info.textureThrottle;
212
213 return info;
214 }
215
216 /// <summary>
217 /// Modifies the UDP throttles
218 /// </summary>
219 /// <param name="info">New throttling values</param>
220 public void SetClientInfo(ClientInfo info)
221 {
222 // TODO: Allowing throttles to be manually set from this function seems like a reasonable
223 // idea. On the other hand, letting external code manipulate our ACK accounting is not
224 // going to happen
225 throw new NotImplementedException();
226 }
227
228 public string GetStats()
229 {
230 // TODO: ???
231 return string.Format("{0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}",
232 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
233 }
234
235 public void SendPacketStats()
236 {
237 PacketStats callback = OnPacketStats;
238 if (callback != null)
239 {
240 int newPacketsReceived = PacketsReceived - m_packetsReceivedReported;
241 int newPacketsSent = PacketsSent - m_packetsSentReported;
242
243 callback(newPacketsReceived, newPacketsSent, UnackedBytes);
244
245 m_packetsReceivedReported += newPacketsReceived;
246 m_packetsSentReported += newPacketsSent;
247 }
248 }
249
250 public void SetThrottles(byte[] throttleData)
251 {
252 byte[] adjData;
253 int pos = 0;
254
255 if (!BitConverter.IsLittleEndian)
256 {
257 byte[] newData = new byte[7 * 4];
258 Buffer.BlockCopy(throttleData, 0, newData, 0, 7 * 4);
259
260 for (int i = 0; i < 7; i++)
261 Array.Reverse(newData, i * 4, 4);
262
263 adjData = newData;
264 }
265 else
266 {
267 adjData = throttleData;
268 }
269
270 int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
271 int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
272 int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
273 int cloud = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
274 int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
275 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
276 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
277
278 resend = (resend <= defaultThrottleRates.ResendLimit) ? resend : defaultThrottleRates.ResendLimit;
279 land = (land <= defaultThrottleRates.LandLimit) ? land : defaultThrottleRates.LandLimit;
280 wind = (wind <= defaultThrottleRates.WindLimit) ? wind : defaultThrottleRates.WindLimit;
281 cloud = (cloud <= defaultThrottleRates.CloudLimit) ? cloud : defaultThrottleRates.CloudLimit;
282 task = (task <= defaultThrottleRates.TaskLimit) ? task : defaultThrottleRates.TaskLimit;
283 texture = (texture <= defaultThrottleRates.TextureLimit) ? texture : defaultThrottleRates.TextureLimit;
284 asset = (asset <= defaultThrottleRates.AssetLimit) ? asset : defaultThrottleRates.AssetLimit;
285
286 SetThrottle(ThrottleOutPacketType.Resend, resend);
287 SetThrottle(ThrottleOutPacketType.Land, land);
288 SetThrottle(ThrottleOutPacketType.Wind, wind);
289 SetThrottle(ThrottleOutPacketType.Cloud, cloud);
290 SetThrottle(ThrottleOutPacketType.Task, task);
291 SetThrottle(ThrottleOutPacketType.Texture, texture);
292 SetThrottle(ThrottleOutPacketType.Asset, asset);
293 }
294
295 public byte[] GetThrottlesPacked()
296 {
297 byte[] data = new byte[7 * 4];
298 int i = 0;
299
300 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4;
301 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4;
302 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4;
303 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4;
304 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Task].DripRate), 0, data, i, 4); i += 4;
305 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4;
306 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4;
307
308 return data;
309 }
310
311 public void SetThrottle(ThrottleOutPacketType category, int rate)
312 {
313 int i = (int)category;
314 if (i >= 0 && i < throttleCategories.Length)
315 {
316 TokenBucket bucket = throttleCategories[(int)category];
317 bucket.MaxBurst = rate;
318 bucket.DripRate = rate;
319 }
320 }
321
322 public bool EnqueueOutgoing(OutgoingPacket packet)
323 {
324 int category = (int)packet.Category;
325
326 if (category >= 0 && category < packetOutboxes.Length)
327 {
328 LocklessQueue<OutgoingPacket> queue = packetOutboxes[category];
329 TokenBucket bucket = throttleCategories[category];
330
331 if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength))
332 {
333 // Enough tokens were removed from the bucket, the packet will not be queued
334 return false;
335 }
336 else
337 {
338 // Not enough tokens in the bucket, queue this packet
339 queue.Enqueue(packet);
340 return true;
341 }
342 }
343 else
344 {
345 // We don't have a token bucket for this category, so it will not be queued
346 return false;
347 }
348 }
349
350 /// <summary>
351 /// Loops through all of the packet queues for this client and tries to send
352 /// any outgoing packets, obeying the throttling bucket limits
353 /// </summary>
354 /// <remarks>This function is only called from a synchronous loop in the
355 /// UDPServer so we don't need to bother making this thread safe</remarks>
356 /// <returns>True if any packets were sent, otherwise false</returns>
357 public bool DequeueOutgoing()
358 {
359 OutgoingPacket packet;
360 LocklessQueue<OutgoingPacket> queue;
361 TokenBucket bucket;
362 bool packetSent = false;
363
364 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
365 {
366 bucket = throttleCategories[i];
367
368 if (nextPackets[i] != null)
369 {
370 // This bucket was empty the last time we tried to send a packet,
371 // leaving a dequeued packet still waiting to be sent out. Try to
372 // send it again
373 if (bucket.RemoveTokens(nextPacketLengths[i]))
374 {
375 // Send the packet
376 udpServer.SendPacketFinal(nextPackets[i]);
377 nextPackets[i] = null;
378 packetSent = true;
379 }
380 }
381 else
382 {
383 // No dequeued packet waiting to be sent, try to pull one off
384 // this queue
385 queue = packetOutboxes[i];
386 if (queue.Dequeue(out packet))
387 {
388 // A packet was pulled off the queue. See if we have
389 // enough tokens in the bucket to send it out
390 if (bucket.RemoveTokens(packet.Buffer.DataLength))
391 {
392 // Send the packet
393 udpServer.SendPacketFinal(packet);
394 packetSent = true;
395 }
396 else
397 {
398 // Save the dequeued packet and the length calculation for
399 // the next iteration
400 nextPackets[i] = packet;
401 nextPacketLengths[i] = packet.Buffer.DataLength;
402 }
403 }
404 else
405 {
406 // No packets in this queue. Fire the queue empty callback
407 QueueEmpty callback = OnQueueEmpty;
408 if (callback != null)
409 callback((ThrottleOutPacketType)i);
410 }
411 }
412 }
413
414 return packetSent;
415 }
416
417 public void UpdateRoundTrip(float r)
418 {
419 const float ALPHA = 0.125f;
420 const float BETA = 0.25f;
421 const float K = 4.0f;
422
423 if (RTTVAR == 0.0f)
424 {
425 // First RTT measurement
426 SRTT = r;
427 RTTVAR = r * 0.5f;
428 }
429 else
430 {
431 // Subsequence RTT measurement
432 RTTVAR = (1.0f - BETA) * RTTVAR + BETA * Math.Abs(SRTT - r);
433 SRTT = (1.0f - ALPHA) * SRTT + ALPHA * r;
434 }
435
436 // Always round retransmission timeout up to two seconds
437 RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR)));
438 //Logger.Debug("Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " +
439 // RTTVAR + " based on new RTT of " + r + "ms");
440 }
441 }
442}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs
new file mode 100644
index 0000000..7d2da68
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs
@@ -0,0 +1,185 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Net;
31using OpenSim.Framework;
32using OpenMetaverse;
33
34using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim;
35
36namespace OpenSim.Region.ClientStack.LindenUDP
37{
38 public sealed class UDPClientCollection
39 {
40 Dictionary<UUID, LLUDPClient> Dictionary1;
41 Dictionary<IPEndPoint, LLUDPClient> Dictionary2;
42 LLUDPClient[] Array;
43 ReaderWriterLockImpl rwLock = new ReaderWriterLockImpl();
44
45 public UDPClientCollection()
46 {
47 Dictionary1 = new Dictionary<UUID, LLUDPClient>();
48 Dictionary2 = new Dictionary<IPEndPoint, LLUDPClient>();
49 Array = new LLUDPClient[0];
50 }
51
52 public UDPClientCollection(int capacity)
53 {
54 Dictionary1 = new Dictionary<UUID, LLUDPClient>(capacity);
55 Dictionary2 = new Dictionary<IPEndPoint, LLUDPClient>(capacity);
56 Array = new LLUDPClient[0];
57 }
58
59 public void Add(UUID key1, IPEndPoint key2, LLUDPClient value)
60 {
61 rwLock.EnterWriteLock();
62
63 try
64 {
65 if (Dictionary1.ContainsKey(key1))
66 {
67 if (!Dictionary2.ContainsKey(key2))
68 throw new ArgumentException("key1 exists in the dictionary but not key2");
69 }
70 else if (Dictionary2.ContainsKey(key2))
71 {
72 if (!Dictionary1.ContainsKey(key1))
73 throw new ArgumentException("key2 exists in the dictionary but not key1");
74 }
75
76 Dictionary1[key1] = value;
77 Dictionary2[key2] = value;
78
79 LLUDPClient[] oldArray = Array;
80 int oldLength = oldArray.Length;
81
82 LLUDPClient[] newArray = new LLUDPClient[oldLength + 1];
83 for (int i = 0; i < oldLength; i++)
84 newArray[i] = oldArray[i];
85 newArray[oldLength] = value;
86
87 Array = newArray;
88 }
89 finally { rwLock.ExitWriteLock(); }
90 }
91
92 public bool Remove(UUID key1, IPEndPoint key2)
93 {
94 rwLock.EnterWriteLock();
95
96 try
97 {
98 LLUDPClient value;
99 if (Dictionary1.TryGetValue(key1, out value))
100 {
101 Dictionary1.Remove(key1);
102 Dictionary2.Remove(key2);
103
104 LLUDPClient[] oldArray = Array;
105 int oldLength = oldArray.Length;
106
107 LLUDPClient[] newArray = new LLUDPClient[oldLength - 1];
108 int j = 0;
109 for (int i = 0; i < oldLength; i++)
110 {
111 if (oldArray[i] != value)
112 newArray[j++] = oldArray[i];
113 }
114
115 Array = newArray;
116 return true;
117 }
118 }
119 finally { rwLock.ExitWriteLock(); }
120
121 return false;
122 }
123
124 public void Clear()
125 {
126 rwLock.EnterWriteLock();
127
128 try
129 {
130 Dictionary1.Clear();
131 Dictionary2.Clear();
132 Array = new LLUDPClient[0];
133 }
134 finally { rwLock.ExitWriteLock(); }
135 }
136
137 public int Count
138 {
139 get { return Array.Length; }
140 }
141
142 public bool ContainsKey(UUID key)
143 {
144 return Dictionary1.ContainsKey(key);
145 }
146
147 public bool ContainsKey(IPEndPoint key)
148 {
149 return Dictionary2.ContainsKey(key);
150 }
151
152 public bool TryGetValue(UUID key, out LLUDPClient value)
153 {
154 bool success;
155 bool doLock = !rwLock.IsUpgradeableReadLockHeld;
156 if (doLock) rwLock.EnterReadLock();
157
158 try { success = Dictionary1.TryGetValue(key, out value); }
159 finally { if (doLock) rwLock.ExitReadLock(); }
160
161 return success;
162 }
163
164 public bool TryGetValue(IPEndPoint key, out LLUDPClient value)
165 {
166 bool success;
167 bool doLock = !rwLock.IsUpgradeableReadLockHeld;
168 if (doLock) rwLock.EnterReadLock();
169
170 try { success = Dictionary2.TryGetValue(key, out value); }
171 finally { if (doLock) rwLock.ExitReadLock(); }
172
173 return success;
174 }
175
176 public void ForEach(Action<LLUDPClient> action)
177 {
178 bool doLock = !rwLock.IsUpgradeableReadLockHeld;
179 if (doLock) rwLock.EnterUpgradeableReadLock();
180
181 try { Parallel.ForEach<LLUDPClient>(Array, action); }
182 finally { if (doLock) rwLock.ExitUpgradeableReadLock(); }
183 }
184 }
185}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index c779b08..2c5ad85 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
@@ -26,616 +26,779 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections;
30using System.Collections.Generic; 29using System.Collections.Generic;
31using System.Net; 30using System.Net;
32using System.Net.Sockets; 31using System.Net.Sockets;
33using System.Reflection; 32using System.Reflection;
33using System.Threading;
34using log4net; 34using log4net;
35using Nini.Config; 35using Nini.Config;
36using OpenMetaverse.Packets; 36using OpenMetaverse.Packets;
37using OpenSim.Framework; 37using OpenSim.Framework;
38using OpenSim.Framework.Statistics;
38using OpenSim.Region.Framework.Scenes; 39using OpenSim.Region.Framework.Scenes;
40using OpenMetaverse;
39 41
40namespace OpenSim.Region.ClientStack.LindenUDP 42namespace OpenSim.Region.ClientStack.LindenUDP
41{ 43{
42 /// <summary> 44 /// <summary>
43 /// This class handles the initial UDP circuit setup with a client and passes on subsequent packets to the LLPacketServer 45 /// A shim around LLUDPServer that implements the IClientNetworkServer interface
44 /// </summary> 46 /// </summary>
45 public class LLUDPServer : ILLClientStackNetworkHandler, IClientNetworkServer 47 public sealed class LLUDPServerShim : IClientNetworkServer
46 { 48 {
47 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 49 LLUDPServer m_udpServer;
48
49 /// <value>
50 /// The client circuits established with this UDP server. If a client exists here we can also assume that
51 /// it is populated in clientCircuits_reverse and proxyCircuits (if relevant)
52 /// </value>
53 protected Dictionary<EndPoint, uint> clientCircuits = new Dictionary<EndPoint, uint>();
54 public Hashtable clientCircuits_reverse = Hashtable.Synchronized(new Hashtable());
55 protected Dictionary<uint, EndPoint> proxyCircuits = new Dictionary<uint, EndPoint>();
56
57 private Socket m_socket;
58 protected IPEndPoint ServerIncoming;
59 protected byte[] RecvBuffer = new byte[4096];
60 protected byte[] ZeroBuffer = new byte[8192];
61 50
62 /// <value> 51 public LLUDPServerShim()
63 /// This is an endpoint that is reused where we don't need to protect the information from potentially 52 {
64 /// being stomped on by other threads. 53 }
65 /// </value>
66 protected EndPoint reusedEpSender = new IPEndPoint(IPAddress.Any, 0);
67
68 protected int proxyPortOffset;
69
70 protected AsyncCallback ReceivedData;
71 protected LLPacketServer m_packetServer;
72 protected Location m_location;
73 54
74 protected uint listenPort; 55 public void Initialise(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
75 protected bool Allow_Alternate_Port; 56 {
76 protected IPAddress listenIP = IPAddress.Parse("0.0.0.0"); 57 m_udpServer = new LLUDPServer(listenIP, ref port, proxyPortOffsetParm, allow_alternate_port, configSource, circuitManager);
77 protected IScene m_localScene; 58 }
78 protected int m_clientSocketReceiveBuffer = 0;
79 59
80 /// <value> 60 public void NetworkStop()
81 /// Manages authentication for agent circuits 61 {
82 /// </value> 62 m_udpServer.Stop();
83 protected AgentCircuitManager m_circuitManager; 63 }
84 64
85 public IScene LocalScene 65 public void AddScene(IScene scene)
86 { 66 {
87 set 67 m_udpServer.AddScene(scene);
88 { 68 }
89 m_localScene = value;
90 m_packetServer.LocalScene = m_localScene;
91 69
92 m_location = new Location(m_localScene.RegionInfo.RegionHandle); 70 public bool HandlesRegion(Location x)
93 } 71 {
72 return m_udpServer.HandlesRegion(x);
94 } 73 }
95 74
96 public ulong RegionHandle 75 public void Start()
97 { 76 {
98 get { return m_location.RegionHandle; } 77 m_udpServer.Start();
99 } 78 }
100 79
101 Socket IClientNetworkServer.Server 80 public void Stop()
102 { 81 {
103 get { return m_socket; } 82 m_udpServer.Stop();
104 } 83 }
84 }
105 85
106 public bool HandlesRegion(Location x) 86 /// <summary>
87 /// The LLUDP server for a region. This handles incoming and outgoing
88 /// packets for all UDP connections to the region
89 /// </summary>
90 public class LLUDPServer : UDPBase
91 {
92 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
93
94 /// <summary>Handlers for incoming packets</summary>
95 //PacketEventDictionary packetEvents = new PacketEventDictionary();
96 /// <summary>Incoming packets that are awaiting handling</summary>
97 private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
98 /// <summary></summary>
99 private UDPClientCollection clients = new UDPClientCollection();
100 /// <summary>Bandwidth throttle for this UDP server</summary>
101 private TokenBucket m_throttle;
102 /// <summary>Bandwidth throttle rates for this UDP server</summary>
103 private ThrottleRates m_throttleRates;
104 /// <summary>Manages authentication for agent circuits</summary>
105 private AgentCircuitManager m_circuitManager;
106 /// <summary>Reference to the scene this UDP server is attached to</summary>
107 private IScene m_scene;
108 /// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary>
109 private Location m_location;
110 /// <summary>The measured resolution of Environment.TickCount</summary>
111 private float m_tickCountResolution;
112
113 /// <summary>The measured resolution of Environment.TickCount</summary>
114 public float TickCountResolution { get { return m_tickCountResolution; } }
115 public Socket Server { get { return null; } }
116
117 public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
118 : base((int)port)
107 { 119 {
108 //return x.RegionHandle == m_location.RegionHandle; 120 #region Environment.TickCount Measurement
109 return x == m_location; 121
122 // Measure the resolution of Environment.TickCount
123 m_tickCountResolution = 0f;
124 for (int i = 0; i < 5; i++)
125 {
126 int start = Environment.TickCount;
127 int now = start;
128 while (now == start)
129 now = Environment.TickCount;
130 m_tickCountResolution += (float)(now - start) * 0.2f;
131 }
132 m_log.Info("[LLUDPSERVER]: Average Environment.TickCount resolution: " + TickCountResolution + "ms");
133
134 #endregion Environment.TickCount Measurement
135
136 m_circuitManager = circuitManager;
137
138 // TODO: Config support for throttling the entire connection
139 m_throttle = new TokenBucket(null, 0, 0);
140 m_throttleRates = new ThrottleRates(configSource);
110 } 141 }
111 142
112 public void AddScene(IScene x) 143 public new void Start()
113 { 144 {
114 LocalScene = x; 145 if (m_scene == null)
146 throw new InvalidOperationException("Cannot LLUDPServer.Start() without an IScene reference");
147
148 base.Start();
149
150 // Start the incoming packet processing thread
151 Thread incomingThread = new Thread(IncomingPacketHandler);
152 incomingThread.Name = "Incoming Packets (" + m_scene.RegionInfo.RegionName + ")";
153 incomingThread.Start();
154
155 Thread outgoingThread = new Thread(OutgoingPacketHandler);
156 outgoingThread.Name = "Outgoing Packets (" + m_scene.RegionInfo.RegionName + ")";
157 outgoingThread.Start();
115 } 158 }
116 159
117 public void Start() 160 public new void Stop()
118 { 161 {
119 ServerListener(); 162 m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
163 base.Stop();
120 } 164 }
121 165
122 public void Stop() 166 public void AddScene(IScene scene)
123 { 167 {
124 m_socket.Close(); 168 if (m_scene == null)
169 {
170 m_scene = scene;
171 m_location = new Location(m_scene.RegionInfo.RegionHandle);
172 }
173 else
174 {
175 m_log.Error("[LLUDPSERVER]: AddScene() called on an LLUDPServer that already has a scene");
176 }
125 } 177 }
126 178
127 public LLUDPServer() 179 public bool HandlesRegion(Location x)
128 { 180 {
181 return x == m_location;
129 } 182 }
130 183
131 public LLUDPServer( 184 public void RemoveClient(IClientAPI client)
132 IPAddress _listenIP, ref uint port, int proxyPortOffset, bool allow_alternate_port, IConfigSource configSource,
133 AgentCircuitManager authenticateClass)
134 { 185 {
135 Initialise(_listenIP, ref port, proxyPortOffset, allow_alternate_port, configSource, authenticateClass); 186 m_scene.ClientManager.Remove(client.CircuitCode);
187 client.Close(false);
188
189 LLUDPClient udpClient;
190 if (clients.TryGetValue(client.AgentId, out udpClient))
191 {
192 m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + client.Name);
193 udpClient.Shutdown();
194 clients.Remove(client.AgentId, udpClient.RemoteEndPoint);
195 }
196 else
197 {
198 m_log.Warn("[LLUDPSERVER]: Failed to remove LLUDPClient for " + client.Name);
199 }
136 } 200 }
137 201
138 /// <summary> 202 public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting)
139 /// Initialize the server
140 /// </summary>
141 /// <param name="_listenIP"></param>
142 /// <param name="port"></param>
143 /// <param name="proxyPortOffsetParm"></param>
144 /// <param name="allow_alternate_port"></param>
145 /// <param name="configSource"></param>
146 /// <param name="assetCache"></param>
147 /// <param name="circuitManager"></param>
148 public void Initialise(
149 IPAddress _listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource,
150 AgentCircuitManager circuitManager)
151 { 203 {
152 ClientStackUserSettings userSettings = new ClientStackUserSettings(); 204 // CoarseLocationUpdate packets cannot be split in an automated way
153 205 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
154 IConfig config = configSource.Configs["ClientStack.LindenUDP"]; 206 allowSplitting = false;
155 207
156 if (config != null) 208 if (allowSplitting && packet.HasVariableBlocks)
157 { 209 {
158 if (config.Contains("client_throttle_max_bps")) 210 byte[][] datas = packet.ToBytesMultiple();
159 { 211 int packetCount = datas.Length;
160 int maxBPS = config.GetInt("client_throttle_max_bps", 1500000);
161 userSettings.TotalThrottleSettings = new ThrottleSettings(0, maxBPS,
162 maxBPS > 28000 ? maxBPS : 28000);
163 }
164 212
165 if (config.Contains("client_throttle_multiplier")) 213 //if (packetCount > 1)
166 userSettings.ClientThrottleMultipler = config.GetFloat("client_throttle_multiplier"); 214 // m_log.Debug("[LLUDPSERVER]: Split " + packet.Type + " packet into " + packetCount + " packets");
167 if (config.Contains("client_socket_rcvbuf_size"))
168 m_clientSocketReceiveBuffer = config.GetInt("client_socket_rcvbuf_size");
169 }
170
171 m_log.DebugFormat("[CLIENT]: client_throttle_multiplier = {0}", userSettings.ClientThrottleMultipler);
172 m_log.DebugFormat("[CLIENT]: client_socket_rcvbuf_size = {0}", (m_clientSocketReceiveBuffer != 0 ?
173 m_clientSocketReceiveBuffer.ToString() : "OS default"));
174
175 proxyPortOffset = proxyPortOffsetParm;
176 listenPort = (uint) (port + proxyPortOffsetParm);
177 listenIP = _listenIP;
178 Allow_Alternate_Port = allow_alternate_port;
179 m_circuitManager = circuitManager;
180 CreatePacketServer(userSettings);
181 215
182 // Return new port 216 for (int i = 0; i < packetCount; i++)
183 // This because in Grid mode it is not really important what port the region listens to as long as it is correctly registered. 217 {
184 // So the option allow_alternate_ports="true" was added to default.xml 218 byte[] data = datas[i];
185 port = (uint)(listenPort - proxyPortOffsetParm); 219 clients.ForEach(
220 delegate(LLUDPClient client)
221 { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); });
222 }
223 }
224 else
225 {
226 byte[] data = packet.ToBytes();
227 clients.ForEach(
228 delegate(LLUDPClient client)
229 { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); });
230 }
186 } 231 }
187 232
188 protected virtual void CreatePacketServer(ClientStackUserSettings userSettings) 233 public void SendPacket(UUID agentID, Packet packet, ThrottleOutPacketType category, bool allowSplitting)
189 { 234 {
190 new LLPacketServer(this, userSettings); 235 LLUDPClient client;
236 if (clients.TryGetValue(agentID, out client))
237 SendPacket(client, packet, category, allowSplitting);
238 else
239 m_log.Warn("[LLUDPSERVER]: Attempted to send a packet to unknown agentID " + agentID);
191 } 240 }
192 241
193 /// <summary> 242 public void SendPacket(LLUDPClient client, Packet packet, ThrottleOutPacketType category, bool allowSplitting)
194 /// This method is called every time that we receive new UDP data.
195 /// </summary>
196 /// <param name="result"></param>
197 protected virtual void OnReceivedData(IAsyncResult result)
198 { 243 {
199 Packet packet = null; 244 // CoarseLocationUpdate packets cannot be split in an automated way
200 int numBytes = 1; 245 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
201 EndPoint epSender = new IPEndPoint(IPAddress.Any, 0); 246 allowSplitting = false;
202 EndPoint epProxy = null;
203 247
204 try 248 if (allowSplitting && packet.HasVariableBlocks)
205 { 249 {
206 if (EndReceive(out numBytes, result, ref epSender)) 250 byte[][] datas = packet.ToBytesMultiple();
207 { 251 int packetCount = datas.Length;
208 // Make sure we are getting zeroes when running off the 252
209 // end of grab / degrab packets from old clients 253 //if (packetCount > 1)
210 Array.Clear(RecvBuffer, numBytes, RecvBuffer.Length - numBytes); 254 // m_log.Debug("[LLUDPSERVER]: Split " + packet.Type + " packet into " + packetCount + " packets");
211 255
212 int packetEnd = numBytes - 1; 256 for (int i = 0; i < packetCount; i++)
213 if (proxyPortOffset != 0) packetEnd -= 6;
214
215 try
216 {
217 packet = PacketPool.Instance.GetPacket(RecvBuffer, ref packetEnd, ZeroBuffer);
218 }
219 catch (MalformedDataException e)
220 {
221 m_log.DebugFormat("[CLIENT]: Dropped Malformed Packet due to MalformedDataException: {0}", e.StackTrace);
222 }
223 catch (IndexOutOfRangeException e)
224 {
225 m_log.DebugFormat("[CLIENT]: Dropped Malformed Packet due to IndexOutOfRangeException: {0}", e.StackTrace);
226 }
227 catch (Exception e)
228 {
229 m_log.Debug("[CLIENT]: " + e);
230 }
231 }
232
233
234 if (proxyPortOffset != 0)
235 { 257 {
236 // If we've received a use circuit packet, then we need to decode an endpoint proxy, if one exists, 258 byte[] data = datas[i];
237 // before allowing the RecvBuffer to be overwritten by the next packet. 259 SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category);
238 if (packet != null && packet.Type == PacketType.UseCircuitCode)
239 {
240 epProxy = epSender;
241 }
242
243 // Now decode the message from the proxy server
244 epSender = ProxyCodec.DecodeProxyMessage(RecvBuffer, ref numBytes);
245 } 260 }
246 } 261 }
247 catch (Exception ex) 262 else
248 { 263 {
249 m_log.ErrorFormat("[CLIENT]: Exception thrown during EndReceive(): {0}", ex); 264 byte[] data = packet.ToBytes();
265 SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category);
250 } 266 }
267 }
251 268
252 BeginRobustReceive(); 269 public void SendPacketData(LLUDPClient client, byte[] data, int dataLength, PacketType type, bool doZerocode, ThrottleOutPacketType category)
270 {
271 // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum.
272 // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting
273 // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here
274 // to accomodate for both common scenarios and provide ample room for ACK appending in both
275 int bufferSize = (dataLength > 180) ? Packet.MTU : 200;
276
277 UDPPacketBuffer buffer = new UDPPacketBuffer(client.RemoteEndPoint, bufferSize);
253 278
254 if (packet != null) 279 // Zerocode if needed
280 if (doZerocode)
255 { 281 {
256 if (packet.Type == PacketType.UseCircuitCode) 282 try { dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); }
257 AddNewClient((UseCircuitCodePacket)packet, epSender, epProxy); 283 catch (IndexOutOfRangeException)
258 else 284 {
259 ProcessInPacket(packet, epSender); 285 // The packet grew larger than the bufferSize while zerocoding.
286 // Remove the MSG_ZEROCODED flag and send the unencoded data
287 // instead
288 m_log.Info("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding. Removing MSG_ZEROCODED flag");
289 data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED);
290 Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
291 }
292 }
293 else
294 {
295 Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
260 } 296 }
297 buffer.DataLength = dataLength;
298
299 #region Queue or Send
300
301 // Look up the UDPClient this is going to
302 OutgoingPacket outgoingPacket = new OutgoingPacket(client, buffer, category);
303
304 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket))
305 SendPacketFinal(outgoingPacket);
306
307 #endregion Queue or Send
261 } 308 }
262 309
263 /// <summary> 310 public void SendAcks(LLUDPClient client)
264 /// Process a successfully received packet.
265 /// </summary>
266 /// <param name="packet"></param>
267 /// <param name="epSender"></param>
268 protected virtual void ProcessInPacket(Packet packet, EndPoint epSender)
269 { 311 {
270 try 312 uint ack;
313
314 if (client.PendingAcks.Dequeue(out ack))
271 { 315 {
272 // do we already have a circuit for this endpoint 316 List<PacketAckPacket.PacketsBlock> blocks = new List<PacketAckPacket.PacketsBlock>();
273 uint circuit; 317 PacketAckPacket.PacketsBlock block = new PacketAckPacket.PacketsBlock();
274 bool ret; 318 block.ID = ack;
275 319 blocks.Add(block);
276 lock (clientCircuits) 320
321 while (client.PendingAcks.Dequeue(out ack))
277 { 322 {
278 ret = clientCircuits.TryGetValue(epSender, out circuit); 323 block = new PacketAckPacket.PacketsBlock();
324 block.ID = ack;
325 blocks.Add(block);
279 } 326 }
280 327
281 if (ret) 328 PacketAckPacket packet = new PacketAckPacket();
282 { 329 packet.Header.Reliable = false;
283 //if so then send packet to the packetserver 330 packet.Packets = blocks.ToArray();
284 //m_log.DebugFormat(
285 // "[UDPSERVER]: For circuit {0} {1} got packet {2}", circuit, epSender, packet.Type);
286 331
287 m_packetServer.InPacket(circuit, packet); 332 SendPacket(client, packet, ThrottleOutPacketType.Unknown, true);
288 }
289 }
290 catch (Exception e)
291 {
292 m_log.Error("[CLIENT]: Exception in processing packet - ignoring: ", e);
293 } 333 }
294 } 334 }
295 335
296 /// <summary> 336 public void SendPing(LLUDPClient client)
297 /// Begin an asynchronous receive of the next bit of raw data
298 /// </summary>
299 protected virtual void BeginReceive()
300 { 337 {
301 m_socket.BeginReceiveFrom( 338 IClientAPI api = client.ClientAPI;
302 RecvBuffer, 0, RecvBuffer.Length, SocketFlags.None, ref reusedEpSender, ReceivedData, null); 339 if (api != null)
340 api.SendStartPingCheck(client.CurrentPingSequence++);
303 } 341 }
304 342
305 /// <summary> 343 public void ResendUnacked(LLUDPClient client)
306 /// Begin a robust asynchronous receive of the next bit of raw data. Robust means that SocketExceptions are
307 /// automatically dealt with until the next set of valid UDP data is received.
308 /// </summary>
309 private void BeginRobustReceive()
310 { 344 {
311 bool done = false; 345 if (client.NeedAcks.Count > 0)
312
313 while (!done)
314 { 346 {
315 try 347 List<OutgoingPacket> expiredPackets = client.NeedAcks.GetExpiredPackets(client.RTO);
316 {
317 BeginReceive();
318 done = true;
319 }
320 catch (SocketException e)
321 {
322 // ENDLESS LOOP ON PURPOSE!
323 // Reset connection and get next UDP packet off the buffer
324 // If the UDP packet is part of the same stream, this will happen several hundreds of times before
325 // the next set of UDP data is for a valid client.
326 348
327 try 349 if (expiredPackets != null)
328 { 350 {
329 CloseCircuit(e); 351 // Resend packets
330 } 352 for (int i = 0; i < expiredPackets.Count; i++)
331 catch (Exception e2)
332 { 353 {
333 m_log.ErrorFormat( 354 OutgoingPacket outgoingPacket = expiredPackets[i];
334 "[CLIENT]: Exception thrown when trying to close the circuit for {0} - {1}", reusedEpSender, 355
335 e2); 356 // FIXME: Make this an .ini setting
357 if (outgoingPacket.ResendCount < 3)
358 {
359 //Logger.Debug(String.Format("Resending packet #{0} (attempt {1}), {2}ms have passed",
360 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount));
361
362 // Set the resent flag
363 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
364 outgoingPacket.Category = ThrottleOutPacketType.Resend;
365
366 // The TickCount will be set to the current time when the packet
367 // is actually sent out again
368 outgoingPacket.TickCount = 0;
369
370 // Bump up the resend count on this packet
371 Interlocked.Increment(ref outgoingPacket.ResendCount);
372 //Interlocked.Increment(ref Stats.ResentPackets);
373
374 // Queue or (re)send the packet
375 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket))
376 SendPacketFinal(outgoingPacket);
377 }
378 else
379 {
380 m_log.DebugFormat("[LLUDPSERVER]: Dropping packet #{0} for agent {1} after {2} failed attempts",
381 outgoingPacket.SequenceNumber, outgoingPacket.Client.RemoteEndPoint, outgoingPacket.ResendCount);
382
383 lock (client.NeedAcks.SyncRoot)
384 client.NeedAcks.RemoveUnsafe(outgoingPacket.SequenceNumber);
385
386 //Interlocked.Increment(ref Stats.DroppedPackets);
387
388 // Disconnect an agent if no packets are received for some time
389 //FIXME: Make 60 an .ini setting
390 if (Environment.TickCount - client.TickLastPacketReceived > 1000 * 60)
391 {
392 m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + client.ClientAPI.Name);
393
394 RemoveClient(client.ClientAPI);
395 return;
396 }
397 }
336 } 398 }
337 } 399 }
338 catch (ObjectDisposedException)
339 {
340 m_log.Info(
341 "[UDPSERVER]: UDP Object disposed. No need to worry about this if you're restarting the simulator.");
342
343 done = true;
344 }
345 catch (Exception ex)
346 {
347 m_log.ErrorFormat("[CLIENT]: Exception thrown during BeginReceive(): {0}", ex);
348 }
349 } 400 }
350 } 401 }
351 402
352 /// <summary> 403 public void Flush()
353 /// Close a client circuit. This is done in response to an exception on receive, and should not be called
354 /// normally.
355 /// </summary>
356 /// <param name="e">The exception that caused the close. Can be null if there was no exception</param>
357 private void CloseCircuit(Exception e)
358 { 404 {
359 uint circuit; 405 // FIXME: Implement?
360 lock (clientCircuits) 406 }
407
408 internal void SendPacketFinal(OutgoingPacket outgoingPacket)
409 {
410 UDPPacketBuffer buffer = outgoingPacket.Buffer;
411 byte flags = buffer.Data[0];
412 bool isResend = (flags & Helpers.MSG_RESENT) != 0;
413 bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0;
414 LLUDPClient client = outgoingPacket.Client;
415
416 // Keep track of when this packet was sent out (right now)
417 outgoingPacket.TickCount = Environment.TickCount;
418
419 #region ACK Appending
420
421 int dataLength = buffer.DataLength;
422
423 // Keep appending ACKs until there is no room left in the packet or there are
424 // no more ACKs to append
425 uint ackCount = 0;
426 uint ack;
427 while (dataLength + 5 < buffer.Data.Length && client.PendingAcks.Dequeue(out ack))
428 {
429 Utils.UIntToBytesBig(ack, buffer.Data, dataLength);
430 dataLength += 4;
431 ++ackCount;
432 }
433
434 if (ackCount > 0)
435 {
436 // Set the last byte of the packet equal to the number of appended ACKs
437 buffer.Data[dataLength++] = (byte)ackCount;
438 // Set the appended ACKs flag on this packet
439 buffer.Data[0] = (byte)(buffer.Data[0] | Helpers.MSG_APPENDED_ACKS);
440 }
441
442 buffer.DataLength = dataLength;
443
444 #endregion ACK Appending
445
446 if (!isResend)
361 { 447 {
362 if (clientCircuits.TryGetValue(reusedEpSender, out circuit)) 448 // Not a resend, assign a new sequence number
449 uint sequenceNumber = (uint)Interlocked.Increment(ref client.CurrentSequence);
450 Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1);
451 outgoingPacket.SequenceNumber = sequenceNumber;
452
453 if (isReliable)
363 { 454 {
364 m_packetServer.CloseCircuit(circuit); 455 // Add this packet to the list of ACK responses we are waiting on from the server
365 456 client.NeedAcks.Add(outgoingPacket);
366 if (e != null)
367 m_log.ErrorFormat(
368 "[CLIENT]: Closed circuit {0} {1} due to exception {2}", circuit, reusedEpSender, e);
369 } 457 }
370 } 458 }
459
460 // Stats tracking
461 Interlocked.Increment(ref client.PacketsSent);
462 if (isReliable)
463 Interlocked.Add(ref client.UnackedBytes, outgoingPacket.Buffer.DataLength);
464
465 // Put the UDP payload on the wire
466 AsyncBeginSend(buffer);
371 } 467 }
372 468
373 /// <summary> 469 protected override void PacketReceived(UDPPacketBuffer buffer)
374 /// Finish the process of asynchronously receiving the next bit of raw data
375 /// </summary>
376 /// <param name="numBytes">The number of bytes received. Will return 0 if no bytes were recieved
377 /// <param name="result"></param>
378 /// <param name="epSender">The sender of the data</param>
379 /// <returns></returns>
380 protected virtual bool EndReceive(out int numBytes, IAsyncResult result, ref EndPoint epSender)
381 { 470 {
382 bool hasReceivedOkay = false; 471 // Debugging/Profiling
383 numBytes = 0; 472 //try { Thread.CurrentThread.Name = "PacketReceived (" + scene.RegionName + ")"; }
384 473 //catch (Exception) { }
474
475 LLUDPClient client = null;
476 Packet packet = null;
477 int packetEnd = buffer.DataLength - 1;
478 IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint;
479
480 #region Decoding
481
385 try 482 try
386 { 483 {
387 numBytes = m_socket.EndReceiveFrom(result, ref epSender); 484 packet = Packet.BuildPacket(buffer.Data, ref packetEnd,
388 hasReceivedOkay = true; 485 // Only allocate a buffer for zerodecoding if the packet is zerocoded
486 ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null);
389 } 487 }
390 catch (SocketException e) 488 catch (MalformedDataException)
391 { 489 {
392 // TODO : Actually only handle those states that we have control over, re-throw everything else, 490 m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet:\n{0}",
393 // TODO: implement cases as we encounter them. 491 Utils.BytesToHexString(buffer.Data, buffer.DataLength, null));
394 //m_log.Error("[CLIENT]: Connection Error! - " + e.ToString()); 492 }
395 switch (e.SocketErrorCode) 493
494 // Fail-safe check
495 if (packet == null)
496 {
497 m_log.Warn("[LLUDPSERVER]: Couldn't build a message from the incoming data");
498 return;
499 }
500
501 //Stats.RecvBytes += (ulong)buffer.DataLength;
502 //++Stats.RecvPackets;
503
504 #endregion Decoding
505
506 #region UseCircuitCode Handling
507
508 if (packet.Type == PacketType.UseCircuitCode)
509 {
510 UseCircuitCodePacket useCircuitCode = (UseCircuitCodePacket)packet;
511 IClientAPI newuser;
512 uint circuitCode = useCircuitCode.CircuitCode.Code;
513
514 // Check if the client is already established
515 if (!m_scene.ClientManager.TryGetClient(circuitCode, out newuser))
396 { 516 {
397 case SocketError.AlreadyInProgress: 517 AddNewClient(useCircuitCode, (IPEndPoint)buffer.RemoteEndPoint);
398 return hasReceivedOkay; 518 }
519 }
520
521 // Determine which agent this packet came from
522 if (!clients.TryGetValue(address, out client))
523 {
524 m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address);
525 return;
526 }
527
528 #endregion UseCircuitCode Handling
529
530 // Stats tracking
531 Interlocked.Increment(ref client.PacketsReceived);
399 532
400 case SocketError.NetworkReset: 533 #region ACK Receiving
401 case SocketError.ConnectionReset:
402 case SocketError.OperationAborted:
403 break;
404 534
405 default: 535 int now = Environment.TickCount;
406 throw; 536 client.TickLastPacketReceived = now;
537
538 // Handle appended ACKs
539 if (packet.Header.AppendedAcks && packet.Header.AckList != null)
540 {
541 lock (client.NeedAcks.SyncRoot)
542 {
543 for (int i = 0; i < packet.Header.AckList.Length; i++)
544 AcknowledgePacket(client, packet.Header.AckList[i], now, packet.Header.Resent);
407 } 545 }
408 } 546 }
409 catch (ObjectDisposedException e) 547
548 // Handle PacketAck packets
549 if (packet.Type == PacketType.PacketAck)
550 {
551 PacketAckPacket ackPacket = (PacketAckPacket)packet;
552
553 lock (client.NeedAcks.SyncRoot)
554 {
555 for (int i = 0; i < ackPacket.Packets.Length; i++)
556 AcknowledgePacket(client, ackPacket.Packets[i].ID, now, packet.Header.Resent);
557 }
558 }
559
560 #endregion ACK Receiving
561
562 #region ACK Sending
563
564 if (packet.Header.Reliable)
565 client.PendingAcks.Enqueue((uint)packet.Header.Sequence);
566
567 // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out,
568 // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove
569 // 2*MTU bytes from the value and send ACKs, and finally add the local value back to
570 // client.BytesSinceLastACK. Lockless thread safety
571 int bytesSinceLastACK = Interlocked.Exchange(ref client.BytesSinceLastACK, 0);
572 bytesSinceLastACK += buffer.DataLength;
573 if (bytesSinceLastACK > Packet.MTU * 2)
574 {
575 bytesSinceLastACK -= Packet.MTU * 2;
576 SendAcks(client);
577 }
578 Interlocked.Add(ref client.BytesSinceLastACK, bytesSinceLastACK);
579
580 #endregion ACK Sending
581
582 #region Incoming Packet Accounting
583
584 // Check the archive of received reliable packet IDs to see whether we already received this packet
585 if (packet.Header.Reliable && !client.PacketArchive.TryEnqueue(packet.Header.Sequence))
410 { 586 {
411 m_log.DebugFormat("[CLIENT]: ObjectDisposedException: Object {0} disposed.", e.ObjectName); 587 if (packet.Header.Resent)
412 // Uhh, what object, and why? this needs better handling. 588 m_log.Debug("[LLUDPSERVER]: Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type);
589 else
590 m_log.Warn("[LLUDPSERVER]: Received a duplicate (not marked as resend) of packet #" + packet.Header.Sequence + ", type: " + packet.Type);
591
592 // Avoid firing a callback twice for the same packet
593 return;
594 }
595
596 #endregion Incoming Packet Accounting
597
598 // Don't bother clogging up the queue with PacketAck packets that are already handled here
599 if (packet.Type != PacketType.PacketAck)
600 {
601 // Inbox insertion
602 packetInbox.Enqueue(new IncomingPacket(client, packet));
413 } 603 }
414
415 return hasReceivedOkay;
416 } 604 }
417 605
418 /// <summary> 606 protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent)
419 /// Add a new client circuit. 607 {
420 /// </summary> 608 }
421 /// <param name="packet"></param> 609
422 /// <param name="epSender"></param> 610 private bool IsClientAuthorized(UseCircuitCodePacket useCircuitCode, out AuthenticateResponse sessionInfo)
423 /// <param name="epProxy"></param> 611 {
424 protected virtual void AddNewClient(UseCircuitCodePacket useCircuit, EndPoint epSender, EndPoint epProxy) 612 UUID agentID = useCircuitCode.CircuitCode.ID;
613 UUID sessionID = useCircuitCode.CircuitCode.SessionID;
614 uint circuitCode = useCircuitCode.CircuitCode.Code;
615
616 sessionInfo = m_circuitManager.AuthenticateSession(sessionID, agentID, circuitCode);
617 return sessionInfo.Authorised;
618 }
619
620 private void AddNewClient(UseCircuitCodePacket useCircuitCode, IPEndPoint remoteEndPoint)
425 { 621 {
426 //Slave regions don't accept new clients 622 //Slave regions don't accept new clients
427 if (m_localScene.RegionStatus != RegionStatus.SlaveScene) 623 if (m_scene.RegionStatus != RegionStatus.SlaveScene)
428 { 624 {
429 AuthenticateResponse sessionInfo; 625 AuthenticateResponse sessionInfo;
430 bool isNewCircuit = false; 626 bool isNewCircuit = !clients.ContainsKey(remoteEndPoint);
431 627
432 if (!m_packetServer.IsClientAuthorized(useCircuit, m_circuitManager, out sessionInfo)) 628 if (!IsClientAuthorized(useCircuitCode, out sessionInfo))
433 { 629 {
434 m_log.WarnFormat( 630 m_log.WarnFormat(
435 "[CONNECTION FAILURE]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", 631 "[CONNECTION FAILURE]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}",
436 useCircuit.CircuitCode.ID, useCircuit.CircuitCode.Code, epSender); 632 useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint);
437
438 return; 633 return;
439 } 634 }
440
441 lock (clientCircuits)
442 {
443 if (!clientCircuits.ContainsKey(epSender))
444 {
445 clientCircuits.Add(epSender, useCircuit.CircuitCode.Code);
446 isNewCircuit = true;
447 }
448 }
449 635
450 if (isNewCircuit) 636 if (isNewCircuit)
451 { 637 {
452 // This doesn't need locking as it's synchronized data 638 UUID agentID = useCircuitCode.CircuitCode.ID;
453 clientCircuits_reverse[useCircuit.CircuitCode.Code] = epSender; 639 UUID sessionID = useCircuitCode.CircuitCode.SessionID;
640 uint circuitCode = useCircuitCode.CircuitCode.Code;
454 641
455 lock (proxyCircuits) 642 AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo);
456 {
457 proxyCircuits[useCircuit.CircuitCode.Code] = epProxy;
458 }
459
460 m_packetServer.AddNewClient(epSender, useCircuit, sessionInfo, epProxy);
461
462 //m_log.DebugFormat(
463 // "[CONNECTION SUCCESS]: Incoming client {0} (circuit code {1}) received and authenticated for {2}",
464 // useCircuit.CircuitCode.ID, useCircuit.CircuitCode.Code, m_localScene.RegionInfo.RegionName);
465 } 643 }
466 } 644 }
467
468 // Ack the UseCircuitCode packet
469 PacketAckPacket ack_it = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck);
470 // TODO: don't create new blocks if recycling an old packet
471 ack_it.Packets = new PacketAckPacket.PacketsBlock[1];
472 ack_it.Packets[0] = new PacketAckPacket.PacketsBlock();
473 ack_it.Packets[0].ID = useCircuit.Header.Sequence;
474 // ((useCircuit.Header.Sequence < uint.MaxValue) ? useCircuit.Header.Sequence : 0) is just a failsafe to ensure that we don't overflow.
475 ack_it.Header.Sequence = ((useCircuit.Header.Sequence < uint.MaxValue) ? useCircuit.Header.Sequence : 0) + 1;
476 ack_it.Header.Reliable = false;
477
478 byte[] ackmsg = ack_it.ToBytes();
479
480 // Need some extra space in case we need to add proxy
481 // information to the message later
482 byte[] msg = new byte[4096];
483 Buffer.BlockCopy(ackmsg, 0, msg, 0, ackmsg.Length);
484
485 SendPacketTo(msg, ackmsg.Length, SocketFlags.None, useCircuit.CircuitCode.Code);
486
487 PacketPool.Instance.ReturnPacket(useCircuit);
488 } 645 }
489 646
490 public void ServerListener() 647 private void AddClient(uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo)
491 { 648 {
492 uint newPort = listenPort; 649 // Create the LLUDPClient
493 m_log.Info("[UDPSERVER]: Opening UDP socket on " + listenIP + " " + newPort + "."); 650 LLUDPClient client = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint);
494
495 ServerIncoming = new IPEndPoint(listenIP, (int)newPort);
496 m_socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
497 if (0 != m_clientSocketReceiveBuffer)
498 m_socket.ReceiveBufferSize = m_clientSocketReceiveBuffer;
499 m_socket.Bind(ServerIncoming);
500 // Add flags to the UDP socket to prevent "Socket forcibly closed by host"
501 // uint IOC_IN = 0x80000000;
502 // uint IOC_VENDOR = 0x18000000;
503 // uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
504 // TODO: this apparently works in .NET but not in Mono, need to sort out the right flags here.
505 // m_socket.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
506 651
507 listenPort = newPort; 652 // Create the LLClientView
653 LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, client, sessionInfo, agentID, sessionID, circuitCode);
654 clientApi.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler;
655 clientApi.OnLogout += LogoutHandler;
656 clientApi.OnConnectionClosed += RemoveClient;
508 657
509 m_log.Info("[UDPSERVER]: UDP socket bound, getting ready to listen"); 658 // Start the IClientAPI
659 m_scene.ClientManager.Add(circuitCode, clientApi);
660 clientApi.Start();
510 661
511 ReceivedData = OnReceivedData; 662 // Give LLUDPClient a reference to IClientAPI
512 BeginReceive(); 663 client.ClientAPI = clientApi;
513 664
514 m_log.Info("[UDPSERVER]: Listening on port " + newPort); 665 // Add the new client to our list of tracked clients
666 clients.Add(agentID, client.RemoteEndPoint, client);
515 } 667 }
516 668
517 public virtual void RegisterPacketServer(LLPacketServer server) 669 private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend)
518 { 670 {
519 m_packetServer = server; 671 OutgoingPacket ackedPacket;
672 if (client.NeedAcks.RemoveUnsafe(ack, out ackedPacket) && !fromResend)
673 {
674 // Update stats
675 Interlocked.Add(ref client.UnackedBytes, -ackedPacket.Buffer.DataLength);
676
677 // Calculate the round-trip time for this packet and its ACK
678 int rtt = currentTime - ackedPacket.TickCount;
679 if (rtt > 0)
680 client.UpdateRoundTrip(rtt);
681 }
520 } 682 }
521 683
522 public virtual void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode) 684 private void IncomingPacketHandler()
523 //EndPoint packetSender)
524 { 685 {
525 // find the endpoint for this circuit 686 // Set this culture for the thread that incoming packets are received
526 EndPoint sendto; 687 // on to en-US to avoid number parsing issues
527 try 688 Culture.SetCurrentCulture();
528 {
529 sendto = (EndPoint)clientCircuits_reverse[circuitcode];
530 }
531 catch
532 {
533 // Exceptions here mean there is no circuit
534 m_log.Warn("[CLIENT]: Circuit not found, not sending packet");
535 return;
536 }
537 689
538 if (sendto != null) 690 IncomingPacket incomingPacket = null;
691
692 while (base.IsRunning)
539 { 693 {
540 //we found the endpoint so send the packet to it 694 if (packetInbox.Dequeue(100, ref incomingPacket))
541 if (proxyPortOffset != 0) 695 Util.FireAndForget(ProcessInPacket, incomingPacket);
542 {
543 //MainLog.Instance.Verbose("UDPSERVER", "SendPacketTo proxy " + proxyCircuits[circuitcode].ToString() + ": client " + sendto.ToString());
544 ProxyCodec.EncodeProxyMessage(buffer, ref size, sendto);
545 m_socket.SendTo(buffer, size, flags, proxyCircuits[circuitcode]);
546 }
547 else
548 {
549 //MainLog.Instance.Verbose("UDPSERVER", "SendPacketTo : client " + sendto.ToString());
550 try
551 {
552 m_socket.SendTo(buffer, size, flags, sendto);
553 }
554 catch (SocketException SockE)
555 {
556 m_log.ErrorFormat("[UDPSERVER]: Caught Socket Error in the send buffer!. {0}",SockE.ToString());
557 }
558 }
559 } 696 }
697
698 if (packetInbox.Count > 0)
699 m_log.Warn("[LLUDPSERVER]: IncomingPacketHandler is shutting down, dropping " + packetInbox.Count + " packets");
700 packetInbox.Clear();
560 } 701 }
561 702
562 public virtual void RemoveClientCircuit(uint circuitcode) 703 private void OutgoingPacketHandler()
563 { 704 {
564 EndPoint sendto; 705 // Set this culture for the thread that outgoing packets are sent
565 if (clientCircuits_reverse.Contains(circuitcode)) 706 // on to en-US to avoid number parsing issues
707 Culture.SetCurrentCulture();
708
709 int now = Environment.TickCount;
710 int elapsedMS = 0;
711 int elapsed100MS = 0;
712 int elapsed500MS = 0;
713
714 while (base.IsRunning)
566 { 715 {
567 sendto = (EndPoint)clientCircuits_reverse[circuitcode]; 716 bool resendUnacked = false;
717 bool sendAcks = false;
718 bool sendPings = false;
719 bool packetSent = false;
568 720
569 clientCircuits_reverse.Remove(circuitcode); 721 elapsedMS += Environment.TickCount - now;
570 722
571 lock (clientCircuits) 723 // Check for pending outgoing resends every 100ms
724 if (elapsedMS >= 100)
572 { 725 {
573 if (sendto != null) 726 resendUnacked = true;
574 { 727 elapsedMS -= 100;
575 clientCircuits.Remove(sendto); 728 ++elapsed100MS;
576 }
577 else
578 {
579 m_log.DebugFormat(
580 "[CLIENT]: endpoint for circuit code {0} in RemoveClientCircuit() was unexpectedly null!", circuitcode);
581 }
582 } 729 }
583 lock (proxyCircuits) 730 // Check for pending outgoing ACKs every 500ms
731 if (elapsed100MS >= 5)
584 { 732 {
585 proxyCircuits.Remove(circuitcode); 733 sendAcks = true;
734 elapsed100MS = 0;
735 ++elapsed500MS;
586 } 736 }
737 // Send pings to clients every 5000ms
738 if (elapsed500MS >= 10)
739 {
740 sendPings = true;
741 elapsed500MS = 0;
742 }
743
744 clients.ForEach(
745 delegate(LLUDPClient client)
746 {
747 if (client.DequeueOutgoing())
748 packetSent = true;
749 if (resendUnacked)
750 ResendUnacked(client);
751 if (sendAcks)
752 {
753 SendAcks(client);
754 client.SendPacketStats();
755 }
756 if (sendPings)
757 SendPing(client);
758 }
759 );
760
761 if (!packetSent)
762 Thread.Sleep(20);
587 } 763 }
588 } 764 }
589 765
590 public void RestoreClient(AgentCircuitData circuit, EndPoint userEP, EndPoint proxyEP) 766 private void ProcessInPacket(object state)
591 { 767 {
592 //MainLog.Instance.Verbose("UDPSERVER", "RestoreClient"); 768 IncomingPacket incomingPacket = (IncomingPacket)state;
769 Packet packet = incomingPacket.Packet;
770 LLUDPClient client = incomingPacket.Client;
593 771
594 UseCircuitCodePacket useCircuit = new UseCircuitCodePacket(); 772 // Sanity check
595 useCircuit.CircuitCode.Code = circuit.circuitcode; 773 if (packet == null || client == null || client.ClientAPI == null)
596 useCircuit.CircuitCode.ID = circuit.AgentID;
597 useCircuit.CircuitCode.SessionID = circuit.SessionID;
598
599 AuthenticateResponse sessionInfo;
600
601 if (!m_packetServer.IsClientAuthorized(useCircuit, m_circuitManager, out sessionInfo))
602 { 774 {
603 m_log.WarnFormat( 775 m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", Client=\"{1}\", Client.ClientAPI=\"{2}\"",
604 "[CLIENT]: Restore request denied to avatar {0} connecting with unauthorized circuit code {1}", 776 packet, client, (client != null) ? client.ClientAPI : null);
605 useCircuit.CircuitCode.ID, useCircuit.CircuitCode.Code);
606
607 return;
608 } 777 }
609 778
610 lock (clientCircuits) 779 try
611 { 780 {
612 if (!clientCircuits.ContainsKey(userEP)) 781 // Process this packet
613 clientCircuits.Add(userEP, useCircuit.CircuitCode.Code); 782 client.ClientAPI.ProcessInPacket(packet);
614 else
615 m_log.Error("[CLIENT]: clientCircuits already contains entry for user " + useCircuit.CircuitCode.Code + ". NOT adding.");
616 } 783 }
617 784 catch (ThreadAbortException)
618 // This data structure is synchronized, so we don't need the lock
619 if (!clientCircuits_reverse.ContainsKey(useCircuit.CircuitCode.Code))
620 clientCircuits_reverse.Add(useCircuit.CircuitCode.Code, userEP);
621 else
622 m_log.Error("[CLIENT]: clientCurcuits_reverse already contains entry for user " + useCircuit.CircuitCode.Code + ". NOT adding.");
623
624 lock (proxyCircuits)
625 { 785 {
626 if (!proxyCircuits.ContainsKey(useCircuit.CircuitCode.Code)) 786 // If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down
627 { 787 m_log.Info("[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server");
628 proxyCircuits.Add(useCircuit.CircuitCode.Code, proxyEP); 788 Stop();
629 }
630 else
631 {
632 // re-set proxy endpoint
633 proxyCircuits.Remove(useCircuit.CircuitCode.Code);
634 proxyCircuits.Add(useCircuit.CircuitCode.Code, proxyEP);
635 }
636 } 789 }
790 catch (Exception e)
791 {
792 // Don't let a failure in an individual client thread crash the whole sim.
793 m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", client.AgentID, packet.Type);
794 m_log.Error(e.Message, e);
795 }
796 }
637 797
638 m_packetServer.AddNewClient(userEP, useCircuit, sessionInfo, proxyEP); 798 private void LogoutHandler(IClientAPI client)
799 {
800 client.SendLogoutPacket();
801 RemoveClient(client);
639 } 802 }
640 } 803 }
641} 804}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUtil.cs b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
index c45d11f..1a1a1cb 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUtil.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/OutgoingPacket.cs
@@ -1,4 +1,4 @@
1/* 1/*
2 * Copyright (c) Contributors, http://opensimulator.org/ 2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders. 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 * 4 *
@@ -25,27 +25,46 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using System;
29using OpenSim.Framework;
28using OpenMetaverse; 30using OpenMetaverse;
29 31
30namespace OpenSim.Region.ClientStack.LindenUDP 32namespace OpenSim.Region.ClientStack.LindenUDP
31{ 33{
32 public class LLUtil 34 /// <summary>
35 /// Holds a reference to the <seealso cref="LLUDPClient"/> this packet is
36 /// destined for, along with the serialized packet data, sequence number
37 /// (if this is a resend), number of times this packet has been resent,
38 /// the time of the last resend, and the throttling category for this
39 /// packet
40 /// </summary>
41 public sealed class OutgoingPacket
33 { 42 {
43 /// <summary>Client this packet is destined for</summary>
44 public LLUDPClient Client;
45 /// <summary>Packet data to send</summary>
46 public UDPPacketBuffer Buffer;
47 /// <summary>Sequence number of the wrapped packet</summary>
48 public uint SequenceNumber;
49 /// <summary>Number of times this packet has been resent</summary>
50 public int ResendCount;
51 /// <summary>Environment.TickCount when this packet was last sent over the wire</summary>
52 public int TickCount;
53 /// <summary>Category this packet belongs to</summary>
54 public ThrottleOutPacketType Category;
55
34 /// <summary> 56 /// <summary>
35 /// Convert a string to bytes suitable for use in an LL UDP packet. 57 /// Default constructor
36 /// </summary> 58 /// </summary>
37 /// <param name="s">Truncated to 254 characters if necessary</param> 59 /// <param name="client">Reference to the client this packet is destined for</param>
38 /// <returns></returns> 60 /// <param name="buffer">Serialized packet data. If the flags or sequence number
39 public static byte[] StringToPacketBytes(string s) 61 /// need to be updated, they will be injected directly into this binary buffer</param>
62 /// <param name="category">Throttling category for this packet</param>
63 public OutgoingPacket(LLUDPClient client, UDPPacketBuffer buffer, ThrottleOutPacketType category)
40 { 64 {
41 // Anything more than 254 will cause libsecondlife to barf 65 Client = client;
42 // (libsl 1550) adds an \0 on the Utils.StringToBytes conversion if it isn't present 66 Buffer = buffer;
43 if (s.Length > 254) 67 Category = category;
44 {
45 s = s.Remove(254);
46 }
47
48 return Utils.StringToBytes(s);
49 } 68 }
50 } 69 }
51} 70}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/Tests/PacketHandlerTests.cs b/OpenSim/Region/ClientStack/LindenUDP/Tests/PacketHandlerTests.cs
index cde155b..7d0757f 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/Tests/PacketHandlerTests.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/Tests/PacketHandlerTests.cs
@@ -70,7 +70,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
70 70
71 TestClient testClient = new TestClient(agent, scene); 71 TestClient testClient = new TestClient(agent, scene);
72 72
73 ILLPacketHandler packetHandler 73 LLPacketHandler packetHandler
74 = new LLPacketHandler(testClient, testLLPacketServer, new ClientStackUserSettings()); 74 = new LLPacketHandler(testClient, testLLPacketServer, new ClientStackUserSettings());
75 75
76 packetHandler.InPacket(new AgentAnimationPacket()); 76 packetHandler.InPacket(new AgentAnimationPacket());
diff --git a/OpenSim/Region/ClientStack/LindenUDP/Tests/TestLLPacketServer.cs b/OpenSim/Region/ClientStack/LindenUDP/Tests/TestLLPacketServer.cs
index 1fba847..e995d65 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/Tests/TestLLPacketServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/Tests/TestLLPacketServer.cs
@@ -37,7 +37,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
37 /// </summary> 37 /// </summary>
38 protected Dictionary<PacketType, int> m_packetsReceived = new Dictionary<PacketType, int>(); 38 protected Dictionary<PacketType, int> m_packetsReceived = new Dictionary<PacketType, int>();
39 39
40 public TestLLPacketServer(ILLClientStackNetworkHandler networkHandler, ClientStackUserSettings userSettings) 40 public TestLLPacketServer(LLUDPServer networkHandler, ClientStackUserSettings userSettings)
41 : base(networkHandler, userSettings) 41 : base(networkHandler, userSettings)
42 {} 42 {}
43 43
diff --git a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
new file mode 100644
index 0000000..858a03c
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
@@ -0,0 +1,99 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using Nini.Config;
30
31namespace OpenSim.Region.ClientStack.LindenUDP
32{
33 /// <summary>
34 /// Holds drip rates and maximum burst rates for throttling with hierarchical
35 /// token buckets. The maximum burst rates set here are hard limits and can
36 /// not be overridden by client requests
37 /// </summary>
38 public sealed class ThrottleRates
39 {
40 /// <summary>Drip rate for resent packets</summary>
41 public int Resend;
42 /// <summary>Drip rate for terrain packets</summary>
43 public int Land;
44 /// <summary>Drip rate for wind packets</summary>
45 public int Wind;
46 /// <summary>Drip rate for cloud packets</summary>
47 public int Cloud;
48 /// <summary>Drip rate for task (state and transaction) packets</summary>
49 public int Task;
50 /// <summary>Drip rate for texture packets</summary>
51 public int Texture;
52 /// <summary>Drip rate for asset packets</summary>
53 public int Asset;
54
55 /// <summary>Maximum burst rate for resent packets</summary>
56 public int ResendLimit;
57 /// <summary>Maximum burst rate for land packets</summary>
58 public int LandLimit;
59 /// <summary>Maximum burst rate for wind packets</summary>
60 public int WindLimit;
61 /// <summary>Maximum burst rate for cloud packets</summary>
62 public int CloudLimit;
63 /// <summary>Maximum burst rate for task (state and transaction) packets</summary>
64 public int TaskLimit;
65 /// <summary>Maximum burst rate for texture packets</summary>
66 public int TextureLimit;
67 /// <summary>Maximum burst rate for asset packets</summary>
68 public int AssetLimit;
69
70 /// <summary>
71 /// Default constructor
72 /// </summary>
73 /// <param name="config">Config source to load defaults from</param>
74 public ThrottleRates(IConfigSource config)
75 {
76 try
77 {
78 IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"];
79
80 Resend = throttleConfig.GetInt("ResendDefault", 12500);
81 Land = throttleConfig.GetInt("LandDefault", 500);
82 Wind = throttleConfig.GetInt("WindDefault", 500);
83 Cloud = throttleConfig.GetInt("CloudDefault", 500);
84 Task = throttleConfig.GetInt("TaskDefault", 500);
85 Texture = throttleConfig.GetInt("TextureDefault", 500);
86 Asset = throttleConfig.GetInt("AssetDefault", 500);
87
88 ResendLimit = throttleConfig.GetInt("ResendLimit", 18750);
89 LandLimit = throttleConfig.GetInt("LandLimit", 29750);
90 WindLimit = throttleConfig.GetInt("WindLimit", 18750);
91 CloudLimit = throttleConfig.GetInt("CloudLimit", 18750);
92 TaskLimit = throttleConfig.GetInt("TaskLimit", 55750);
93 TextureLimit = throttleConfig.GetInt("TextureLimit", 55750);
94 AssetLimit = throttleConfig.GetInt("AssetLimit", 27500);
95 }
96 catch (Exception) { }
97 }
98 }
99}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
new file mode 100644
index 0000000..195ca57
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
@@ -0,0 +1,160 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Net;
31using OpenMetaverse;
32
33namespace OpenSim.Region.ClientStack.LindenUDP
34{
35 /// <summary>
36 /// Special collection that is optimized for tracking unacknowledged packets
37 /// </summary>
38 public sealed class UnackedPacketCollection
39 {
40 /// <summary>Synchronization primitive. A lock must be acquired on this
41 /// object before calling any of the unsafe methods</summary>
42 public object SyncRoot = new object();
43
44 /// <summary>Holds the actual unacked packet data, sorted by sequence number</summary>
45 private SortedDictionary<uint, OutgoingPacket> packets = new SortedDictionary<uint, OutgoingPacket>();
46
47 /// <summary>Gets the total number of unacked packets</summary>
48 public int Count { get { return packets.Count; } }
49
50 /// <summary>
51 /// Default constructor
52 /// </summary>
53 public UnackedPacketCollection()
54 {
55 }
56
57 /// <summary>
58 /// Add an unacked packet to the collection
59 /// </summary>
60 /// <param name="packet">Packet that is awaiting acknowledgement</param>
61 /// <returns>True if the packet was successfully added, false if the
62 /// packet already existed in the collection</returns>
63 public bool Add(OutgoingPacket packet)
64 {
65 lock (SyncRoot)
66 {
67 if (!packets.ContainsKey(packet.SequenceNumber))
68 {
69 packets.Add(packet.SequenceNumber, packet);
70 return true;
71 }
72 return false;
73 }
74 }
75
76 /// <summary>
77 /// Removes a packet from the collection without attempting to obtain a
78 /// lock first
79 /// </summary>
80 /// <param name="sequenceNumber">Sequence number of the packet to remove</param>
81 /// <returns>True if the packet was found and removed, otherwise false</returns>
82 public bool RemoveUnsafe(uint sequenceNumber)
83 {
84 return packets.Remove(sequenceNumber);
85 }
86
87 /// <summary>
88 /// Removes a packet from the collection without attempting to obtain a
89 /// lock first
90 /// </summary>
91 /// <param name="sequenceNumber">Sequence number of the packet to remove</param>
92 /// <param name="packet">Returns the removed packet</param>
93 /// <returns>True if the packet was found and removed, otherwise false</returns>
94 public bool RemoveUnsafe(uint sequenceNumber, out OutgoingPacket packet)
95 {
96 if (packets.TryGetValue(sequenceNumber, out packet))
97 {
98 packets.Remove(sequenceNumber);
99 return true;
100 }
101
102 return false;
103 }
104
105 /// <summary>
106 /// Gets the packet with the lowest sequence number
107 /// </summary>
108 /// <returns>The packet with the lowest sequence number, or null if the
109 /// collection is empty</returns>
110 public OutgoingPacket GetOldest()
111 {
112 lock (SyncRoot)
113 {
114 using (SortedDictionary<uint, OutgoingPacket>.ValueCollection.Enumerator e = packets.Values.GetEnumerator())
115 {
116 if (e.MoveNext())
117 return e.Current;
118 else
119 return null;
120 }
121 }
122 }
123
124 /// <summary>
125 /// Returns a list of all of the packets with a TickCount older than
126 /// the specified timeout
127 /// </summary>
128 /// <param name="timeoutMS">Number of ticks (milliseconds) before a
129 /// packet is considered expired</param>
130 /// <returns>A list of all expired packets according to the given
131 /// expiration timeout</returns>
132 public List<OutgoingPacket> GetExpiredPackets(int timeoutMS)
133 {
134 List<OutgoingPacket> expiredPackets = null;
135
136 lock (SyncRoot)
137 {
138 int now = Environment.TickCount;
139 foreach (OutgoingPacket packet in packets.Values)
140 {
141 if (packet.TickCount == 0)
142 continue;
143
144 if (now - packet.TickCount >= timeoutMS)
145 {
146 if (expiredPackets == null)
147 expiredPackets = new List<OutgoingPacket>();
148 expiredPackets.Add(packet);
149 }
150 else
151 {
152 break;
153 }
154 }
155 }
156
157 return expiredPackets;
158 }
159 }
160}
diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs
index 45e724d..d78931a 100644
--- a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs
+++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs
@@ -323,7 +323,6 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
323 httpThread.IsBackground = true; 323 httpThread.IsBackground = true;
324 _finished = false; 324 _finished = false;
325 httpThread.Start(); 325 httpThread.Start();
326 ThreadTracker.Add(httpThread);
327 } 326 }
328 327
329 /* 328 /*
diff --git a/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs b/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs
index 8a169f8..97899a7 100644
--- a/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/XMLRPC/XMLRPCModule.cs
@@ -639,7 +639,6 @@ namespace OpenSim.Region.CoreModules.Scripting.XMLRPC
639 httpThread.IsBackground = true; 639 httpThread.IsBackground = true;
640 _finished = false; 640 _finished = false;
641 httpThread.Start(); 641 httpThread.Start();
642 ThreadTracker.Add(httpThread);
643 } 642 }
644 643
645 /* 644 /*
diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs
index 05ed70a..4fb4c51 100644
--- a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs
+++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs
@@ -345,7 +345,6 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
345 mapItemReqThread.Priority = ThreadPriority.BelowNormal; 345 mapItemReqThread.Priority = ThreadPriority.BelowNormal;
346 mapItemReqThread.SetApartmentState(ApartmentState.MTA); 346 mapItemReqThread.SetApartmentState(ApartmentState.MTA);
347 mapItemReqThread.Start(); 347 mapItemReqThread.Start();
348 ThreadTracker.Add(mapItemReqThread);
349 } 348 }
350 349
351 /// <summary> 350 /// <summary>
@@ -447,7 +446,6 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
447 // end gracefully 446 // end gracefully
448 if (st.agentID == UUID.Zero) 447 if (st.agentID == UUID.Zero)
449 { 448 {
450 ThreadTracker.Remove(mapItemReqThread);
451 break; 449 break;
452 } 450 }
453 451
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs
index 5f18a44..ceff28b 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -903,7 +903,6 @@ namespace OpenSim.Region.Framework.Scenes
903 //m_heartbeatTimer.Elapsed += new ElapsedEventHandler(Heartbeat); 903 //m_heartbeatTimer.Elapsed += new ElapsedEventHandler(Heartbeat);
904 if (HeartbeatThread != null) 904 if (HeartbeatThread != null)
905 { 905 {
906 ThreadTracker.Remove(HeartbeatThread);
907 HeartbeatThread.Abort(); 906 HeartbeatThread.Abort();
908 HeartbeatThread = null; 907 HeartbeatThread = null;
909 } 908 }
@@ -912,7 +911,6 @@ namespace OpenSim.Region.Framework.Scenes
912 HeartbeatThread.SetApartmentState(ApartmentState.MTA); 911 HeartbeatThread.SetApartmentState(ApartmentState.MTA);
913 HeartbeatThread.Name = string.Format("Heartbeat for region {0}", RegionInfo.RegionName); 912 HeartbeatThread.Name = string.Format("Heartbeat for region {0}", RegionInfo.RegionName);
914 HeartbeatThread.Priority = ThreadPriority.AboveNormal; 913 HeartbeatThread.Priority = ThreadPriority.AboveNormal;
915 ThreadTracker.Add(HeartbeatThread);
916 HeartbeatThread.Start(); 914 HeartbeatThread.Start();
917 } 915 }
918 916
@@ -1448,6 +1446,9 @@ namespace OpenSim.Region.Framework.Scenes
1448 m_log.Info("[SCENE]: Loading objects from datastore"); 1446 m_log.Info("[SCENE]: Loading objects from datastore");
1449 1447
1450 List<SceneObjectGroup> PrimsFromDB = m_storageManager.DataStore.LoadObjects(regionID); 1448 List<SceneObjectGroup> PrimsFromDB = m_storageManager.DataStore.LoadObjects(regionID);
1449
1450 m_log.Info("[SCENE]: Loaded " + PrimsFromDB.Count + " objects from the datastore");
1451
1451 foreach (SceneObjectGroup group in PrimsFromDB) 1452 foreach (SceneObjectGroup group in PrimsFromDB)
1452 { 1453 {
1453 if (group.RootPart == null) 1454 if (group.RootPart == null)
diff --git a/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs b/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs
index 9273fb5..cd401a6 100644
--- a/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs
@@ -360,7 +360,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.Chat
360 m_listener.Name = "IRCConnectorListenerThread"; 360 m_listener.Name = "IRCConnectorListenerThread";
361 m_listener.IsBackground = true; 361 m_listener.IsBackground = true;
362 m_listener.Start(); 362 m_listener.Start();
363 ThreadTracker.Add(m_listener);
364 363
365 // This is the message order recommended by RFC 2812 364 // This is the message order recommended by RFC 2812
366 if (m_password != null) 365 if (m_password != null)
diff --git a/OpenSim/Region/OptionalModules/ContentManagementSystem/CMController.cs b/OpenSim/Region/OptionalModules/ContentManagementSystem/CMController.cs
index 7202601..16fe9e9 100644
--- a/OpenSim/Region/OptionalModules/ContentManagementSystem/CMController.cs
+++ b/OpenSim/Region/OptionalModules/ContentManagementSystem/CMController.cs
@@ -152,7 +152,6 @@ namespace OpenSim.Region.OptionalModules.ContentManagement
152 m_thread.Name = "Content Management"; 152 m_thread.Name = "Content Management";
153 m_thread.IsBackground = true; 153 m_thread.IsBackground = true;
154 m_thread.Start(); 154 m_thread.Start();
155 ThreadTracker.Add(m_thread);
156 m_state = State.NONE; 155 m_state = State.NONE;
157 } 156 }
158 } 157 }
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs
index 0feb967..583d2ff 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueThreadClass.cs
@@ -138,7 +138,6 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
138 EventQueueThread.Priority = MyThreadPriority; 138 EventQueueThread.Priority = MyThreadPriority;
139 EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount; 139 EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount;
140 EventQueueThread.Start(); 140 EventQueueThread.Start();
141 ThreadTracker.Add(EventQueueThread);
142 141
143 // Look at this... Don't you wish everyone did that solid 142 // Look at this... Don't you wish everyone did that solid
144 // coding everywhere? :P 143 // coding everywhere? :P
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/MaintenanceThread.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/MaintenanceThread.cs
index 8bafe77..7ffdb1a 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/MaintenanceThread.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/MaintenanceThread.cs
@@ -97,7 +97,6 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
97 MaintenanceThreadThread.Name = "ScriptMaintenanceThread"; 97 MaintenanceThreadThread.Name = "ScriptMaintenanceThread";
98 MaintenanceThreadThread.IsBackground = true; 98 MaintenanceThreadThread.IsBackground = true;
99 MaintenanceThreadThread.Start(); 99 MaintenanceThreadThread.Start();
100 ThreadTracker.Add(MaintenanceThreadThread);
101 } 100 }
102 } 101 }
103 102
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs
index 3c91b29..9806218 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs
@@ -129,10 +129,6 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
129 129
130 public void AddRegion(Scene Sceneworld) 130 public void AddRegion(Scene Sceneworld)
131 { 131 {
132 m_log.Info("[" + ScriptEngineName + "]: ScriptEngine initializing");
133
134 m_Scene = Sceneworld;
135
136 // Make sure we have config 132 // Make sure we have config
137 if (ConfigSource.Configs[ScriptEngineName] == null) 133 if (ConfigSource.Configs[ScriptEngineName] == null)
138 ConfigSource.AddConfig(ScriptEngineName); 134 ConfigSource.AddConfig(ScriptEngineName);
@@ -143,6 +139,10 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine
143 if (!m_enabled) 139 if (!m_enabled)
144 return; 140 return;
145 141
142 m_log.Info("[" + ScriptEngineName + "]: ScriptEngine initializing");
143
144 m_Scene = Sceneworld;
145
146 // Create all objects we'll be using 146 // Create all objects we'll be using
147 m_EventQueueManager = new EventQueueManager(this); 147 m_EventQueueManager = new EventQueueManager(this);
148 m_EventManager = new EventManager(this, true); 148 m_EventManager = new EventManager(this, true);
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs
index e454524..1607d34 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs
@@ -142,7 +142,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
142 cmdHandlerThread.Priority = ThreadPriority.BelowNormal; 142 cmdHandlerThread.Priority = ThreadPriority.BelowNormal;
143 cmdHandlerThread.IsBackground = true; 143 cmdHandlerThread.IsBackground = true;
144 cmdHandlerThread.Start(); 144 cmdHandlerThread.Start();
145 ThreadTracker.Add(cmdHandlerThread);
146 } 145 }
147 } 146 }
148 147
diff --git a/OpenSim/TestSuite/BotManager.cs b/OpenSim/TestSuite/BotManager.cs
index 62e0ec7..55ba687 100644
--- a/OpenSim/TestSuite/BotManager.cs
+++ b/OpenSim/TestSuite/BotManager.cs
@@ -115,7 +115,6 @@ namespace OpenSim.TestSuite
115 m_td[pos].IsBackground = true; 115 m_td[pos].IsBackground = true;
116 m_td[pos].Start(); 116 m_td[pos].Start();
117 m_lBot.Add(pb); 117 m_lBot.Add(pb);
118 ThreadTracker.Add(m_td[pos]);
119 } 118 }
120 119
121 /// <summary> 120 /// <summary>
diff --git a/OpenSim/Tests/Clients/Grid/GridClient.cs b/OpenSim/Tests/Clients/Grid/GridClient.cs
index 23d8593..8798c5e 100644
--- a/OpenSim/Tests/Clients/Grid/GridClient.cs
+++ b/OpenSim/Tests/Clients/Grid/GridClient.cs
@@ -55,7 +55,7 @@ namespace OpenSim.Tests.Clients.GridClient
55 new PatternLayout("%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"); 55 new PatternLayout("%date [%thread] %-5level %logger [%property{NDC}] - %message%newline");
56 log4net.Config.BasicConfigurator.Configure(consoleAppender); 56 log4net.Config.BasicConfigurator.Configure(consoleAppender);
57 57
58 string serverURI = "http://127.0.0.1:" + ConfigSettings.DefaultGridServerHttpPort; 58 string serverURI = "http://127.0.0.1:8001";
59 GridServicesConnector m_Connector = new GridServicesConnector(serverURI); 59 GridServicesConnector m_Connector = new GridServicesConnector(serverURI);
60 60
61 GridRegion r1 = CreateRegion("Test Region 1", 1000, 1000); 61 GridRegion r1 = CreateRegion("Test Region 1", 1000, 1000);
diff --git a/OpenSim/Tools/pCampBot/BotManager.cs b/OpenSim/Tools/pCampBot/BotManager.cs
index 3f4e6ed..614b350 100644
--- a/OpenSim/Tools/pCampBot/BotManager.cs
+++ b/OpenSim/Tools/pCampBot/BotManager.cs
@@ -149,7 +149,6 @@ namespace pCampBot
149 m_td[pos].IsBackground = true; 149 m_td[pos].IsBackground = true;
150 m_td[pos].Start(); 150 m_td[pos].Start();
151 m_lBot.Add(pb); 151 m_lBot.Add(pb);
152 ThreadTracker.Add(m_td[pos]);
153 } 152 }
154 153
155 /// <summary> 154 /// <summary>
diff --git a/prebuild.xml b/prebuild.xml
index e58b81b..bba54f3 100644
--- a/prebuild.xml
+++ b/prebuild.xml
@@ -131,6 +131,7 @@
131 131
132 <ReferencePath>../../bin/</ReferencePath> 132 <ReferencePath>../../bin/</ReferencePath>
133 <Reference name="System"/> 133 <Reference name="System"/>
134 <Reference name="System.Core"/>
134 <Reference name="System.Xml"/> 135 <Reference name="System.Xml"/>
135 <Reference name="System.Data"/> 136 <Reference name="System.Data"/>
136 <Reference name="System.Drawing"/> 137 <Reference name="System.Drawing"/>
@@ -1750,6 +1751,7 @@
1750 1751
1751 <ReferencePath>../../../../bin/</ReferencePath> 1752 <ReferencePath>../../../../bin/</ReferencePath>
1752 <Reference name="System"/> 1753 <Reference name="System"/>
1754 <Reference name="System.Core"/>
1753 <Reference name="System.Xml"/> 1755 <Reference name="System.Xml"/>
1754 <Reference name="OpenMetaverseTypes.dll"/> 1756 <Reference name="OpenMetaverseTypes.dll"/>
1755 <Reference name="OpenMetaverse.StructuredData.dll"/> 1757 <Reference name="OpenMetaverse.StructuredData.dll"/>
@@ -3689,6 +3691,7 @@
3689 </Files> 3691 </Files>
3690 </Project> 3692 </Project>
3691 3693
3694 <!-- Commented for now until new unit tests are written for the new LLUDP implementation
3692 <Project frameworkVersion="v3_5" name="OpenSim.Region.ClientStack.LindenUDP.Tests" path="OpenSim/Region/ClientStack/LindenUDP/Tests" type="Library"> 3695 <Project frameworkVersion="v3_5" name="OpenSim.Region.ClientStack.LindenUDP.Tests" path="OpenSim/Region/ClientStack/LindenUDP/Tests" type="Library">
3693 <Configuration name="Debug"> 3696 <Configuration name="Debug">
3694 <Options> 3697 <Options>
@@ -3722,6 +3725,7 @@
3722 <Match pattern="*.cs" recurse="false"/> 3725 <Match pattern="*.cs" recurse="false"/>
3723 </Files> 3726 </Files>
3724 </Project> 3727 </Project>
3728 -->
3725 3729
3726 <Project frameworkVersion="v3_5" name="OpenSim.Region.ScriptEngine.Tests" path="OpenSim/Region/ScriptEngine" type="Library"> 3730 <Project frameworkVersion="v3_5" name="OpenSim.Region.ScriptEngine.Tests" path="OpenSim/Region/ScriptEngine" type="Library">
3727 <Configuration name="Debug"> 3731 <Configuration name="Debug">