aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Util.cs
diff options
context:
space:
mode:
authorDavid Walter Seikel2016-11-03 21:44:39 +1000
committerDavid Walter Seikel2016-11-03 21:44:39 +1000
commit134f86e8d5c414409631b25b8c6f0ee45fbd8631 (patch)
tree216b89d3fb89acfb81be1e440c25c41ab09fa96d /OpenSim/Framework/Util.cs
parentMore changing to production grid. Double oops. (diff)
downloadopensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.zip
opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.gz
opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.bz2
opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.xz
Initial update to OpenSim 0.8.2.1 source code.
Diffstat (limited to 'OpenSim/Framework/Util.cs')
-rw-r--r--OpenSim/Framework/Util.cs1510
1 files changed, 1246 insertions, 264 deletions
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index 0bd2977..1f74168 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -45,14 +45,33 @@ using System.Text.RegularExpressions;
45using System.Xml; 45using System.Xml;
46using System.Threading; 46using System.Threading;
47using log4net; 47using log4net;
48using log4net.Appender;
48using Nini.Config; 49using Nini.Config;
49using Nwc.XmlRpc; 50using Nwc.XmlRpc;
50using OpenMetaverse; 51using OpenMetaverse;
51using OpenMetaverse.StructuredData; 52using OpenMetaverse.StructuredData;
52using Amib.Threading; 53using Amib.Threading;
54using System.Collections.Concurrent;
55using System.Collections.Specialized;
56using System.Web;
53 57
54namespace OpenSim.Framework 58namespace OpenSim.Framework
55{ 59{
60 [Flags]
61 public enum PermissionMask : uint
62 {
63 None = 0,
64 Transfer = 1 << 13,
65 Modify = 1 << 14,
66 Copy = 1 << 15,
67 Export = 1 << 16,
68 Move = 1 << 19,
69 Damage = 1 << 20,
70 // All does not contain Export, which is special and must be
71 // explicitly given
72 All = (1 << 13) | (1 << 14) | (1 << 15) | (1 << 19)
73 }
74
56 /// <summary> 75 /// <summary>
57 /// The method used by Util.FireAndForget for asynchronously firing events 76 /// The method used by Util.FireAndForget for asynchronously firing events
58 /// </summary> 77 /// </summary>
@@ -73,14 +92,53 @@ namespace OpenSim.Framework
73 } 92 }
74 93
75 /// <summary> 94 /// <summary>
95 /// Class for delivering SmartThreadPool statistical information
96 /// </summary>
97 /// <remarks>
98 /// We do it this way so that we do not directly expose STP.
99 /// </remarks>
100 public class STPInfo
101 {
102 public string Name { get; set; }
103 public STPStartInfo STPStartInfo { get; set; }
104 public WIGStartInfo WIGStartInfo { get; set; }
105 public bool IsIdle { get; set; }
106 public bool IsShuttingDown { get; set; }
107 public int MaxThreads { get; set; }
108 public int MinThreads { get; set; }
109 public int InUseThreads { get; set; }
110 public int ActiveThreads { get; set; }
111 public int WaitingCallbacks { get; set; }
112 public int MaxConcurrentWorkItems { get; set; }
113 }
114
115 /// <summary>
76 /// Miscellaneous utility functions 116 /// Miscellaneous utility functions
77 /// </summary> 117 /// </summary>
78 public class Util 118 public static class Util
79 { 119 {
80 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 120 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
81 121
122 /// <summary>
123 /// Log-level for the thread pool:
124 /// 0 = no logging
125 /// 1 = only first line of stack trace; don't log common threads
126 /// 2 = full stack trace; don't log common threads
127 /// 3 = full stack trace, including common threads
128 /// </summary>
129 public static int LogThreadPool { get; set; }
130 public static bool LogOverloads { get; set; }
131
132 public static readonly int MAX_THREADPOOL_LEVEL = 3;
133
134 static Util()
135 {
136 LogThreadPool = 0;
137 LogOverloads = true;
138 }
139
82 private static uint nextXferID = 5000; 140 private static uint nextXferID = 5000;
83 private static Random randomClass = new Random(); 141 private static Random randomClass = new ThreadSafeRandom();
84 142
85 // Get a list of invalid file characters (OS dependent) 143 // Get a list of invalid file characters (OS dependent)
86 private static string regexInvalidFileChars = "[" + new String(Path.GetInvalidFileNameChars()) + "]"; 144 private static string regexInvalidFileChars = "[" + new String(Path.GetInvalidFileNameChars()) + "]";
@@ -92,8 +150,11 @@ namespace OpenSim.Framework
92 /// </summary> 150 /// </summary>
93 private static SmartThreadPool m_ThreadPool; 151 private static SmartThreadPool m_ThreadPool;
94 152
153 // Watchdog timer that aborts threads that have timed-out
154 private static Timer m_threadPoolWatchdog;
155
95 // Unix-epoch starts at January 1st 1970, 00:00:00 UTC. And all our times in the server are (or at least should be) in UTC. 156 // Unix-epoch starts at January 1st 1970, 00:00:00 UTC. And all our times in the server are (or at least should be) in UTC.
96 private static readonly DateTime unixEpoch = 157 public static readonly DateTime UnixEpoch =
97 DateTime.ParseExact("1970-01-01 00:00:00 +0", "yyyy-MM-dd hh:mm:ss z", DateTimeFormatInfo.InvariantInfo).ToUniversalTime(); 158 DateTime.ParseExact("1970-01-01 00:00:00 +0", "yyyy-MM-dd hh:mm:ss z", DateTimeFormatInfo.InvariantInfo).ToUniversalTime();
98 159
99 private static readonly string rawUUIDPattern 160 private static readonly string rawUUIDPattern
@@ -104,6 +165,11 @@ namespace OpenSim.Framework
104 public static FireAndForgetMethod DefaultFireAndForgetMethod = FireAndForgetMethod.SmartThreadPool; 165 public static FireAndForgetMethod DefaultFireAndForgetMethod = FireAndForgetMethod.SmartThreadPool;
105 public static FireAndForgetMethod FireAndForgetMethod = DefaultFireAndForgetMethod; 166 public static FireAndForgetMethod FireAndForgetMethod = DefaultFireAndForgetMethod;
106 167
168 public static bool IsPlatformMono
169 {
170 get { return Type.GetType("Mono.Runtime") != null; }
171 }
172
107 /// <summary> 173 /// <summary>
108 /// Gets the name of the directory where the current running executable 174 /// Gets the name of the directory where the current running executable
109 /// is located 175 /// is located
@@ -291,6 +357,49 @@ namespace OpenSim.Framework
291 return Utils.UIntsToLong(X, Y); 357 return Utils.UIntsToLong(X, Y);
292 } 358 }
293 359
360 // Regions are identified with a 'handle' made up of its region coordinates packed into a ulong.
361 // Several places rely on the ability to extract a region's location from its handle.
362 // Note the location is in 'world coordinates' (see below).
363 // Region handles are based on the lowest coordinate of the region so trim the passed x,y to be the regions 0,0.
364 public static ulong RegionWorldLocToHandle(uint X, uint Y)
365 {
366 return Utils.UIntsToLong(X, Y);
367 }
368
369 public static ulong RegionLocToHandle(uint X, uint Y)
370 {
371 return Utils.UIntsToLong(Util.RegionToWorldLoc(X), Util.RegionToWorldLoc(Y));
372 }
373
374 public static void RegionHandleToWorldLoc(ulong handle, out uint X, out uint Y)
375 {
376 X = (uint)(handle >> 32);
377 Y = (uint)(handle & (ulong)uint.MaxValue);
378 }
379
380 public static void RegionHandleToRegionLoc(ulong handle, out uint X, out uint Y)
381 {
382 uint worldX, worldY;
383 RegionHandleToWorldLoc(handle, out worldX, out worldY);
384 X = WorldToRegionLoc(worldX);
385 Y = WorldToRegionLoc(worldY);
386 }
387
388 // A region location can be 'world coordinates' (meters from zero) or 'region coordinates'
389 // (number of regions from zero). This measurement of regions relies on the legacy 256 region size.
390 // These routines exist to make what is being converted explicit so the next person knows what was meant.
391 // Convert a region's 'world coordinate' to its 'region coordinate'.
392 public static uint WorldToRegionLoc(uint worldCoord)
393 {
394 return worldCoord / Constants.RegionSize;
395 }
396
397 // Convert a region's 'region coordinate' to its 'world coordinate'.
398 public static uint RegionToWorldLoc(uint regionCoord)
399 {
400 return regionCoord * Constants.RegionSize;
401 }
402
294 public static T Clamp<T>(T x, T min, T max) 403 public static T Clamp<T>(T x, T min, T max)
295 where T : IComparable<T> 404 where T : IComparable<T>
296 { 405 {
@@ -302,15 +411,31 @@ namespace OpenSim.Framework
302 // Clamp the maximum magnitude of a vector 411 // Clamp the maximum magnitude of a vector
303 public static Vector3 ClampV(Vector3 x, float max) 412 public static Vector3 ClampV(Vector3 x, float max)
304 { 413 {
305 Vector3 ret = x;
306 float lenSq = x.LengthSquared(); 414 float lenSq = x.LengthSquared();
307 if (lenSq > (max * max)) 415 if (lenSq > (max * max))
308 { 416 {
309 x = x / x.Length() * max; 417 x = x / x.Length() * max;
310 } 418 }
419
311 return x; 420 return x;
312 } 421 }
313 422
423 /// <summary>
424 /// Check if any of the values in a Vector3 are NaN or Infinity
425 /// </summary>
426 /// <param name="v">Vector3 to check</param>
427 /// <returns></returns>
428 public static bool IsNanOrInfinity(Vector3 v)
429 {
430 if (float.IsNaN(v.X) || float.IsNaN(v.Y) || float.IsNaN(v.Z))
431 return true;
432
433 if (float.IsInfinity(v.X) || float.IsInfinity(v.Y) || float.IsNaN(v.Z))
434 return true;
435
436 return false;
437 }
438
314 // Inclusive, within range test (true if equal to the endpoints) 439 // Inclusive, within range test (true if equal to the endpoints)
315 public static bool InRange<T>(T x, T min, T max) 440 public static bool InRange<T>(T x, T min, T max)
316 where T : IComparable<T> 441 where T : IComparable<T>
@@ -400,6 +525,19 @@ namespace OpenSim.Framework
400 return sb.ToString(); 525 return sb.ToString();
401 } 526 }
402 527
528 public static byte[] DocToBytes(XmlDocument doc)
529 {
530 using (MemoryStream ms = new MemoryStream())
531 using (XmlTextWriter xw = new XmlTextWriter(ms, null))
532 {
533 xw.Formatting = Formatting.Indented;
534 doc.WriteTo(xw);
535 xw.Flush();
536
537 return ms.ToArray();
538 }
539 }
540
403 /// <summary> 541 /// <summary>
404 /// Is the platform Windows? 542 /// Is the platform Windows?
405 /// </summary> 543 /// </summary>
@@ -416,6 +554,11 @@ namespace OpenSim.Framework
416 554
417 public static bool LoadArchSpecificWindowsDll(string libraryName) 555 public static bool LoadArchSpecificWindowsDll(string libraryName)
418 { 556 {
557 return LoadArchSpecificWindowsDll(libraryName, string.Empty);
558 }
559
560 public static bool LoadArchSpecificWindowsDll(string libraryName, string path)
561 {
419 // We do this so that OpenSimulator on Windows loads the correct native library depending on whether 562 // We do this so that OpenSimulator on Windows loads the correct native library depending on whether
420 // it's running as a 32-bit process or a 64-bit one. By invoking LoadLibary here, later DLLImports 563 // it's running as a 32-bit process or a 64-bit one. By invoking LoadLibary here, later DLLImports
421 // will find it already loaded later on. 564 // will find it already loaded later on.
@@ -425,9 +568,9 @@ namespace OpenSim.Framework
425 string nativeLibraryPath; 568 string nativeLibraryPath;
426 569
427 if (Util.Is64BitProcess()) 570 if (Util.Is64BitProcess())
428 nativeLibraryPath = "lib64/" + libraryName; 571 nativeLibraryPath = Path.Combine(Path.Combine(path, "lib64"), libraryName);
429 else 572 else
430 nativeLibraryPath = "lib32/" + libraryName; 573 nativeLibraryPath = Path.Combine(Path.Combine(path, "lib32"), libraryName);
431 574
432 m_log.DebugFormat("[UTIL]: Loading native Windows library at {0}", nativeLibraryPath); 575 m_log.DebugFormat("[UTIL]: Loading native Windows library at {0}", nativeLibraryPath);
433 576
@@ -479,20 +622,18 @@ namespace OpenSim.Framework
479 622
480 public static int ToUnixTime(DateTime stamp) 623 public static int ToUnixTime(DateTime stamp)
481 { 624 {
482 TimeSpan t = stamp.ToUniversalTime() - unixEpoch; 625 TimeSpan t = stamp.ToUniversalTime() - UnixEpoch;
483 return (int) t.TotalSeconds; 626 return (int)t.TotalSeconds;
484 } 627 }
485 628
486 public static DateTime ToDateTime(ulong seconds) 629 public static DateTime ToDateTime(ulong seconds)
487 { 630 {
488 DateTime epoch = unixEpoch; 631 return UnixEpoch.AddSeconds(seconds);
489 return epoch.AddSeconds(seconds);
490 } 632 }
491 633
492 public static DateTime ToDateTime(int seconds) 634 public static DateTime ToDateTime(int seconds)
493 { 635 {
494 DateTime epoch = unixEpoch; 636 return UnixEpoch.AddSeconds(seconds);
495 return epoch.AddSeconds(seconds);
496 } 637 }
497 638
498 /// <summary> 639 /// <summary>
@@ -664,16 +805,6 @@ namespace OpenSim.Framework
664 } 805 }
665 806
666 /// <summary> 807 /// <summary>
667 /// Converts a URL to a IPAddress
668 /// </summary>
669 /// <param name="url">URL Standard Format</param>
670 /// <returns>A resolved IP Address</returns>
671 public static IPAddress GetHostFromURL(string url)
672 {
673 return GetHostFromDNS(url.Split(new char[] {'/', ':'})[3]);
674 }
675
676 /// <summary>
677 /// Returns a IP address from a specified DNS, favouring IPv4 addresses. 808 /// Returns a IP address from a specified DNS, favouring IPv4 addresses.
678 /// </summary> 809 /// </summary>
679 /// <param name="dnsAddress">DNS Hostname</param> 810 /// <param name="dnsAddress">DNS Hostname</param>
@@ -763,6 +894,54 @@ namespace OpenSim.Framework
763 } 894 }
764 895
765 /// <summary> 896 /// <summary>
897 /// Parses a foreign asset ID.
898 /// </summary>
899 /// <param name="id">A possibly-foreign asset ID: http://grid.example.com:8002/00000000-0000-0000-0000-000000000000 </param>
900 /// <param name="url">The URL: http://grid.example.com:8002</param>
901 /// <param name="assetID">The asset ID: 00000000-0000-0000-0000-000000000000. Returned even if 'id' isn't foreign.</param>
902 /// <returns>True: this is a foreign asset ID; False: it isn't</returns>
903 public static bool ParseForeignAssetID(string id, out string url, out string assetID)
904 {
905 url = String.Empty;
906 assetID = String.Empty;
907
908 UUID uuid;
909 if (UUID.TryParse(id, out uuid))
910 {
911 assetID = uuid.ToString();
912 return false;
913 }
914
915 if ((id.Length == 0) || (id[0] != 'h' && id[0] != 'H'))
916 return false;
917
918 Uri assetUri;
919 if (!Uri.TryCreate(id, UriKind.Absolute, out assetUri) || assetUri.Scheme != Uri.UriSchemeHttp)
920 return false;
921
922 // Simian
923 if (assetUri.Query != string.Empty)
924 {
925 NameValueCollection qscoll = HttpUtility.ParseQueryString(assetUri.Query);
926 assetID = qscoll["id"];
927 if (assetID != null)
928 url = id.Replace(assetID, ""); // Malformed again, as simian expects
929 else
930 url = id; // !!! best effort
931 }
932 else // robust
933 {
934 url = "http://" + assetUri.Authority;
935 assetID = assetUri.LocalPath.Trim(new char[] { '/' });
936 }
937
938 if (!UUID.TryParse(assetID, out uuid))
939 return false;
940
941 return true;
942 }
943
944 /// <summary>
766 /// Removes all invalid path chars (OS dependent) 945 /// Removes all invalid path chars (OS dependent)
767 /// </summary> 946 /// </summary>
768 /// <param name="path">path</param> 947 /// <param name="path">path</param>
@@ -816,9 +995,22 @@ namespace OpenSim.Framework
816 return "."; 995 return ".";
817 } 996 }
818 997
998 public static string logFile()
999 {
1000 foreach (IAppender appender in LogManager.GetRepository().GetAppenders())
1001 {
1002 if (appender is FileAppender)
1003 {
1004 return ((FileAppender)appender).File;
1005 }
1006 }
1007
1008 return "./OpenSim.log";
1009 }
1010
819 public static string logDir() 1011 public static string logDir()
820 { 1012 {
821 return "."; 1013 return Path.GetDirectoryName(logFile());
822 } 1014 }
823 1015
824 // From: http://coercedcode.blogspot.com/2008/03/c-generate-unique-filenames-within.html 1016 // From: http://coercedcode.blogspot.com/2008/03/c-generate-unique-filenames-within.html
@@ -849,12 +1041,13 @@ namespace OpenSim.Framework
849 return FileName; 1041 return FileName;
850 } 1042 }
851 1043
852 // Nini (config) related Methods 1044 #region Nini (config) related Methods
1045
853 public static IConfigSource ConvertDataRowToXMLConfig(DataRow row, string fileName) 1046 public static IConfigSource ConvertDataRowToXMLConfig(DataRow row, string fileName)
854 { 1047 {
855 if (!File.Exists(fileName)) 1048 if (!File.Exists(fileName))
856 { 1049 {
857 //create new file 1050 // create new file
858 } 1051 }
859 XmlConfigSource config = new XmlConfigSource(fileName); 1052 XmlConfigSource config = new XmlConfigSource(fileName);
860 AddDataRowToConfig(config, row); 1053 AddDataRowToConfig(config, row);
@@ -872,6 +1065,202 @@ namespace OpenSim.Framework
872 } 1065 }
873 } 1066 }
874 1067
1068 /// <summary>
1069 /// Gets the value of a configuration variable by looking into
1070 /// multiple sections in order. The latter sections overwrite
1071 /// any values previously found.
1072 /// </summary>
1073 /// <typeparam name="T">Type of the variable</typeparam>
1074 /// <param name="config">The configuration object</param>
1075 /// <param name="varname">The configuration variable</param>
1076 /// <param name="sections">Ordered sequence of sections to look at</param>
1077 /// <returns></returns>
1078 public static T GetConfigVarFromSections<T>(IConfigSource config, string varname, string[] sections)
1079 {
1080 return GetConfigVarFromSections<T>(config, varname, sections, default(T));
1081 }
1082
1083 /// <summary>
1084 /// Gets the value of a configuration variable by looking into
1085 /// multiple sections in order. The latter sections overwrite
1086 /// any values previously found.
1087 /// </summary>
1088 /// <remarks>
1089 /// If no value is found then the given default value is returned
1090 /// </remarks>
1091 /// <typeparam name="T">Type of the variable</typeparam>
1092 /// <param name="config">The configuration object</param>
1093 /// <param name="varname">The configuration variable</param>
1094 /// <param name="sections">Ordered sequence of sections to look at</param>
1095 /// <param name="val">Default value</param>
1096 /// <returns></returns>
1097 public static T GetConfigVarFromSections<T>(IConfigSource config, string varname, string[] sections, object val)
1098 {
1099 foreach (string section in sections)
1100 {
1101 IConfig cnf = config.Configs[section];
1102 if (cnf == null)
1103 continue;
1104
1105 if (typeof(T) == typeof(String))
1106 val = cnf.GetString(varname, (string)val);
1107 else if (typeof(T) == typeof(Boolean))
1108 val = cnf.GetBoolean(varname, (bool)val);
1109 else if (typeof(T) == typeof(Int32))
1110 val = cnf.GetInt(varname, (int)val);
1111 else if (typeof(T) == typeof(float))
1112 val = cnf.GetFloat(varname, (float)val);
1113 else
1114 m_log.ErrorFormat("[UTIL]: Unhandled type {0}", typeof(T));
1115 }
1116
1117 return (T)val;
1118 }
1119
1120 public static void MergeEnvironmentToConfig(IConfigSource ConfigSource)
1121 {
1122 IConfig enVars = ConfigSource.Configs["Environment"];
1123 // if section does not exist then user isn't expecting them, so don't bother.
1124 if( enVars != null )
1125 {
1126 // load the values from the environment
1127 EnvConfigSource envConfigSource = new EnvConfigSource();
1128 // add the requested keys
1129 string[] env_keys = enVars.GetKeys();
1130 foreach ( string key in env_keys )
1131 {
1132 envConfigSource.AddEnv(key, string.Empty);
1133 }
1134 // load the values from environment
1135 envConfigSource.LoadEnv();
1136 // add them in to the master
1137 ConfigSource.Merge(envConfigSource);
1138 ConfigSource.ExpandKeyValues();
1139 }
1140 }
1141
1142 public static T ReadSettingsFromIniFile<T>(IConfig config, T settingsClass)
1143 {
1144 Type settingsType = settingsClass.GetType();
1145
1146 FieldInfo[] fieldInfos = settingsType.GetFields();
1147 foreach (FieldInfo fieldInfo in fieldInfos)
1148 {
1149 if (!fieldInfo.IsStatic)
1150 {
1151 if (fieldInfo.FieldType == typeof(System.String))
1152 {
1153 fieldInfo.SetValue(settingsClass, config.Get(fieldInfo.Name, (string)fieldInfo.GetValue(settingsClass)));
1154 }
1155 else if (fieldInfo.FieldType == typeof(System.Boolean))
1156 {
1157 fieldInfo.SetValue(settingsClass, config.GetBoolean(fieldInfo.Name, (bool)fieldInfo.GetValue(settingsClass)));
1158 }
1159 else if (fieldInfo.FieldType == typeof(System.Int32))
1160 {
1161 fieldInfo.SetValue(settingsClass, config.GetInt(fieldInfo.Name, (int)fieldInfo.GetValue(settingsClass)));
1162 }
1163 else if (fieldInfo.FieldType == typeof(System.Single))
1164 {
1165 fieldInfo.SetValue(settingsClass, config.GetFloat(fieldInfo.Name, (float)fieldInfo.GetValue(settingsClass)));
1166 }
1167 else if (fieldInfo.FieldType == typeof(System.UInt32))
1168 {
1169 fieldInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(fieldInfo.Name, ((uint)fieldInfo.GetValue(settingsClass)).ToString())));
1170 }
1171 }
1172 }
1173
1174 PropertyInfo[] propertyInfos = settingsType.GetProperties();
1175 foreach (PropertyInfo propInfo in propertyInfos)
1176 {
1177 if ((propInfo.CanRead) && (propInfo.CanWrite))
1178 {
1179 if (propInfo.PropertyType == typeof(System.String))
1180 {
1181 propInfo.SetValue(settingsClass, config.Get(propInfo.Name, (string)propInfo.GetValue(settingsClass, null)), null);
1182 }
1183 else if (propInfo.PropertyType == typeof(System.Boolean))
1184 {
1185 propInfo.SetValue(settingsClass, config.GetBoolean(propInfo.Name, (bool)propInfo.GetValue(settingsClass, null)), null);
1186 }
1187 else if (propInfo.PropertyType == typeof(System.Int32))
1188 {
1189 propInfo.SetValue(settingsClass, config.GetInt(propInfo.Name, (int)propInfo.GetValue(settingsClass, null)), null);
1190 }
1191 else if (propInfo.PropertyType == typeof(System.Single))
1192 {
1193 propInfo.SetValue(settingsClass, config.GetFloat(propInfo.Name, (float)propInfo.GetValue(settingsClass, null)), null);
1194 }
1195 if (propInfo.PropertyType == typeof(System.UInt32))
1196 {
1197 propInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(propInfo.Name, ((uint)propInfo.GetValue(settingsClass, null)).ToString())), null);
1198 }
1199 }
1200 }
1201
1202 return settingsClass;
1203 }
1204
1205 /// <summary>
1206 /// Reads a configuration file, configFile, merging it with the main configuration, config.
1207 /// If the file doesn't exist, it copies a given exampleConfigFile onto configFile, and then
1208 /// merges it.
1209 /// </summary>
1210 /// <param name="config">The main configuration data</param>
1211 /// <param name="configFileName">The name of a configuration file in ConfigDirectory variable, no path</param>
1212 /// <param name="exampleConfigFile">Full path to an example configuration file</param>
1213 /// <param name="configFilePath">Full path ConfigDirectory/configFileName</param>
1214 /// <param name="created">True if the file was created in ConfigDirectory, false if it existed</param>
1215 /// <returns>True if success</returns>
1216 public static bool MergeConfigurationFile(IConfigSource config, string configFileName, string exampleConfigFile, out string configFilePath, out bool created)
1217 {
1218 created = false;
1219 configFilePath = string.Empty;
1220
1221 IConfig cnf = config.Configs["Startup"];
1222 if (cnf == null)
1223 {
1224 m_log.WarnFormat("[UTILS]: Startup section doesn't exist");
1225 return false;
1226 }
1227
1228 string configDirectory = cnf.GetString("ConfigDirectory", ".");
1229 string configFile = Path.Combine(configDirectory, configFileName);
1230
1231 if (!File.Exists(configFile) && !string.IsNullOrEmpty(exampleConfigFile))
1232 {
1233 // We need to copy the example onto it
1234
1235 if (!Directory.Exists(configDirectory))
1236 Directory.CreateDirectory(configDirectory);
1237
1238 try
1239 {
1240 File.Copy(exampleConfigFile, configFile);
1241 created = true;
1242 }
1243 catch (Exception e)
1244 {
1245 m_log.WarnFormat("[UTILS]: Exception copying configuration file {0} to {1}: {2}", configFile, exampleConfigFile, e.Message);
1246 return false;
1247 }
1248 }
1249
1250 if (File.Exists(configFile))
1251 {
1252 // Merge
1253 config.Merge(new IniConfigSource(configFile));
1254 config.ExpandKeyValues();
1255 configFilePath = configFile;
1256 return true;
1257 }
1258 else
1259 return false;
1260 }
1261
1262 #endregion
1263
875 public static float Clip(float x, float min, float max) 1264 public static float Clip(float x, float min, float max)
876 { 1265 {
877 return Math.Min(Math.Max(x, min), max); 1266 return Math.Min(Math.Max(x, min), max);
@@ -999,46 +1388,6 @@ namespace OpenSim.Framework
999 return ret; 1388 return ret;
1000 } 1389 }
1001 1390
1002 public static string Compress(string text)
1003 {
1004 byte[] buffer = Util.UTF8.GetBytes(text);
1005 MemoryStream memory = new MemoryStream();
1006 using (GZipStream compressor = new GZipStream(memory, CompressionMode.Compress, true))
1007 {
1008 compressor.Write(buffer, 0, buffer.Length);
1009 }
1010
1011 memory.Position = 0;
1012
1013 byte[] compressed = new byte[memory.Length];
1014 memory.Read(compressed, 0, compressed.Length);
1015
1016 byte[] compressedBuffer = new byte[compressed.Length + 4];
1017 Buffer.BlockCopy(compressed, 0, compressedBuffer, 4, compressed.Length);
1018 Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, compressedBuffer, 0, 4);
1019 return Convert.ToBase64String(compressedBuffer);
1020 }
1021
1022 public static string Decompress(string compressedText)
1023 {
1024 byte[] compressedBuffer = Convert.FromBase64String(compressedText);
1025 using (MemoryStream memory = new MemoryStream())
1026 {
1027 int msgLength = BitConverter.ToInt32(compressedBuffer, 0);
1028 memory.Write(compressedBuffer, 4, compressedBuffer.Length - 4);
1029
1030 byte[] buffer = new byte[msgLength];
1031
1032 memory.Position = 0;
1033 using (GZipStream decompressor = new GZipStream(memory, CompressionMode.Decompress))
1034 {
1035 decompressor.Read(buffer, 0, buffer.Length);
1036 }
1037
1038 return Util.UTF8.GetString(buffer);
1039 }
1040 }
1041
1042 /// <summary> 1391 /// <summary>
1043 /// Copy data from one stream to another, leaving the read position of both streams at the beginning. 1392 /// Copy data from one stream to another, leaving the read position of both streams at the beginning.
1044 /// </summary> 1393 /// </summary>
@@ -1119,7 +1468,7 @@ namespace OpenSim.Framework
1119 byte[] bytes = 1468 byte[] bytes =
1120 { 1469 {
1121 (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24), 1470 (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24),
1122 (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle << 56), 1471 (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56),
1123 (byte)x, (byte)(x >> 8), 0, 0, 1472 (byte)x, (byte)(x >> 8), 0, 0,
1124 (byte)y, (byte)(y >> 8), 0, 0 }; 1473 (byte)y, (byte)(y >> 8), 0, 0 };
1125 return new UUID(bytes, 0); 1474 return new UUID(bytes, 0);
@@ -1130,7 +1479,7 @@ namespace OpenSim.Framework
1130 byte[] bytes = 1479 byte[] bytes =
1131 { 1480 {
1132 (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24), 1481 (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24),
1133 (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle << 56), 1482 (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56),
1134 (byte)x, (byte)(x >> 8), (byte)z, (byte)(z >> 8), 1483 (byte)x, (byte)(x >> 8), (byte)z, (byte)(z >> 8),
1135 (byte)y, (byte)(y >> 8), 0, 0 }; 1484 (byte)y, (byte)(y >> 8), 0, 0 };
1136 return new UUID(bytes, 0); 1485 return new UUID(bytes, 0);
@@ -1203,7 +1552,7 @@ namespace OpenSim.Framework
1203 ru = "OSX/Mono"; 1552 ru = "OSX/Mono";
1204 else 1553 else
1205 { 1554 {
1206 if (Type.GetType("Mono.Runtime") != null) 1555 if (IsPlatformMono)
1207 ru = "Win/Mono"; 1556 ru = "Win/Mono";
1208 else 1557 else
1209 ru = "Win/.NET"; 1558 ru = "Win/.NET";
@@ -1242,69 +1591,6 @@ namespace OpenSim.Framework
1242 return displayConnectionString; 1591 return displayConnectionString;
1243 } 1592 }
1244 1593
1245 public static T ReadSettingsFromIniFile<T>(IConfig config, T settingsClass)
1246 {
1247 Type settingsType = settingsClass.GetType();
1248
1249 FieldInfo[] fieldInfos = settingsType.GetFields();
1250 foreach (FieldInfo fieldInfo in fieldInfos)
1251 {
1252 if (!fieldInfo.IsStatic)
1253 {
1254 if (fieldInfo.FieldType == typeof(System.String))
1255 {
1256 fieldInfo.SetValue(settingsClass, config.Get(fieldInfo.Name, (string)fieldInfo.GetValue(settingsClass)));
1257 }
1258 else if (fieldInfo.FieldType == typeof(System.Boolean))
1259 {
1260 fieldInfo.SetValue(settingsClass, config.GetBoolean(fieldInfo.Name, (bool)fieldInfo.GetValue(settingsClass)));
1261 }
1262 else if (fieldInfo.FieldType == typeof(System.Int32))
1263 {
1264 fieldInfo.SetValue(settingsClass, config.GetInt(fieldInfo.Name, (int)fieldInfo.GetValue(settingsClass)));
1265 }
1266 else if (fieldInfo.FieldType == typeof(System.Single))
1267 {
1268 fieldInfo.SetValue(settingsClass, config.GetFloat(fieldInfo.Name, (float)fieldInfo.GetValue(settingsClass)));
1269 }
1270 else if (fieldInfo.FieldType == typeof(System.UInt32))
1271 {
1272 fieldInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(fieldInfo.Name, ((uint)fieldInfo.GetValue(settingsClass)).ToString())));
1273 }
1274 }
1275 }
1276
1277 PropertyInfo[] propertyInfos = settingsType.GetProperties();
1278 foreach (PropertyInfo propInfo in propertyInfos)
1279 {
1280 if ((propInfo.CanRead) && (propInfo.CanWrite))
1281 {
1282 if (propInfo.PropertyType == typeof(System.String))
1283 {
1284 propInfo.SetValue(settingsClass, config.Get(propInfo.Name, (string)propInfo.GetValue(settingsClass, null)), null);
1285 }
1286 else if (propInfo.PropertyType == typeof(System.Boolean))
1287 {
1288 propInfo.SetValue(settingsClass, config.GetBoolean(propInfo.Name, (bool)propInfo.GetValue(settingsClass, null)), null);
1289 }
1290 else if (propInfo.PropertyType == typeof(System.Int32))
1291 {
1292 propInfo.SetValue(settingsClass, config.GetInt(propInfo.Name, (int)propInfo.GetValue(settingsClass, null)), null);
1293 }
1294 else if (propInfo.PropertyType == typeof(System.Single))
1295 {
1296 propInfo.SetValue(settingsClass, config.GetFloat(propInfo.Name, (float)propInfo.GetValue(settingsClass, null)), null);
1297 }
1298 if (propInfo.PropertyType == typeof(System.UInt32))
1299 {
1300 propInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(propInfo.Name, ((uint)propInfo.GetValue(settingsClass, null)).ToString())), null);
1301 }
1302 }
1303 }
1304
1305 return settingsClass;
1306 }
1307
1308 public static string Base64ToString(string str) 1594 public static string Base64ToString(string str)
1309 { 1595 {
1310 Decoder utf8Decode = Encoding.UTF8.GetDecoder(); 1596 Decoder utf8Decode = Encoding.UTF8.GetDecoder();
@@ -1317,6 +1603,46 @@ namespace OpenSim.Framework
1317 return result; 1603 return result;
1318 } 1604 }
1319 1605
1606 public static void BinaryToASCII(char[] chars)
1607 {
1608 for (int i = 0; i < chars.Length; i++)
1609 {
1610 char ch = chars[i];
1611 if (ch < 32 || ch > 127)
1612 chars[i] = '.';
1613 }
1614 }
1615
1616 public static string BinaryToASCII(string src)
1617 {
1618 char[] chars = src.ToCharArray();
1619 BinaryToASCII(chars);
1620 return new String(chars);
1621 }
1622
1623 /// <summary>
1624 /// Reads a known number of bytes from a stream.
1625 /// Throws EndOfStreamException if the stream doesn't contain enough data.
1626 /// </summary>
1627 /// <param name="stream">The stream to read data from</param>
1628 /// <param name="data">The array to write bytes into. The array
1629 /// will be completely filled from the stream, so an appropriate
1630 /// size must be given.</param>
1631 public static void ReadStream(Stream stream, byte[] data)
1632 {
1633 int offset = 0;
1634 int remaining = data.Length;
1635
1636 while (remaining > 0)
1637 {
1638 int read = stream.Read(data, offset, remaining);
1639 if (read <= 0)
1640 throw new EndOfStreamException(String.Format("End of stream reached with {0} bytes left to read", remaining));
1641 remaining -= read;
1642 offset += read;
1643 }
1644 }
1645
1320 public static Guid GetHashGuid(string data, string salt) 1646 public static Guid GetHashGuid(string data, string salt)
1321 { 1647 {
1322 byte[] hash = ComputeMD5Hash(data + salt); 1648 byte[] hash = ComputeMD5Hash(data + salt);
@@ -1466,32 +1792,6 @@ namespace OpenSim.Framework
1466 return found.ToArray(); 1792 return found.ToArray();
1467 } 1793 }
1468 1794
1469 public static string ServerURI(string uri)
1470 {
1471 if (uri == string.Empty)
1472 return string.Empty;
1473
1474 // Get rid of eventual slashes at the end
1475 uri = uri.TrimEnd('/');
1476
1477 IPAddress ipaddr1 = null;
1478 string port1 = "";
1479 try
1480 {
1481 ipaddr1 = Util.GetHostFromURL(uri);
1482 }
1483 catch { }
1484
1485 try
1486 {
1487 port1 = uri.Split(new char[] { ':' })[2];
1488 }
1489 catch { }
1490
1491 // We tried our best to convert the domain names to IP addresses
1492 return (ipaddr1 != null) ? "http://" + ipaddr1.ToString() + ":" + port1 : uri;
1493 }
1494
1495 /// <summary> 1795 /// <summary>
1496 /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 256 bytes if necessary. 1796 /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 256 bytes if necessary.
1497 /// </summary> 1797 /// </summary>
@@ -1518,17 +1818,26 @@ namespace OpenSim.Framework
1518 /// <returns></returns> 1818 /// <returns></returns>
1519 public static byte[] StringToBytes256(string str) 1819 public static byte[] StringToBytes256(string str)
1520 { 1820 {
1521 if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; } 1821 if (String.IsNullOrEmpty(str))
1522 if (str.Length > 254) str = str.Remove(254); 1822 return Utils.EmptyBytes;
1523 if (!str.EndsWith("\0")) { str += "\0"; } 1823
1824 if (!str.EndsWith("\0"))
1825 str += "\0";
1524 1826
1525 // Because this is UTF-8 encoding and not ASCII, it's possible we 1827 // Because this is UTF-8 encoding and not ASCII, it's possible we
1526 // might have gotten an oversized array even after the string trim 1828 // might have gotten an oversized array even after the string trim
1527 byte[] data = UTF8.GetBytes(str); 1829 byte[] data = UTF8.GetBytes(str);
1830
1528 if (data.Length > 256) 1831 if (data.Length > 256)
1529 { 1832 {
1530 Array.Resize<byte>(ref data, 256); 1833 int cut = 255;
1531 data[255] = 0; 1834 if((data[cut] & 0x80 ) != 0 )
1835 {
1836 while(cut > 0 && (data[cut] & 0xc0) != 0xc0)
1837 cut--;
1838 }
1839 Array.Resize<byte>(ref data, cut + 1);
1840 data[cut] = 0;
1532 } 1841 }
1533 1842
1534 return data; 1843 return data;
@@ -1560,23 +1869,56 @@ namespace OpenSim.Framework
1560 /// <returns></returns> 1869 /// <returns></returns>
1561 public static byte[] StringToBytes1024(string str) 1870 public static byte[] StringToBytes1024(string str)
1562 { 1871 {
1563 if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; } 1872 if (String.IsNullOrEmpty(str))
1564 if (str.Length > 1023) str = str.Remove(1023); 1873 return Utils.EmptyBytes;
1565 if (!str.EndsWith("\0")) { str += "\0"; } 1874
1875 if (!str.EndsWith("\0"))
1876 str += "\0";
1566 1877
1567 // Because this is UTF-8 encoding and not ASCII, it's possible we 1878 // Because this is UTF-8 encoding and not ASCII, it's possible we
1568 // might have gotten an oversized array even after the string trim 1879 // might have gotten an oversized array even after the string trim
1569 byte[] data = UTF8.GetBytes(str); 1880 byte[] data = UTF8.GetBytes(str);
1881
1570 if (data.Length > 1024) 1882 if (data.Length > 1024)
1571 { 1883 {
1572 Array.Resize<byte>(ref data, 1024); 1884 int cut = 1023;
1573 data[1023] = 0; 1885 if((data[cut] & 0x80 ) != 0 )
1886 {
1887 while(cut > 0 && (data[cut] & 0xc0) != 0xc0)
1888 cut--;
1889 }
1890 Array.Resize<byte>(ref data, cut + 1);
1891 data[cut] = 0;
1574 } 1892 }
1575 1893
1576 return data; 1894 return data;
1577 } 1895 }
1578 1896
1579 /// <summary> 1897 /// <summary>
1898 /// Pretty format the hashtable contents to a single line.
1899 /// </summary>
1900 /// <remarks>
1901 /// Used for debugging output.
1902 /// </remarks>
1903 /// <param name='ht'></param>
1904 public static string PrettyFormatToSingleLine(Hashtable ht)
1905 {
1906 StringBuilder sb = new StringBuilder();
1907
1908 int i = 0;
1909
1910 foreach (string key in ht.Keys)
1911 {
1912 sb.AppendFormat("{0}:{1}", key, ht[key]);
1913
1914 if (++i < ht.Count)
1915 sb.AppendFormat(", ");
1916 }
1917
1918 return sb.ToString();
1919 }
1920
1921 /// <summary>
1580 /// Used to trigger an early library load on Windows systems. 1922 /// Used to trigger an early library load on Windows systems.
1581 /// </summary> 1923 /// </summary>
1582 /// <remarks> 1924 /// <remarks>
@@ -1646,25 +1988,28 @@ namespace OpenSim.Framework
1646 } 1988 }
1647 } 1989 }
1648 1990
1649 public static void FireAndForget(System.Threading.WaitCallback callback) 1991 public static void InitThreadPool(int minThreads, int maxThreads)
1650 {
1651 FireAndForget(callback, null);
1652 }
1653
1654 public static void InitThreadPool(int maxThreads)
1655 { 1992 {
1656 if (maxThreads < 2) 1993 if (maxThreads < 2)
1657 throw new ArgumentOutOfRangeException("maxThreads", "maxThreads must be greater than 2"); 1994 throw new ArgumentOutOfRangeException("maxThreads", "maxThreads must be greater than 2");
1995
1996 if (minThreads > maxThreads || minThreads < 2)
1997 throw new ArgumentOutOfRangeException("minThreads", "minThreads must be greater than 2 and less than or equal to maxThreads");
1998
1658 if (m_ThreadPool != null) 1999 if (m_ThreadPool != null)
1659 throw new InvalidOperationException("SmartThreadPool is already initialized"); 2000 {
2001 m_log.Warn("SmartThreadPool is already initialized. Ignoring request.");
2002 return;
2003 }
1660 2004
1661 STPStartInfo startInfo = new STPStartInfo(); 2005 STPStartInfo startInfo = new STPStartInfo();
1662 startInfo.ThreadPoolName = "Util"; 2006 startInfo.ThreadPoolName = "Util";
1663 startInfo.IdleTimeout = 2000; 2007 startInfo.IdleTimeout = 2000;
1664 startInfo.MaxWorkerThreads = maxThreads; 2008 startInfo.MaxWorkerThreads = maxThreads;
1665 startInfo.MinWorkerThreads = 2; 2009 startInfo.MinWorkerThreads = minThreads;
1666 2010
1667 m_ThreadPool = new SmartThreadPool(startInfo); 2011 m_ThreadPool = new SmartThreadPool(startInfo);
2012 m_threadPoolWatchdog = new Timer(ThreadPoolWatchdog, null, 0, 1000);
1668 } 2013 }
1669 2014
1670 public static int FireAndForgetCount() 2015 public static int FireAndForgetCount()
@@ -1687,15 +2032,179 @@ namespace OpenSim.Framework
1687 throw new NotImplementedException(); 2032 throw new NotImplementedException();
1688 } 2033 }
1689 } 2034 }
2035
2036 /// <summary>
2037 /// Additional information about threads in the main thread pool. Used to time how long the
2038 /// thread has been running, and abort it if it has timed-out.
2039 /// </summary>
2040 private class ThreadInfo
2041 {
2042 public long ThreadFuncNum { get; set; }
2043 public string StackTrace { get; set; }
2044 private string context;
2045 public bool LogThread { get; set; }
2046
2047 public IWorkItemResult WorkItem { get; set; }
2048 public Thread Thread { get; set; }
2049 public bool Running { get; set; }
2050 public bool Aborted { get; set; }
2051 private int started;
2052
2053 public ThreadInfo(long threadFuncNum, string context)
2054 {
2055 ThreadFuncNum = threadFuncNum;
2056 this.context = context;
2057 LogThread = false;
2058 Thread = null;
2059 Running = false;
2060 Aborted = false;
2061 }
2062
2063 public void Started()
2064 {
2065 Thread = Thread.CurrentThread;
2066 started = EnvironmentTickCount();
2067 Running = true;
2068 }
2069
2070 public void Ended()
2071 {
2072 Running = false;
2073 }
2074
2075 public int Elapsed()
2076 {
2077 return EnvironmentTickCountSubtract(started);
2078 }
2079
2080 public void Abort()
2081 {
2082 Aborted = true;
2083 WorkItem.Cancel(true);
2084 }
2085
2086 /// <summary>
2087 /// Returns the thread's stack trace.
2088 /// </summary>
2089 /// <remarks>
2090 /// May return one of two stack traces. First, tries to get the thread's active stack
2091 /// trace. But this can fail, so as a fallback this method will return the stack
2092 /// trace that was active when the task was queued.
2093 /// </remarks>
2094 public string GetStackTrace()
2095 {
2096 string ret = (context == null) ? "" : ("(" + context + ") ");
2097
2098 StackTrace activeStackTrace = Util.GetStackTrace(Thread);
2099 if (activeStackTrace != null)
2100 ret += activeStackTrace.ToString();
2101 else if (StackTrace != null)
2102 ret += "(Stack trace when queued) " + StackTrace;
2103 // else, no stack trace available
2104
2105 return ret;
2106 }
2107 }
2108
2109 private static long nextThreadFuncNum = 0;
2110 private static long numQueuedThreadFuncs = 0;
2111 private static long numRunningThreadFuncs = 0;
2112 private static long numTotalThreadFuncsCalled = 0;
2113 private static Int32 threadFuncOverloadMode = 0;
2114
2115 public static long TotalQueuedFireAndForgetCalls { get { return numQueuedThreadFuncs; } }
2116 public static long TotalRunningFireAndForgetCalls { get { return numRunningThreadFuncs; } }
2117
2118 // Maps (ThreadFunc number -> Thread)
2119 private static ConcurrentDictionary<long, ThreadInfo> activeThreads = new ConcurrentDictionary<long, ThreadInfo>();
2120
2121 private static readonly int THREAD_TIMEOUT = 10 * 60 * 1000; // 10 minutes
2122
2123 /// <summary>
2124 /// Finds threads in the main thread pool that have timed-out, and aborts them.
2125 /// </summary>
2126 private static void ThreadPoolWatchdog(object state)
2127 {
2128 foreach (KeyValuePair<long, ThreadInfo> entry in activeThreads)
2129 {
2130 ThreadInfo t = entry.Value;
2131 if (t.Running && !t.Aborted && (t.Elapsed() >= THREAD_TIMEOUT))
2132 {
2133 m_log.WarnFormat("Timeout in threadfunc {0} ({1}) {2}", t.ThreadFuncNum, t.Thread.Name, t.GetStackTrace());
2134 t.Abort();
2135
2136 ThreadInfo dummy;
2137 activeThreads.TryRemove(entry.Key, out dummy);
2138
2139 // It's possible that the thread won't abort. To make sure the thread pool isn't
2140 // depleted, increase the pool size.
2141 m_ThreadPool.MaxThreads++;
2142 }
2143 }
2144 }
2145
2146 public static long TotalFireAndForgetCallsMade { get { return numTotalThreadFuncsCalled; } }
2147
2148 public static Dictionary<string, int> GetFireAndForgetCallsMade()
2149 {
2150 return new Dictionary<string, int>(m_fireAndForgetCallsMade);
2151 }
2152
2153 private static Dictionary<string, int> m_fireAndForgetCallsMade = new Dictionary<string, int>();
2154
2155 public static Dictionary<string, int> GetFireAndForgetCallsInProgress()
2156 {
2157 return new Dictionary<string, int>(m_fireAndForgetCallsInProgress);
2158 }
2159
2160 private static Dictionary<string, int> m_fireAndForgetCallsInProgress = new Dictionary<string, int>();
2161
2162 public static void FireAndForget(System.Threading.WaitCallback callback)
2163 {
2164 FireAndForget(callback, null, null);
2165 }
1690 2166
1691 public static void FireAndForget(System.Threading.WaitCallback callback, object obj) 2167 public static void FireAndForget(System.Threading.WaitCallback callback, object obj)
1692 { 2168 {
2169 FireAndForget(callback, obj, null);
2170 }
2171
2172 public static void FireAndForget(System.Threading.WaitCallback callback, object obj, string context)
2173 {
2174 Interlocked.Increment(ref numTotalThreadFuncsCalled);
2175
2176 if (context != null)
2177 {
2178 if (!m_fireAndForgetCallsMade.ContainsKey(context))
2179 m_fireAndForgetCallsMade[context] = 1;
2180 else
2181 m_fireAndForgetCallsMade[context]++;
2182
2183 if (!m_fireAndForgetCallsInProgress.ContainsKey(context))
2184 m_fireAndForgetCallsInProgress[context] = 1;
2185 else
2186 m_fireAndForgetCallsInProgress[context]++;
2187 }
2188
1693 WaitCallback realCallback; 2189 WaitCallback realCallback;
1694 2190
2191 bool loggingEnabled = LogThreadPool > 0;
2192
2193 long threadFuncNum = Interlocked.Increment(ref nextThreadFuncNum);
2194 ThreadInfo threadInfo = new ThreadInfo(threadFuncNum, context);
2195
1695 if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest) 2196 if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
1696 { 2197 {
1697 // If we're running regression tests, then we want any exceptions to rise up to the test code. 2198 // If we're running regression tests, then we want any exceptions to rise up to the test code.
1698 realCallback = o => { Culture.SetCurrentCulture(); callback(o); }; 2199 realCallback =
2200 o =>
2201 {
2202 Culture.SetCurrentCulture();
2203 callback(o);
2204
2205 if (context != null)
2206 m_fireAndForgetCallsInProgress[context]--;
2207 };
1699 } 2208 }
1700 else 2209 else
1701 { 2210 {
@@ -1704,118 +2213,291 @@ namespace OpenSim.Framework
1704 // for decimals places but is read by a culture that treats commas as number seperators. 2213 // for decimals places but is read by a culture that treats commas as number seperators.
1705 realCallback = o => 2214 realCallback = o =>
1706 { 2215 {
1707 Culture.SetCurrentCulture(); 2216 long numQueued1 = Interlocked.Decrement(ref numQueuedThreadFuncs);
2217 long numRunning1 = Interlocked.Increment(ref numRunningThreadFuncs);
2218 threadInfo.Started();
2219 activeThreads[threadFuncNum] = threadInfo;
1708 2220
1709 try 2221 try
1710 { 2222 {
2223 if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread)
2224 m_log.DebugFormat("Run threadfunc {0} (Queued {1}, Running {2})", threadFuncNum, numQueued1, numRunning1);
2225
2226 Culture.SetCurrentCulture();
2227
1711 callback(o); 2228 callback(o);
1712 } 2229 }
2230 catch (ThreadAbortException e)
2231 {
2232 m_log.Error(string.Format("Aborted threadfunc {0} ", threadFuncNum), e);
2233 }
1713 catch (Exception e) 2234 catch (Exception e)
1714 { 2235 {
1715 m_log.ErrorFormat( 2236 m_log.Error(string.Format("[UTIL]: Util STP threadfunc {0} terminated with error ", threadFuncNum), e);
1716 "[UTIL]: Continuing after async_call_method thread terminated with exception {0}{1}", 2237 }
1717 e.Message, e.StackTrace); 2238 finally
2239 {
2240 Interlocked.Decrement(ref numRunningThreadFuncs);
2241 threadInfo.Ended();
2242 ThreadInfo dummy;
2243 activeThreads.TryRemove(threadFuncNum, out dummy);
2244 if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread)
2245 m_log.DebugFormat("Exit threadfunc {0} ({1})", threadFuncNum, FormatDuration(threadInfo.Elapsed()));
2246
2247 if (context != null)
2248 m_fireAndForgetCallsInProgress[context]--;
1718 } 2249 }
1719 }; 2250 };
1720 } 2251 }
1721 2252
1722 switch (FireAndForgetMethod) 2253 long numQueued = Interlocked.Increment(ref numQueuedThreadFuncs);
2254 try
1723 { 2255 {
1724 case FireAndForgetMethod.RegressionTest: 2256 long numRunning = numRunningThreadFuncs;
1725 case FireAndForgetMethod.None: 2257
1726 realCallback.Invoke(obj); 2258 if (m_ThreadPool != null && LogOverloads)
1727 break; 2259 {
1728 case FireAndForgetMethod.UnsafeQueueUserWorkItem: 2260 if ((threadFuncOverloadMode == 0) && (numRunning >= m_ThreadPool.MaxThreads))
1729 ThreadPool.UnsafeQueueUserWorkItem(realCallback, obj); 2261 {
1730 break; 2262 if (Interlocked.CompareExchange(ref threadFuncOverloadMode, 1, 0) == 0)
1731 case FireAndForgetMethod.QueueUserWorkItem: 2263 m_log.DebugFormat("Threadfunc: enable overload mode (Queued {0}, Running {1})", numQueued, numRunning);
1732 ThreadPool.QueueUserWorkItem(realCallback, obj); 2264 }
1733 break; 2265 else if ((threadFuncOverloadMode == 1) && (numRunning <= (m_ThreadPool.MaxThreads * 2) / 3))
1734 case FireAndForgetMethod.BeginInvoke: 2266 {
1735 FireAndForgetWrapper wrapper = FireAndForgetWrapper.Instance; 2267 if (Interlocked.CompareExchange(ref threadFuncOverloadMode, 0, 1) == 1)
1736 wrapper.FireAndForget(realCallback, obj); 2268 m_log.DebugFormat("Threadfunc: disable overload mode (Queued {0}, Running {1})", numQueued, numRunning);
1737 break; 2269 }
1738 case FireAndForgetMethod.SmartThreadPool: 2270 }
1739 if (m_ThreadPool == null) 2271
1740 InitThreadPool(15); 2272 if (loggingEnabled || (threadFuncOverloadMode == 1))
1741 m_ThreadPool.QueueWorkItem(SmartThreadPoolCallback, new object[] { realCallback, obj }); 2273 {
1742 break; 2274 string full, partial;
1743 case FireAndForgetMethod.Thread: 2275 GetFireAndForgetStackTrace(out full, out partial);
1744 Thread thread = new Thread(delegate(object o) { realCallback(o); }); 2276 threadInfo.StackTrace = full;
1745 thread.Start(obj); 2277 threadInfo.LogThread = ShouldLogThread(partial);
1746 break; 2278
1747 default: 2279 if (threadInfo.LogThread)
1748 throw new NotImplementedException(); 2280 {
2281 m_log.DebugFormat("Queue threadfunc {0} (Queued {1}, Running {2}) {3}{4}",
2282 threadFuncNum, numQueued, numRunningThreadFuncs,
2283 (context == null) ? "" : ("(" + context + ") "),
2284 (LogThreadPool >= 2) ? full : partial);
2285 }
2286 }
2287 else
2288 {
2289 // Since we didn't log "Queue threadfunc", don't log "Run threadfunc" or "End threadfunc" either.
2290 // Those log lines aren't useful when we don't know which function is running in the thread.
2291 threadInfo.LogThread = false;
2292 }
2293
2294 switch (FireAndForgetMethod)
2295 {
2296 case FireAndForgetMethod.RegressionTest:
2297 case FireAndForgetMethod.None:
2298 realCallback.Invoke(obj);
2299 break;
2300 case FireAndForgetMethod.UnsafeQueueUserWorkItem:
2301 ThreadPool.UnsafeQueueUserWorkItem(realCallback, obj);
2302 break;
2303 case FireAndForgetMethod.QueueUserWorkItem:
2304 ThreadPool.QueueUserWorkItem(realCallback, obj);
2305 break;
2306 case FireAndForgetMethod.BeginInvoke:
2307 FireAndForgetWrapper wrapper = FireAndForgetWrapper.Instance;
2308 wrapper.FireAndForget(realCallback, obj);
2309 break;
2310 case FireAndForgetMethod.SmartThreadPool:
2311 if (m_ThreadPool == null)
2312 InitThreadPool(2, 15);
2313 threadInfo.WorkItem = m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj);
2314 break;
2315 case FireAndForgetMethod.Thread:
2316 Thread thread = new Thread(delegate(object o) { realCallback(o); });
2317 thread.Start(obj);
2318 break;
2319 default:
2320 throw new NotImplementedException();
2321 }
2322 }
2323 catch (Exception)
2324 {
2325 Interlocked.Decrement(ref numQueuedThreadFuncs);
2326 ThreadInfo dummy;
2327 activeThreads.TryRemove(threadFuncNum, out dummy);
2328 throw;
1749 } 2329 }
1750 } 2330 }
1751 2331
1752 /// <summary> 2332 /// <summary>
1753 /// Get a thread pool report. 2333 /// Returns whether the thread should be logged. Some very common threads aren't logged,
2334 /// to avoid filling up the log.
1754 /// </summary> 2335 /// </summary>
1755 /// <returns></returns> 2336 /// <param name="stackTrace">A partial stack trace of where the thread was queued</param>
1756 public static string GetThreadPoolReport() 2337 /// <returns>Whether to log this thread</returns>
2338 private static bool ShouldLogThread(string stackTrace)
1757 { 2339 {
1758 string threadPoolUsed = null; 2340 if (LogThreadPool < 3)
1759 int maxThreads = 0; 2341 {
1760 int minThreads = 0; 2342 if (stackTrace.Contains("BeginFireQueueEmpty"))
1761 int allocatedThreads = 0; 2343 return false;
1762 int inUseThreads = 0; 2344 }
1763 int waitingCallbacks = 0; 2345
1764 int completionPortThreads = 0; 2346 return true;
2347 }
1765 2348
1766 StringBuilder sb = new StringBuilder(); 2349 /// <summary>
1767 if (FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool) 2350 /// Returns a stack trace for a thread added using FireAndForget().
2351 /// </summary>
2352 /// <param name="full">Will contain the full stack trace</param>
2353 /// <param name="partial">Will contain only the first frame of the stack trace</param>
2354 private static void GetFireAndForgetStackTrace(out string full, out string partial)
2355 {
2356 string src = Environment.StackTrace;
2357 string[] lines = src.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
2358
2359 StringBuilder dest = new StringBuilder(src.Length);
2360
2361 bool started = false;
2362 bool first = true;
2363 partial = "";
2364
2365 for (int i = 0; i < lines.Length; i++)
1768 { 2366 {
1769 // ROBUST currently leaves this the FireAndForgetMethod but never actually initializes the threadpool. 2367 string line = lines[i];
1770 if (m_ThreadPool != null) 2368
2369 if (!started)
2370 {
2371 // Skip the initial stack frames, because they're of no interest for debugging
2372 if (line.Contains("StackTrace") || line.Contains("FireAndForget"))
2373 continue;
2374 started = true;
2375 }
2376
2377 if (first)
1771 { 2378 {
1772 threadPoolUsed = "SmartThreadPool"; 2379 line = line.TrimStart();
1773 maxThreads = m_ThreadPool.MaxThreads; 2380 first = false;
1774 minThreads = m_ThreadPool.MinThreads; 2381 partial = line;
1775 inUseThreads = m_ThreadPool.InUseThreads;
1776 allocatedThreads = m_ThreadPool.ActiveThreads;
1777 waitingCallbacks = m_ThreadPool.WaitingCallbacks;
1778 } 2382 }
2383
2384 bool last = (i == lines.Length - 1);
2385 if (last)
2386 dest.Append(line);
2387 else
2388 dest.AppendLine(line);
1779 } 2389 }
1780 else if ( 2390
1781 FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem 2391 full = dest.ToString();
1782 || FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem) 2392 }
2393
2394#pragma warning disable 0618
2395 /// <summary>
2396 /// Return the stack trace of a different thread.
2397 /// </summary>
2398 /// <remarks>
2399 /// This is complicated because the thread needs to be paused in order to get its stack
2400 /// trace. And pausing another thread can cause a deadlock. This method attempts to
2401 /// avoid deadlock by using a short timeout (200ms), after which it gives up and
2402 /// returns 'null' instead of the stack trace.
2403 ///
2404 /// Take from: http://stackoverflow.com/a/14935378
2405 ///
2406 /// WARNING: this doesn't work in Mono. See https://bugzilla.novell.com/show_bug.cgi?id=571691
2407 ///
2408 /// </remarks>
2409 /// <returns>The stack trace, or null if failed to get it</returns>
2410 private static StackTrace GetStackTrace(Thread targetThread)
2411 {
2412 if (IsPlatformMono)
1783 { 2413 {
1784 threadPoolUsed = "BuiltInThreadPool"; 2414 // This doesn't work in Mono
1785 ThreadPool.GetMaxThreads(out maxThreads, out completionPortThreads); 2415 return null;
1786 ThreadPool.GetMinThreads(out minThreads, out completionPortThreads);
1787 int availableThreads;
1788 ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads);
1789 inUseThreads = maxThreads - availableThreads;
1790 allocatedThreads = -1;
1791 waitingCallbacks = -1;
1792 } 2416 }
1793 2417
1794 if (threadPoolUsed != null) 2418 ManualResetEventSlim fallbackThreadReady = new ManualResetEventSlim();
2419 ManualResetEventSlim exitedSafely = new ManualResetEventSlim();
2420
2421 try
1795 { 2422 {
1796 sb.AppendFormat("Thread pool used : {0}\n", threadPoolUsed); 2423 new Thread(delegate()
1797 sb.AppendFormat("Max threads : {0}\n", maxThreads); 2424 {
1798 sb.AppendFormat("Min threads : {0}\n", minThreads); 2425 fallbackThreadReady.Set();
1799 sb.AppendFormat("Allocated threads : {0}\n", allocatedThreads < 0 ? "not applicable" : allocatedThreads.ToString()); 2426 while (!exitedSafely.Wait(200))
1800 sb.AppendFormat("In use threads : {0}\n", inUseThreads); 2427 {
1801 sb.AppendFormat("Work items waiting : {0}\n", waitingCallbacks < 0 ? "not available" : waitingCallbacks.ToString()); 2428 try
2429 {
2430 targetThread.Resume();
2431 }
2432 catch (Exception)
2433 {
2434 // Whatever happens, do never stop to resume the main-thread regularly until the main-thread has exited safely.
2435 }
2436 }
2437 }).Start();
2438
2439 fallbackThreadReady.Wait();
2440 // From here, you have about 200ms to get the stack-trace
2441
2442 targetThread.Suspend();
2443
2444 StackTrace trace = null;
2445 try
2446 {
2447 trace = new StackTrace(targetThread, true);
2448 }
2449 catch (ThreadStateException)
2450 {
2451 //failed to get stack trace, since the fallback-thread resumed the thread
2452 //possible reasons:
2453 //1.) This thread was just too slow
2454 //2.) A deadlock ocurred
2455 //Automatic retry seems too risky here, so just return null.
2456 }
2457
2458 try
2459 {
2460 targetThread.Resume();
2461 }
2462 catch (ThreadStateException)
2463 {
2464 // Thread is running again already
2465 }
2466
2467 return trace;
1802 } 2468 }
1803 else 2469 finally
1804 { 2470 {
1805 sb.AppendFormat("Thread pool not used\n"); 2471 // Signal the fallack-thread to stop
2472 exitedSafely.Set();
1806 } 2473 }
1807
1808 return sb.ToString();
1809 } 2474 }
2475#pragma warning restore 0618
1810 2476
1811 private static object SmartThreadPoolCallback(object o) 2477 /// <summary>
2478 /// Get information about the current state of the smart thread pool.
2479 /// </summary>
2480 /// <returns>
2481 /// null if this isn't the pool being used for non-scriptengine threads.
2482 /// </returns>
2483 public static STPInfo GetSmartThreadPoolInfo()
1812 { 2484 {
1813 object[] array = (object[])o; 2485 if (m_ThreadPool == null)
1814 WaitCallback callback = (WaitCallback)array[0]; 2486 return null;
1815 object obj = array[1];
1816 2487
1817 callback(obj); 2488 STPInfo stpi = new STPInfo();
1818 return null; 2489 stpi.Name = m_ThreadPool.Name;
2490 stpi.STPStartInfo = m_ThreadPool.STPStartInfo;
2491 stpi.IsIdle = m_ThreadPool.IsIdle;
2492 stpi.IsShuttingDown = m_ThreadPool.IsShuttingdown;
2493 stpi.MaxThreads = m_ThreadPool.MaxThreads;
2494 stpi.MinThreads = m_ThreadPool.MinThreads;
2495 stpi.InUseThreads = m_ThreadPool.InUseThreads;
2496 stpi.ActiveThreads = m_ThreadPool.ActiveThreads;
2497 stpi.WaitingCallbacks = m_ThreadPool.WaitingCallbacks;
2498 stpi.MaxConcurrentWorkItems = m_ThreadPool.Concurrency;
2499
2500 return stpi;
1819 } 2501 }
1820 2502
1821 #endregion FireAndForget Threading Pattern 2503 #endregion FireAndForget Threading Pattern
@@ -1876,6 +2558,60 @@ namespace OpenSim.Framework
1876 } 2558 }
1877 2559
1878 /// <summary> 2560 /// <summary>
2561 /// Formats a duration (given in milliseconds).
2562 /// </summary>
2563 public static string FormatDuration(int ms)
2564 {
2565 TimeSpan span = new TimeSpan(ms * TimeSpan.TicksPerMillisecond);
2566
2567 string str = "";
2568 string suffix = null;
2569
2570 int hours = (int)span.TotalHours;
2571 if (hours > 0)
2572 {
2573 str += hours.ToString(str.Length == 0 ? "0" : "00");
2574 suffix = "hours";
2575 }
2576
2577 if ((hours > 0) || (span.Minutes > 0))
2578 {
2579 if (str.Length > 0)
2580 str += ":";
2581 str += span.Minutes.ToString(str.Length == 0 ? "0" : "00");
2582 if (suffix == null)
2583 suffix = "min";
2584 }
2585
2586 if ((hours > 0) || (span.Minutes > 0) || (span.Seconds > 0))
2587 {
2588 if (str.Length > 0)
2589 str += ":";
2590 str += span.Seconds.ToString(str.Length == 0 ? "0" : "00");
2591 if (suffix == null)
2592 suffix = "sec";
2593 }
2594
2595 if (suffix == null)
2596 suffix = "ms";
2597
2598 if (span.TotalMinutes < 1)
2599 {
2600 int ms1 = span.Milliseconds;
2601 if (str.Length > 0)
2602 {
2603 ms1 /= 100;
2604 str += ".";
2605 }
2606 str += ms1.ToString("0");
2607 }
2608
2609 str += " " + suffix;
2610
2611 return str;
2612 }
2613
2614 /// <summary>
1879 /// Prints the call stack at any given point. Useful for debugging. 2615 /// Prints the call stack at any given point. Useful for debugging.
1880 /// </summary> 2616 /// </summary>
1881 public static void PrintCallStack() 2617 public static void PrintCallStack()
@@ -1942,16 +2678,18 @@ namespace OpenSim.Framework
1942 } 2678 }
1943 2679
1944 #region Xml Serialization Utilities 2680 #region Xml Serialization Utilities
1945 public static bool ReadBoolean(XmlTextReader reader) 2681 public static bool ReadBoolean(XmlReader reader)
1946 { 2682 {
2683 // AuroraSim uses "int" for some fields that are boolean in OpenSim, e.g. "PassCollisions". Don't fail because of this.
1947 reader.ReadStartElement(); 2684 reader.ReadStartElement();
1948 bool result = Boolean.Parse(reader.ReadContentAsString().ToLower()); 2685 string val = reader.ReadContentAsString().ToLower();
2686 bool result = val.Equals("true") || val.Equals("1");
1949 reader.ReadEndElement(); 2687 reader.ReadEndElement();
1950 2688
1951 return result; 2689 return result;
1952 } 2690 }
1953 2691
1954 public static UUID ReadUUID(XmlTextReader reader, string name) 2692 public static UUID ReadUUID(XmlReader reader, string name)
1955 { 2693 {
1956 UUID id; 2694 UUID id;
1957 string idStr; 2695 string idStr;
@@ -1970,7 +2708,7 @@ namespace OpenSim.Framework
1970 return id; 2708 return id;
1971 } 2709 }
1972 2710
1973 public static Vector3 ReadVector(XmlTextReader reader, string name) 2711 public static Vector3 ReadVector(XmlReader reader, string name)
1974 { 2712 {
1975 Vector3 vec; 2713 Vector3 vec;
1976 2714
@@ -1983,7 +2721,7 @@ namespace OpenSim.Framework
1983 return vec; 2721 return vec;
1984 } 2722 }
1985 2723
1986 public static Quaternion ReadQuaternion(XmlTextReader reader, string name) 2724 public static Quaternion ReadQuaternion(XmlReader reader, string name)
1987 { 2725 {
1988 Quaternion quat = new Quaternion(); 2726 Quaternion quat = new Quaternion();
1989 2727
@@ -2012,7 +2750,7 @@ namespace OpenSim.Framework
2012 return quat; 2750 return quat;
2013 } 2751 }
2014 2752
2015 public static T ReadEnum<T>(XmlTextReader reader, string name) 2753 public static T ReadEnum<T>(XmlReader reader, string name)
2016 { 2754 {
2017 string value = reader.ReadElementContentAsString(name, String.Empty); 2755 string value = reader.ReadElementContentAsString(name, String.Empty);
2018 // !!!!! to deal with flags without commas 2756 // !!!!! to deal with flags without commas
@@ -2024,7 +2762,9 @@ namespace OpenSim.Framework
2024 #endregion 2762 #endregion
2025 2763
2026 #region Universal User Identifiers 2764 #region Universal User Identifiers
2027 /// <summary> 2765
2766 /// <summary>
2767 /// Attempts to parse a UUI into its components: UUID, name and URL.
2028 /// </summary> 2768 /// </summary>
2029 /// <param name="value">uuid[;endpoint[;first last[;secret]]]</param> 2769 /// <param name="value">uuid[;endpoint[;first last[;secret]]]</param>
2030 /// <param name="uuid">the uuid part</param> 2770 /// <param name="uuid">the uuid part</param>
@@ -2034,7 +2774,7 @@ namespace OpenSim.Framework
2034 /// <param name="secret">the secret part</param> 2774 /// <param name="secret">the secret part</param>
2035 public static bool ParseUniversalUserIdentifier(string value, out UUID uuid, out string url, out string firstname, out string lastname, out string secret) 2775 public static bool ParseUniversalUserIdentifier(string value, out UUID uuid, out string url, out string firstname, out string lastname, out string secret)
2036 { 2776 {
2037 uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "User"; secret = string.Empty; 2777 uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "UserUPUUI"; secret = string.Empty;
2038 2778
2039 string[] parts = value.Split(';'); 2779 string[] parts = value.Split(';');
2040 if (parts.Length >= 1) 2780 if (parts.Length >= 1)
@@ -2060,6 +2800,27 @@ namespace OpenSim.Framework
2060 } 2800 }
2061 2801
2062 /// <summary> 2802 /// <summary>
2803 /// For foreign avatars, extracts their original name and Server URL from their First Name and Last Name.
2804 /// </summary>
2805 public static bool ParseForeignAvatarName(string firstname, string lastname,
2806 out string realFirstName, out string realLastName, out string serverURI)
2807 {
2808 realFirstName = realLastName = serverURI = string.Empty;
2809
2810 if (!lastname.Contains("@"))
2811 return false;
2812
2813 if (!firstname.Contains("."))
2814 return false;
2815
2816 realFirstName = firstname.Split('.')[0];
2817 realLastName = firstname.Split('.')[1];
2818 serverURI = new Uri("http://" + lastname.Replace("@", "")).ToString();
2819
2820 return true;
2821 }
2822
2823 /// <summary>
2063 /// Produces a universal (HG) system-facing identifier given the information 2824 /// Produces a universal (HG) system-facing identifier given the information
2064 /// </summary> 2825 /// </summary>
2065 /// <param name="acircuit"></param> 2826 /// <param name="acircuit"></param>
@@ -2092,10 +2853,15 @@ namespace OpenSim.Framework
2092 { 2853 {
2093 string[] parts = firstName.Split(new char[] { '.' }); 2854 string[] parts = firstName.Split(new char[] { '.' });
2094 if (parts.Length == 2) 2855 if (parts.Length == 2)
2095 return id.ToString() + ";" + agentsURI + ";" + parts[0] + " " + parts[1]; 2856 return CalcUniversalIdentifier(id, agentsURI, parts[0] + " " + parts[1]);
2096 } 2857 }
2097 return id.ToString() + ";" + agentsURI + ";" + firstName + " " + lastName; 2858
2859 return CalcUniversalIdentifier(id, agentsURI, firstName + " " + lastName);
2860 }
2098 2861
2862 private static string CalcUniversalIdentifier(UUID id, string agentsURI, string name)
2863 {
2864 return id.ToString() + ";" + agentsURI + ";" + name;
2099 } 2865 }
2100 2866
2101 /// <summary> 2867 /// <summary>
@@ -2119,5 +2885,221 @@ namespace OpenSim.Framework
2119 return firstName + "." + lastName + " " + "@" + uri.Authority; 2885 return firstName + "." + lastName + " " + "@" + uri.Authority;
2120 } 2886 }
2121 #endregion 2887 #endregion
2888
2889 /// <summary>
2890 /// Escapes the special characters used in "LIKE".
2891 /// </summary>
2892 /// <remarks>
2893 /// For example: EscapeForLike("foo_bar%baz") = "foo\_bar\%baz"
2894 /// </remarks>
2895 public static string EscapeForLike(string str)
2896 {
2897 return str.Replace("_", "\\_").Replace("%", "\\%");
2898 }
2899
2900 /// <summary>
2901 /// Returns the name of the user's viewer.
2902 /// </summary>
2903 /// <remarks>
2904 /// This method handles two ways that viewers specify their name:
2905 /// 1. Viewer = "Firestorm-Release 4.4.2.34167", Channel = "(don't care)" -> "Firestorm-Release 4.4.2.34167"
2906 /// 2. Viewer = "4.5.1.38838", Channel = "Firestorm-Beta" -> "Firestorm-Beta 4.5.1.38838"
2907 /// </remarks>
2908 public static string GetViewerName(AgentCircuitData agent)
2909 {
2910 string name = agent.Viewer;
2911 if (name == null)
2912 name = "";
2913 else
2914 name = name.Trim();
2915
2916 // Check if 'Viewer' is just a version number. If it's *not*, then we
2917 // assume that it contains the real viewer name, and we return it.
2918 foreach (char c in name)
2919 {
2920 if (Char.IsLetter(c))
2921 return name;
2922 }
2923
2924 // The 'Viewer' string contains just a version number. If there's anything in
2925 // 'Channel' then assume that it's the viewer name.
2926 if ((agent.Channel != null) && (agent.Channel.Length > 0))
2927 name = agent.Channel.Trim() + " " + name;
2928
2929 return name;
2930 }
2931
2932 public static void LogFailedXML(string message, string xml)
2933 {
2934 int length = xml.Length;
2935 if (length > 2000)
2936 xml = xml.Substring(0, 2000) + "...";
2937
2938 m_log.ErrorFormat("{0} Failed XML ({1} bytes) = {2}", message, length, xml);
2939 }
2940
2941 }
2942
2943 public class DoubleQueue<T> where T:class
2944 {
2945 private Queue<T> m_lowQueue = new Queue<T>();
2946 private Queue<T> m_highQueue = new Queue<T>();
2947
2948 private object m_syncRoot = new object();
2949 private Semaphore m_s = new Semaphore(0, 1);
2950
2951 public DoubleQueue()
2952 {
2953 }
2954
2955 public virtual int Count
2956 {
2957 get
2958 {
2959 lock (m_syncRoot)
2960 return m_highQueue.Count + m_lowQueue.Count;
2961 }
2962 }
2963
2964 public virtual void Enqueue(T data)
2965 {
2966 Enqueue(m_lowQueue, data);
2967 }
2968
2969 public virtual void EnqueueLow(T data)
2970 {
2971 Enqueue(m_lowQueue, data);
2972 }
2973
2974 public virtual void EnqueueHigh(T data)
2975 {
2976 Enqueue(m_highQueue, data);
2977 }
2978
2979 private void Enqueue(Queue<T> q, T data)
2980 {
2981 lock (m_syncRoot)
2982 {
2983 q.Enqueue(data);
2984 m_s.WaitOne(0);
2985 m_s.Release();
2986 }
2987 }
2988
2989 public virtual T Dequeue()
2990 {
2991 return Dequeue(Timeout.Infinite);
2992 }
2993
2994 public virtual T Dequeue(int tmo)
2995 {
2996 return Dequeue(TimeSpan.FromMilliseconds(tmo));
2997 }
2998
2999 public virtual T Dequeue(TimeSpan wait)
3000 {
3001 T res = null;
3002
3003 if (!Dequeue(wait, ref res))
3004 return null;
3005
3006 return res;
3007 }
3008
3009 public bool Dequeue(int timeout, ref T res)
3010 {
3011 return Dequeue(TimeSpan.FromMilliseconds(timeout), ref res);
3012 }
3013
3014 public bool Dequeue(TimeSpan wait, ref T res)
3015 {
3016 if (!m_s.WaitOne(wait))
3017 return false;
3018
3019 lock (m_syncRoot)
3020 {
3021 if (m_highQueue.Count > 0)
3022 res = m_highQueue.Dequeue();
3023 else if (m_lowQueue.Count > 0)
3024 res = m_lowQueue.Dequeue();
3025
3026 if (m_highQueue.Count == 0 && m_lowQueue.Count == 0)
3027 return true;
3028
3029 try
3030 {
3031 m_s.Release();
3032 }
3033 catch
3034 {
3035 }
3036
3037 return true;
3038 }
3039 }
3040
3041 public virtual void Clear()
3042 {
3043
3044 lock (m_syncRoot)
3045 {
3046 // Make sure sem count is 0
3047 m_s.WaitOne(0);
3048
3049 m_lowQueue.Clear();
3050 m_highQueue.Clear();
3051 }
3052 }
3053 }
3054
3055 public class BetterRandom
3056 {
3057 private const int BufferSize = 1024; // must be a multiple of 4
3058 private byte[] RandomBuffer;
3059 private int BufferOffset;
3060 private RNGCryptoServiceProvider rng;
3061 public BetterRandom()
3062 {
3063 RandomBuffer = new byte[BufferSize];
3064 rng = new RNGCryptoServiceProvider();
3065 BufferOffset = RandomBuffer.Length;
3066 }
3067 private void FillBuffer()
3068 {
3069 rng.GetBytes(RandomBuffer);
3070 BufferOffset = 0;
3071 }
3072 public int Next()
3073 {
3074 if (BufferOffset >= RandomBuffer.Length)
3075 {
3076 FillBuffer();
3077 }
3078 int val = BitConverter.ToInt32(RandomBuffer, BufferOffset) & 0x7fffffff;
3079 BufferOffset += sizeof(int);
3080 return val;
3081 }
3082 public int Next(int maxValue)
3083 {
3084 return Next() % maxValue;
3085 }
3086 public int Next(int minValue, int maxValue)
3087 {
3088 if (maxValue < minValue)
3089 {
3090 throw new ArgumentOutOfRangeException("maxValue must be greater than or equal to minValue");
3091 }
3092 int range = maxValue - minValue;
3093 return minValue + Next(range);
3094 }
3095 public double NextDouble()
3096 {
3097 int val = Next();
3098 return (double)val / int.MaxValue;
3099 }
3100 public void GetBytes(byte[] buff)
3101 {
3102 rng.GetBytes(buff);
3103 }
2122 } 3104 }
2123} 3105}