EventQueueThreadClass.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSim Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections;
  29. using System.Collections.Generic;
  30. using System.Reflection;
  31. using System.Text.RegularExpressions;
  32. using System.Threading;
  33. using System.Globalization;
  34. using OpenMetaverse;
  35. using log4net;
  36. using OpenSim.Framework;
  37. using OpenSim.Region.Environment.Scenes;
  38. using OpenSim.Region.Environment.Scenes.Scripting;
  39. using OpenSim.Region.ScriptEngine.Shared;
  40. using OpenSim.Region.ScriptEngine.Shared.CodeTools;
  41. namespace OpenSim.Region.ScriptEngine.DotNetEngine
  42. {
  43. // Because every thread needs some data set for it
  44. // (time started to execute current function), it will do its work
  45. // within a class
  46. public class EventQueueThreadClass
  47. {
  48. // How many ms to sleep if queue is empty
  49. private static int nothingToDoSleepms;// = 50;
  50. private static ThreadPriority MyThreadPriority;
  51. public long LastExecutionStarted;
  52. public bool InExecution = false;
  53. public bool KillCurrentScript = false;
  54. //private EventQueueManager eventQueueManager;
  55. public Thread EventQueueThread;
  56. private static int ThreadCount = 0;
  57. private string ScriptEngineName = "ScriptEngine.Common";
  58. public EventQueueThreadClass()//EventQueueManager eqm
  59. {
  60. CultureInfo USCulture = new CultureInfo("en-US");
  61. Thread.CurrentThread.CurrentCulture = USCulture;
  62. //eventQueueManager = eqm;
  63. ReadConfig();
  64. Start();
  65. }
  66. ~EventQueueThreadClass()
  67. {
  68. Stop();
  69. }
  70. public void ReadConfig()
  71. {
  72. lock (ScriptEngine.ScriptEngines)
  73. {
  74. foreach (ScriptEngine m_ScriptEngine in
  75. ScriptEngine.ScriptEngines)
  76. {
  77. ScriptEngineName = m_ScriptEngine.ScriptEngineName;
  78. nothingToDoSleepms =
  79. m_ScriptEngine.ScriptConfigSource.GetInt(
  80. "SleepTimeIfNoScriptExecutionMs", 50);
  81. string pri = m_ScriptEngine.ScriptConfigSource.GetString(
  82. "ScriptThreadPriority", "BelowNormal");
  83. switch (pri.ToLower())
  84. {
  85. case "lowest":
  86. MyThreadPriority = ThreadPriority.Lowest;
  87. break;
  88. case "belownormal":
  89. MyThreadPriority = ThreadPriority.BelowNormal;
  90. break;
  91. case "normal":
  92. MyThreadPriority = ThreadPriority.Normal;
  93. break;
  94. case "abovenormal":
  95. MyThreadPriority = ThreadPriority.AboveNormal;
  96. break;
  97. case "highest":
  98. MyThreadPriority = ThreadPriority.Highest;
  99. break;
  100. default:
  101. MyThreadPriority = ThreadPriority.BelowNormal;
  102. m_ScriptEngine.Log.Error(
  103. "[ScriptEngine.DotNetEngine]: Unknown "+
  104. "priority type \"" + pri +
  105. "\" in config file. Defaulting to "+
  106. "\"BelowNormal\".");
  107. break;
  108. }
  109. }
  110. }
  111. // Now set that priority
  112. if (EventQueueThread != null)
  113. if (EventQueueThread.IsAlive)
  114. EventQueueThread.Priority = MyThreadPriority;
  115. }
  116. /// <summary>
  117. /// Start thread
  118. /// </summary>
  119. private void Start()
  120. {
  121. EventQueueThread = new Thread(EventQueueThreadLoop);
  122. EventQueueThread.IsBackground = true;
  123. EventQueueThread.Priority = MyThreadPriority;
  124. EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount;
  125. EventQueueThread.Start();
  126. ThreadTracker.Add(EventQueueThread);
  127. // Look at this... Don't you wish everyone did that solid
  128. // coding everywhere? :P
  129. if (ThreadCount == int.MaxValue)
  130. ThreadCount = 0;
  131. ThreadCount++;
  132. }
  133. public void Stop()
  134. {
  135. if (EventQueueThread != null && EventQueueThread.IsAlive == true)
  136. {
  137. try
  138. {
  139. EventQueueThread.Abort(); // Send abort
  140. }
  141. catch (Exception)
  142. {
  143. }
  144. }
  145. }
  146. private EventQueueManager.QueueItemStruct BlankQIS =
  147. new EventQueueManager.QueueItemStruct();
  148. private ScriptEngine lastScriptEngine;
  149. private uint lastLocalID;
  150. // Queue processing thread loop
  151. private void EventQueueThreadLoop()
  152. {
  153. CultureInfo USCulture = new CultureInfo("en-US");
  154. Thread.CurrentThread.CurrentCulture = USCulture;
  155. try
  156. {
  157. while (true)
  158. {
  159. try
  160. {
  161. while (true)
  162. {
  163. DoProcessQueue();
  164. }
  165. }
  166. catch (ThreadAbortException)
  167. {
  168. if (lastScriptEngine != null)
  169. lastScriptEngine.Log.Info("[" + ScriptEngineName +
  170. "]: ThreadAbortException while executing "+
  171. "function.");
  172. }
  173. catch (SelfDeleteException) // Must delete SOG
  174. {
  175. SceneObjectPart part =
  176. lastScriptEngine.World.GetSceneObjectPart(
  177. lastLocalID);
  178. if (part != null && part.ParentGroup != null)
  179. lastScriptEngine.World.DeleteSceneObject(
  180. part.ParentGroup, false);
  181. }
  182. catch (Exception e)
  183. {
  184. if (lastScriptEngine != null)
  185. lastScriptEngine.Log.ErrorFormat("[{0}]: Exception {1} thrown", ScriptEngineName, e.GetType().ToString());
  186. throw e;
  187. }
  188. }
  189. }
  190. catch (ThreadAbortException)
  191. {
  192. }
  193. catch (Exception e)
  194. {
  195. // TODO: Let users in the sim and those entering it and possibly an external watchdog know what has happened
  196. if (lastScriptEngine != null)
  197. lastScriptEngine.Log.ErrorFormat(
  198. "[{0}]: Event queue thread terminating with exception. PLEASE REBOOT YOUR SIM - SCRIPT EVENTS WILL NOT WORK UNTIL YOU DO. Exception is {1}",
  199. ScriptEngineName, e);
  200. }
  201. }
  202. public void DoProcessQueue()
  203. {
  204. foreach (ScriptEngine m_ScriptEngine in
  205. new ArrayList(ScriptEngine.ScriptEngines))
  206. {
  207. lastScriptEngine = m_ScriptEngine;
  208. EventQueueManager.QueueItemStruct QIS = BlankQIS;
  209. bool GotItem = false;
  210. //if (PleaseShutdown)
  211. // return;
  212. if (m_ScriptEngine.m_EventQueueManager == null ||
  213. m_ScriptEngine.m_EventQueueManager.eventQueue == null)
  214. continue;
  215. if (m_ScriptEngine.m_EventQueueManager.eventQueue.Count == 0)
  216. {
  217. // Nothing to do? Sleep a bit waiting for something to do
  218. Thread.Sleep(nothingToDoSleepms);
  219. }
  220. else
  221. {
  222. // Something in queue, process
  223. // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME
  224. // OBJECT IS NOT GOOD
  225. lock (m_ScriptEngine.m_EventQueueManager.eventQueue)
  226. {
  227. GotItem = false;
  228. for (int qc = 0; qc < m_ScriptEngine.m_EventQueueManager.eventQueue.Count; qc++)
  229. {
  230. // Get queue item
  231. QIS = m_ScriptEngine.m_EventQueueManager.eventQueue.Dequeue();
  232. // Check if object is being processed by
  233. // someone else
  234. if (m_ScriptEngine.m_EventQueueManager.TryLock(
  235. QIS.localID) == false)
  236. {
  237. // Object is already being processed, requeue it
  238. m_ScriptEngine.m_EventQueueManager.
  239. eventQueue.Enqueue(QIS);
  240. }
  241. else
  242. {
  243. // We have lock on an object and can process it
  244. GotItem = true;
  245. break;
  246. }
  247. }
  248. }
  249. if (GotItem == true)
  250. {
  251. // Execute function
  252. try
  253. {
  254. // Only pipe event if land supports it.
  255. if (m_ScriptEngine.World.PipeEventsForScript(
  256. QIS.localID))
  257. {
  258. lastLocalID = QIS.localID;
  259. LastExecutionStarted = DateTime.Now.Ticks;
  260. KillCurrentScript = false;
  261. InExecution = true;
  262. m_ScriptEngine.m_ScriptManager.ExecuteEvent(
  263. QIS.localID,
  264. QIS.itemID,
  265. QIS.functionName,
  266. QIS.llDetectParams,
  267. QIS.param);
  268. InExecution = false;
  269. }
  270. }
  271. catch (TargetInvocationException tie)
  272. {
  273. Exception e = tie.InnerException;
  274. if (e is SelfDeleteException) // Forward it
  275. throw e;
  276. InExecution = false;
  277. string text = FormatException(tie, QIS.LineMap);
  278. // DISPLAY ERROR INWORLD
  279. // if (e.InnerException != null)
  280. // {
  281. // // Send inner exception
  282. // string line = " (unknown line)";
  283. // Regex rx = new Regex(@"SecondLife\.Script\..+[\s:](?<line>\d+)\.?\r?$", RegexOptions.Compiled);
  284. // if (rx.Match(e.InnerException.ToString()).Success)
  285. // line = " (line " + rx.Match(e.InnerException.ToString()).Result("${line}") + ")";
  286. // text += e.InnerException.Message.ToString() + line;
  287. // }
  288. // else
  289. // {
  290. // text += "\r\n";
  291. // // Send normal
  292. // text += e.Message.ToString();
  293. // }
  294. // if (KillCurrentScript)
  295. // text += "\r\nScript will be deactivated!";
  296. try
  297. {
  298. if (text.Length >= 1100)
  299. text = text.Substring(0, 1099);
  300. IScriptHost m_host =
  301. m_ScriptEngine.World.GetSceneObjectPart(QIS.localID);
  302. m_ScriptEngine.World.SimChat(
  303. Utils.StringToBytes(text),
  304. ChatTypeEnum.DebugChannel, 2147483647,
  305. m_host.AbsolutePosition,
  306. m_host.Name, m_host.UUID, false);
  307. }
  308. catch (Exception)
  309. {
  310. m_ScriptEngine.m_EventQueueManager.
  311. m_ScriptEngine.Log.Error("[" +
  312. ScriptEngineName + "]: " +
  313. "Unable to send text in-world:\r\n" +
  314. text);
  315. }
  316. finally
  317. {
  318. // So we are done sending message in-world
  319. if (KillCurrentScript)
  320. {
  321. m_ScriptEngine.m_EventQueueManager.
  322. m_ScriptEngine.m_ScriptManager.
  323. StopScript(
  324. QIS.localID, QIS.itemID);
  325. }
  326. }
  327. }
  328. catch (Exception)
  329. {
  330. throw;
  331. }
  332. finally
  333. {
  334. InExecution = false;
  335. m_ScriptEngine.m_EventQueueManager.ReleaseLock(
  336. QIS.localID);
  337. }
  338. }
  339. }
  340. }
  341. }
  342. string FormatException(Exception e, Dictionary<KeyValuePair<int,int>,
  343. KeyValuePair<int,int>> LineMap)
  344. {
  345. if (e.InnerException == null)
  346. return e.ToString();
  347. string message = "Runtime error:\n" + e.InnerException.StackTrace;
  348. string[] lines = message.Split(new char[] {'\n'});
  349. foreach (string line in lines)
  350. {
  351. if (line.Contains("SecondLife.Script"))
  352. {
  353. int idx = line.IndexOf(':');
  354. if (idx != -1)
  355. {
  356. string val = line.Substring(idx+1);
  357. int lineNum = 0;
  358. if (int.TryParse(val, out lineNum))
  359. {
  360. KeyValuePair<int, int> pos =
  361. Compiler.FindErrorPosition(
  362. lineNum, 0, LineMap);
  363. int scriptLine = pos.Key;
  364. int col = pos.Value;
  365. if (scriptLine == 0)
  366. scriptLine++;
  367. if (col == 0)
  368. col++;
  369. message = string.Format("Runtime error:\n" +
  370. "Line ({0}): {1}", scriptLine - 1,
  371. e.InnerException.Message);
  372. return message;
  373. }
  374. }
  375. }
  376. }
  377. return message;
  378. }
  379. }
  380. }