EventQueueThreadClass.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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.Framework.Scenes;
  38. using OpenSim.Region.Framework.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. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  49. // How many ms to sleep if queue is empty
  50. private static int nothingToDoSleepms;// = 50;
  51. private static ThreadPriority MyThreadPriority;
  52. public long LastExecutionStarted;
  53. public bool InExecution = false;
  54. public bool KillCurrentScript = false;
  55. //private EventQueueManager eventQueueManager;
  56. public Thread EventQueueThread;
  57. private static int ThreadCount = 0;
  58. private string ScriptEngineName = "ScriptEngine.Common";
  59. public EventQueueThreadClass()//EventQueueManager eqm
  60. {
  61. CultureInfo USCulture = new CultureInfo("en-US");
  62. Thread.CurrentThread.CurrentCulture = USCulture;
  63. //eventQueueManager = eqm;
  64. ReadConfig();
  65. Start();
  66. }
  67. ~EventQueueThreadClass()
  68. {
  69. Stop();
  70. }
  71. public void ReadConfig()
  72. {
  73. lock (ScriptEngine.ScriptEngines)
  74. {
  75. foreach (ScriptEngine m_ScriptEngine in
  76. ScriptEngine.ScriptEngines)
  77. {
  78. ScriptEngineName = m_ScriptEngine.ScriptEngineName;
  79. nothingToDoSleepms =
  80. m_ScriptEngine.ScriptConfigSource.GetInt(
  81. "SleepTimeIfNoScriptExecutionMs", 50);
  82. string pri = m_ScriptEngine.ScriptConfigSource.GetString(
  83. "ScriptThreadPriority", "BelowNormal");
  84. switch (pri.ToLower())
  85. {
  86. case "lowest":
  87. MyThreadPriority = ThreadPriority.Lowest;
  88. break;
  89. case "belownormal":
  90. MyThreadPriority = ThreadPriority.BelowNormal;
  91. break;
  92. case "normal":
  93. MyThreadPriority = ThreadPriority.Normal;
  94. break;
  95. case "abovenormal":
  96. MyThreadPriority = ThreadPriority.AboveNormal;
  97. break;
  98. case "highest":
  99. MyThreadPriority = ThreadPriority.Highest;
  100. break;
  101. default:
  102. MyThreadPriority = ThreadPriority.BelowNormal;
  103. m_log.Error(
  104. "[ScriptEngine.DotNetEngine]: Unknown "+
  105. "priority type \"" + pri +
  106. "\" in config file. Defaulting to "+
  107. "\"BelowNormal\".");
  108. break;
  109. }
  110. }
  111. }
  112. // Now set that priority
  113. if (EventQueueThread != null)
  114. if (EventQueueThread.IsAlive)
  115. EventQueueThread.Priority = MyThreadPriority;
  116. }
  117. /// <summary>
  118. /// Start thread
  119. /// </summary>
  120. private void Start()
  121. {
  122. EventQueueThread = new Thread(EventQueueThreadLoop);
  123. EventQueueThread.IsBackground = true;
  124. EventQueueThread.Priority = MyThreadPriority;
  125. EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount;
  126. EventQueueThread.Start();
  127. ThreadTracker.Add(EventQueueThread);
  128. // Look at this... Don't you wish everyone did that solid
  129. // coding everywhere? :P
  130. if (ThreadCount == int.MaxValue)
  131. ThreadCount = 0;
  132. ThreadCount++;
  133. }
  134. public void Stop()
  135. {
  136. if (EventQueueThread != null && EventQueueThread.IsAlive == true)
  137. {
  138. try
  139. {
  140. EventQueueThread.Abort(); // Send abort
  141. }
  142. catch (Exception)
  143. {
  144. }
  145. }
  146. }
  147. private EventQueueManager.QueueItemStruct BlankQIS =
  148. new EventQueueManager.QueueItemStruct();
  149. private ScriptEngine lastScriptEngine;
  150. private uint lastLocalID;
  151. private UUID lastItemID;
  152. // Queue processing thread loop
  153. private void EventQueueThreadLoop()
  154. {
  155. CultureInfo USCulture = new CultureInfo("en-US");
  156. Thread.CurrentThread.CurrentCulture = USCulture;
  157. try
  158. {
  159. while (true)
  160. {
  161. try
  162. {
  163. while (true)
  164. {
  165. DoProcessQueue();
  166. }
  167. }
  168. catch (ThreadAbortException)
  169. {
  170. m_log.Info("[" + ScriptEngineName +
  171. "]: ThreadAbortException while executing "+
  172. "function.");
  173. }
  174. catch (SelfDeleteException) // Must delete SOG
  175. {
  176. SceneObjectPart part =
  177. lastScriptEngine.World.GetSceneObjectPart(
  178. lastLocalID);
  179. if (part != null && part.ParentGroup != null)
  180. lastScriptEngine.World.DeleteSceneObject(
  181. part.ParentGroup, false);
  182. }
  183. catch (ScriptDeleteException) // Must delete item
  184. {
  185. SceneObjectPart part =
  186. lastScriptEngine.World.GetSceneObjectPart(
  187. lastLocalID);
  188. if (part != null && part.ParentGroup != null)
  189. part.Inventory.RemoveInventoryItem(lastItemID);
  190. }
  191. catch (Exception e)
  192. {
  193. m_log.ErrorFormat("[{0}]: Exception {1} thrown", ScriptEngineName, e.GetType().ToString());
  194. throw e;
  195. }
  196. }
  197. }
  198. catch (ThreadAbortException)
  199. {
  200. }
  201. catch (Exception e)
  202. {
  203. // TODO: Let users in the sim and those entering it and possibly an external watchdog know what has happened
  204. m_log.ErrorFormat(
  205. "[{0}]: Event queue thread terminating with exception. PLEASE REBOOT YOUR SIM - SCRIPT EVENTS WILL NOT WORK UNTIL YOU DO. Exception is {1}",
  206. ScriptEngineName, e);
  207. }
  208. }
  209. public void DoProcessQueue()
  210. {
  211. foreach (ScriptEngine m_ScriptEngine in
  212. new ArrayList(ScriptEngine.ScriptEngines))
  213. {
  214. lastScriptEngine = m_ScriptEngine;
  215. EventQueueManager.QueueItemStruct QIS = BlankQIS;
  216. bool GotItem = false;
  217. //if (PleaseShutdown)
  218. // return;
  219. if (m_ScriptEngine.m_EventQueueManager == null ||
  220. m_ScriptEngine.m_EventQueueManager.eventQueue == null)
  221. continue;
  222. if (m_ScriptEngine.m_EventQueueManager.eventQueue.Count == 0)
  223. {
  224. // Nothing to do? Sleep a bit waiting for something to do
  225. Thread.Sleep(nothingToDoSleepms);
  226. }
  227. else
  228. {
  229. // Something in queue, process
  230. // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME
  231. // OBJECT IS NOT GOOD
  232. lock (m_ScriptEngine.m_EventQueueManager.eventQueue)
  233. {
  234. GotItem = false;
  235. for (int qc = 0; qc < m_ScriptEngine.m_EventQueueManager.eventQueue.Count; qc++)
  236. {
  237. // Get queue item
  238. QIS = m_ScriptEngine.m_EventQueueManager.eventQueue.Dequeue();
  239. // Check if object is being processed by
  240. // someone else
  241. if (m_ScriptEngine.m_EventQueueManager.TryLock(
  242. QIS.localID) == false)
  243. {
  244. // Object is already being processed, requeue it
  245. m_ScriptEngine.m_EventQueueManager.
  246. eventQueue.Enqueue(QIS);
  247. }
  248. else
  249. {
  250. // We have lock on an object and can process it
  251. GotItem = true;
  252. break;
  253. }
  254. }
  255. }
  256. if (GotItem == true)
  257. {
  258. // Execute function
  259. try
  260. {
  261. // Only pipe event if land supports it.
  262. if (m_ScriptEngine.World.PipeEventsForScript(
  263. QIS.localID))
  264. {
  265. lastLocalID = QIS.localID;
  266. lastItemID = QIS.itemID;
  267. LastExecutionStarted = DateTime.Now.Ticks;
  268. KillCurrentScript = false;
  269. InExecution = true;
  270. m_ScriptEngine.m_ScriptManager.ExecuteEvent(
  271. QIS.localID,
  272. QIS.itemID,
  273. QIS.functionName,
  274. QIS.llDetectParams,
  275. QIS.param);
  276. InExecution = false;
  277. }
  278. }
  279. catch (TargetInvocationException tie)
  280. {
  281. Exception e = tie.InnerException;
  282. if (e is SelfDeleteException) // Forward it
  283. throw e;
  284. InExecution = false;
  285. string text = FormatException(tie, QIS.LineMap);
  286. // DISPLAY ERROR INWORLD
  287. // if (e.InnerException != null)
  288. // {
  289. // // Send inner exception
  290. // string line = " (unknown line)";
  291. // Regex rx = new Regex(@"SecondLife\.Script\..+[\s:](?<line>\d+)\.?\r?$", RegexOptions.Compiled);
  292. // if (rx.Match(e.InnerException.ToString()).Success)
  293. // line = " (line " + rx.Match(e.InnerException.ToString()).Result("${line}") + ")";
  294. // text += e.InnerException.Message.ToString() + line;
  295. // }
  296. // else
  297. // {
  298. // text += "\r\n";
  299. // // Send normal
  300. // text += e.Message.ToString();
  301. // }
  302. // if (KillCurrentScript)
  303. // text += "\r\nScript will be deactivated!";
  304. try
  305. {
  306. if (text.Length >= 1100)
  307. text = text.Substring(0, 1099);
  308. IScriptHost m_host =
  309. m_ScriptEngine.World.GetSceneObjectPart(QIS.localID);
  310. m_ScriptEngine.World.SimChat(
  311. Utils.StringToBytes(text),
  312. ChatTypeEnum.DebugChannel, 2147483647,
  313. m_host.AbsolutePosition,
  314. m_host.Name, m_host.UUID, false);
  315. }
  316. catch (Exception)
  317. {
  318. m_log.Error("[" +
  319. ScriptEngineName + "]: " +
  320. "Unable to send text in-world:\r\n" +
  321. text);
  322. }
  323. finally
  324. {
  325. // So we are done sending message in-world
  326. if (KillCurrentScript)
  327. {
  328. m_ScriptEngine.m_EventQueueManager.
  329. m_ScriptEngine.m_ScriptManager.
  330. StopScript(
  331. QIS.localID, QIS.itemID);
  332. }
  333. }
  334. }
  335. catch (Exception)
  336. {
  337. throw;
  338. }
  339. finally
  340. {
  341. InExecution = false;
  342. m_ScriptEngine.m_EventQueueManager.ReleaseLock(
  343. QIS.localID);
  344. }
  345. }
  346. }
  347. }
  348. }
  349. string FormatException(Exception e, Dictionary<KeyValuePair<int,int>,
  350. KeyValuePair<int,int>> LineMap)
  351. {
  352. if (e.InnerException == null)
  353. return e.ToString();
  354. string message = "Runtime error:\n" + e.InnerException.StackTrace;
  355. string[] lines = message.Split(new char[] {'\n'});
  356. foreach (string line in lines)
  357. {
  358. if (line.Contains("SecondLife.Script"))
  359. {
  360. int idx = line.IndexOf(':');
  361. if (idx != -1)
  362. {
  363. string val = line.Substring(idx+1);
  364. int lineNum = 0;
  365. if (int.TryParse(val, out lineNum))
  366. {
  367. KeyValuePair<int, int> pos =
  368. Compiler.FindErrorPosition(
  369. lineNum, 0, LineMap);
  370. int scriptLine = pos.Key;
  371. int col = pos.Value;
  372. if (scriptLine == 0)
  373. scriptLine++;
  374. if (col == 0)
  375. col++;
  376. message = string.Format("Runtime error:\n" +
  377. "Line ({0}): {1}", scriptLine - 1,
  378. e.InnerException.Message);
  379. return message;
  380. }
  381. }
  382. }
  383. }
  384. return message;
  385. }
  386. }
  387. }