diff options
author | David Walter Seikel | 2016-11-03 21:44:39 +1000 |
---|---|---|
committer | David Walter Seikel | 2016-11-03 21:44:39 +1000 |
commit | 134f86e8d5c414409631b25b8c6f0ee45fbd8631 (patch) | |
tree | 216b89d3fb89acfb81be1e440c25c41ab09fa96d /OpenSim/Framework/Util.cs | |
parent | More changing to production grid. Double oops. (diff) | |
download | opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.zip opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.gz opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.bz2 opensim-SC_OLD-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.cs | 1510 |
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; | |||
45 | using System.Xml; | 45 | using System.Xml; |
46 | using System.Threading; | 46 | using System.Threading; |
47 | using log4net; | 47 | using log4net; |
48 | using log4net.Appender; | ||
48 | using Nini.Config; | 49 | using Nini.Config; |
49 | using Nwc.XmlRpc; | 50 | using Nwc.XmlRpc; |
50 | using OpenMetaverse; | 51 | using OpenMetaverse; |
51 | using OpenMetaverse.StructuredData; | 52 | using OpenMetaverse.StructuredData; |
52 | using Amib.Threading; | 53 | using Amib.Threading; |
54 | using System.Collections.Concurrent; | ||
55 | using System.Collections.Specialized; | ||
56 | using System.Web; | ||
53 | 57 | ||
54 | namespace OpenSim.Framework | 58 | namespace 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 | } |