XMRInstRun.cs 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Threading;
  29. using System.Collections.Generic;
  30. using System.Text;
  31. using OpenMetaverse;
  32. using OpenSim.Framework;
  33. using OpenSim.Region.Framework.Interfaces;
  34. using OpenSim.Region.ScriptEngine.Shared;
  35. using OpenSim.Region.ScriptEngine.Shared.Api;
  36. using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
  37. using OpenSim.Region.Framework.Scenes;
  38. using log4net;
  39. using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
  40. using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
  41. using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
  42. using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
  43. using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
  44. using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
  45. using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
  46. namespace OpenSim.Region.ScriptEngine.Yengine
  47. {
  48. public partial class XMRInstance
  49. {
  50. /************************************************************************************\
  51. * This module contains these externally useful methods: *
  52. * PostEvent() - queues an event to script and wakes script thread to process it *
  53. * RunOne() - runs script for a time slice or until it volunteers to give up cpu *
  54. * CallSEH() - runs in the microthread to call the event handler *
  55. \************************************************************************************/
  56. /**
  57. * @brief This can be called in any thread (including the script thread itself)
  58. * to queue event to script for processing.
  59. */
  60. public void PostEvent(EventParams evt)
  61. {
  62. ScriptEventCode evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode),
  63. evt.EventName);
  64. // Put event on end of event queue.
  65. bool startIt = false;
  66. bool wakeIt = false;
  67. lock(m_QueueLock)
  68. {
  69. bool construct = (m_IState == XMRInstState.CONSTRUCT);
  70. // Ignore event if we don't even have such an handler in any state.
  71. // We can't be state-specific here because state might be different
  72. // by the time this event is dequeued and delivered to the script.
  73. if(!construct && // make sure m_HaveEventHandlers is filled in
  74. ((uint)evc < (uint)m_HaveEventHandlers.Length) &&
  75. !m_HaveEventHandlers[(int)evc]) // don't bother if we don't have such a handler in any state
  76. return;
  77. // Not running means we ignore any incoming events.
  78. // But queue if still constructing because m_Running is not yet valid.
  79. if(!m_Running && !construct)
  80. return;
  81. // Only so many of each event type allowed to queue.
  82. if((uint)evc < (uint)m_EventCounts.Length)
  83. {
  84. if(evc == ScriptEventCode.timer)
  85. {
  86. if(m_EventCounts[(int)evc] >= 1)
  87. return;
  88. }
  89. else if(m_EventCounts[(int)evc] >= MAXEVENTQUEUE)
  90. return;
  91. m_EventCounts[(int)evc]++;
  92. }
  93. // Put event on end of instance's event queue.
  94. LinkedListNode<EventParams> lln = new LinkedListNode<EventParams>(evt);
  95. switch(evc)
  96. {
  97. // These need to go first. The only time we manually
  98. // queue them is for the default state_entry() and we
  99. // need to make sure they go before any attach() events
  100. // so the heapLimit value gets properly initialized.
  101. case ScriptEventCode.state_entry:
  102. m_EventQueue.AddFirst(lln);
  103. break;
  104. // The attach event sneaks to the front of the queue.
  105. // This is needed for quantum limiting to work because
  106. // we want the attach(NULL_KEY) event to come in front
  107. // of all others so the m_DetachQuantum won't run out
  108. // before attach(NULL_KEY) is executed.
  109. case ScriptEventCode.attach:
  110. if(evt.Params[0].ToString() == UUID.Zero.ToString())
  111. {
  112. LinkedListNode<EventParams> lln2 = null;
  113. for(lln2 = m_EventQueue.First; lln2 != null; lln2 = lln2.Next)
  114. {
  115. EventParams evt2 = lln2.Value;
  116. ScriptEventCode evc2 = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode),
  117. evt2.EventName);
  118. if((evc2 != ScriptEventCode.state_entry) &&
  119. (evc2 != ScriptEventCode.attach))
  120. break;
  121. }
  122. if(lln2 == null)
  123. m_EventQueue.AddLast(lln);
  124. else
  125. m_EventQueue.AddBefore(lln2, lln);
  126. // If we're detaching, limit the qantum. This will also
  127. // cause the script to self-suspend after running this
  128. // event
  129. m_DetachReady.Reset();
  130. m_DetachQuantum = 100;
  131. }
  132. else
  133. m_EventQueue.AddLast(lln);
  134. break;
  135. // All others just go on end in the order queued.
  136. default:
  137. m_EventQueue.AddLast(lln);
  138. break;
  139. }
  140. // If instance is idle (ie, not running or waiting to run),
  141. // flag it to be on m_StartQueue as we are about to do so.
  142. // Flag it now before unlocking so another thread won't try
  143. // to do the same thing right now.
  144. // Dont' flag it if it's still suspended!
  145. if((m_IState == XMRInstState.IDLE) && !m_Suspended)
  146. {
  147. m_IState = XMRInstState.ONSTARTQ;
  148. startIt = true;
  149. }
  150. // If instance is sleeping (ie, possibly in xmrEventDequeue),
  151. // wake it up if event is in the mask.
  152. if((m_SleepUntil > DateTime.UtcNow) && !m_Suspended)
  153. {
  154. int evc1 = (int)evc;
  155. int evc2 = evc1 - 32;
  156. if((((uint)evc1 < (uint)32) && (((m_SleepEventMask1 >> evc1) & 1) != 0)) ||
  157. (((uint)evc2 < (uint)32) && (((m_SleepEventMask2 >> evc2) & 1) != 0)))
  158. wakeIt = true;
  159. }
  160. }
  161. // If transitioned from IDLE->ONSTARTQ, actually go insert it
  162. // on m_StartQueue and give the RunScriptThread() a wake-up.
  163. if(startIt)
  164. m_Engine.QueueToStart(this);
  165. // Likewise, if the event mask triggered a wake, wake it up.
  166. if(wakeIt)
  167. {
  168. m_SleepUntil = DateTime.MinValue;
  169. m_Engine.WakeFromSleep(this);
  170. }
  171. }
  172. // This is called in the script thread to step script until it calls
  173. // CheckRun(). It returns what the instance's next state should be,
  174. // ONSLEEPQ, ONYIELDQ, SUSPENDED or FINISHED.
  175. public XMRInstState RunOne()
  176. {
  177. DateTime now = DateTime.UtcNow;
  178. m_SliceStart = Util.GetTimeStampMS();
  179. // If script has called llSleep(), don't do any more until time is up.
  180. m_RunOnePhase = "check m_SleepUntil";
  181. if(m_SleepUntil > now)
  182. {
  183. m_RunOnePhase = "return is sleeping";
  184. return XMRInstState.ONSLEEPQ;
  185. }
  186. // Also, someone may have called Suspend().
  187. m_RunOnePhase = "check m_SuspendCount";
  188. if(m_SuspendCount > 0)
  189. {
  190. m_RunOnePhase = "return is suspended";
  191. return XMRInstState.SUSPENDED;
  192. }
  193. // Make sure we aren't being migrated in or out and prevent that
  194. // whilst we are in here. If migration has it locked, don't call
  195. // back right away, delay a bit so we don't get in infinite loop.
  196. m_RunOnePhase = "lock m_RunLock";
  197. if(!Monitor.TryEnter(m_RunLock))
  198. {
  199. m_SleepUntil = now.AddMilliseconds(3);
  200. m_RunOnePhase = "return was locked";
  201. return XMRInstState.ONSLEEPQ;
  202. }
  203. try
  204. {
  205. m_RunOnePhase = "check entry invariants";
  206. CheckRunLockInvariants(true);
  207. Exception e = null;
  208. // Maybe it has been Disposed()
  209. if(m_Part == null)
  210. {
  211. m_RunOnePhase = "runone saw it disposed";
  212. return XMRInstState.DISPOSED;
  213. }
  214. // Do some more of the last event if it didn't finish.
  215. if(this.eventCode != ScriptEventCode.None)
  216. {
  217. lock(m_QueueLock)
  218. {
  219. if(m_DetachQuantum > 0 && --m_DetachQuantum == 0)
  220. {
  221. m_Suspended = true;
  222. m_DetachReady.Set();
  223. m_RunOnePhase = "detach quantum went zero";
  224. CheckRunLockInvariants(true);
  225. return XMRInstState.FINISHED;
  226. }
  227. }
  228. m_RunOnePhase = "resume old event handler";
  229. m_LastRanAt = now;
  230. m_InstEHSlice++;
  231. callMode = CallMode_NORMAL;
  232. e = ResumeEx();
  233. }
  234. // Otherwise, maybe we can dequeue a new event and start
  235. // processing it.
  236. else
  237. {
  238. m_RunOnePhase = "lock event queue";
  239. EventParams evt = null;
  240. ScriptEventCode evc = ScriptEventCode.None;
  241. lock(m_QueueLock)
  242. {
  243. // We can't get here unless the script has been resumed
  244. // after creation, then suspended again, and then had
  245. // an event posted to it. We just pretend there is no
  246. // event int he queue and let the normal mechanics
  247. // carry out the suspension. A Resume will handle the
  248. // restarting gracefully. This is taking the easy way
  249. // out and may be improved in the future.
  250. if(m_Suspended)
  251. {
  252. m_RunOnePhase = "m_Suspended is set";
  253. CheckRunLockInvariants(true);
  254. return XMRInstState.FINISHED;
  255. }
  256. m_RunOnePhase = "dequeue event";
  257. if(m_EventQueue.First != null)
  258. {
  259. evt = m_EventQueue.First.Value;
  260. if(m_DetachQuantum > 0)
  261. {
  262. evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode),
  263. evt.EventName);
  264. if(evc != ScriptEventCode.attach)
  265. {
  266. // This is the case where the attach event
  267. // has completed and another event is queued
  268. // Stop it from running and suspend
  269. m_Suspended = true;
  270. m_DetachReady.Set();
  271. m_DetachQuantum = 0;
  272. m_RunOnePhase = "nothing to do #3";
  273. CheckRunLockInvariants(true);
  274. return XMRInstState.FINISHED;
  275. }
  276. }
  277. m_EventQueue.RemoveFirst();
  278. evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode),
  279. evt.EventName);
  280. if((int)evc >= 0)
  281. m_EventCounts[(int)evc]--;
  282. }
  283. // If there is no event to dequeue, don't run this script
  284. // until another event gets queued.
  285. if(evt == null)
  286. {
  287. if(m_DetachQuantum > 0)
  288. {
  289. // This will happen if the attach event has run
  290. // and exited with time slice left.
  291. m_Suspended = true;
  292. m_DetachReady.Set();
  293. m_DetachQuantum = 0;
  294. }
  295. m_RunOnePhase = "nothing to do #4";
  296. CheckRunLockInvariants(true);
  297. return XMRInstState.FINISHED;
  298. }
  299. }
  300. // Dequeued an event, so start it going until it either
  301. // finishes or it calls CheckRun().
  302. m_RunOnePhase = "start event handler";
  303. m_DetectParams = evt.DetectParams;
  304. m_LastRanAt = now;
  305. m_InstEHEvent++;
  306. e = StartEventHandler(evc, evt.Params);
  307. }
  308. m_RunOnePhase = "done running";
  309. m_CPUTime += DateTime.UtcNow.Subtract(now).TotalMilliseconds;
  310. // Maybe it puqued.
  311. if(e != null)
  312. {
  313. m_RunOnePhase = "handling exception " + e.Message;
  314. HandleScriptException(e);
  315. m_RunOnePhase = "return had exception " + e.Message;
  316. CheckRunLockInvariants(true);
  317. return XMRInstState.FINISHED;
  318. }
  319. // If event handler completed, get rid of detect params.
  320. if(this.eventCode == ScriptEventCode.None)
  321. m_DetectParams = null;
  322. }
  323. finally
  324. {
  325. m_RunOnePhase += "; checking exit invariants and unlocking";
  326. CheckRunLockInvariants(false);
  327. Monitor.Exit(m_RunLock);
  328. }
  329. // Cycle script through the yield queue and call it back asap.
  330. m_RunOnePhase = "last return";
  331. return XMRInstState.ONYIELDQ;
  332. }
  333. /**
  334. * @brief Immediately after taking m_RunLock or just before releasing it, check invariants.
  335. */
  336. private ScriptEventCode lastEventCode = ScriptEventCode.None;
  337. private bool lastActive = false;
  338. private string lastRunPhase = "";
  339. public void CheckRunLockInvariants(bool throwIt)
  340. {
  341. // If not executing any event handler, there shouldn't be any saved stack frames.
  342. // If executing an event handler, there should be some saved stack frames.
  343. bool active = (stackFrames != null);
  344. ScriptEventCode ec = this.eventCode;
  345. if(((ec == ScriptEventCode.None) && active) ||
  346. ((ec != ScriptEventCode.None) && !active))
  347. {
  348. m_log.Error("CheckRunLockInvariants: script=" + m_DescName);
  349. m_log.Error("CheckRunLockInvariants: eventcode=" + ec.ToString() + ", active=" + active.ToString());
  350. m_log.Error("CheckRunLockInvariants: m_RunOnePhase=" + m_RunOnePhase);
  351. m_log.Error("CheckRunLockInvariants: lastec=" + lastEventCode + ", lastAct=" + lastActive + ", lastPhase=" + lastRunPhase);
  352. if(throwIt)
  353. throw new Exception("CheckRunLockInvariants: eventcode=" + ec.ToString() + ", active=" + active.ToString());
  354. }
  355. lastEventCode = ec;
  356. lastActive = active;
  357. lastRunPhase = m_RunOnePhase;
  358. }
  359. /*
  360. * Start event handler.
  361. *
  362. * Input:
  363. * newEventCode = code of event to be processed
  364. * newEhArgs = arguments for the event handler
  365. *
  366. * Caution:
  367. * It is up to the caller to make sure ehArgs[] is correct for
  368. * the particular event handler being called. The first thing
  369. * a script event handler method does is to unmarshall the args
  370. * from ehArgs[] and will throw an array bounds or cast exception
  371. * if it can't.
  372. */
  373. private Exception StartEventHandler(ScriptEventCode newEventCode, object[] newEhArgs)
  374. {
  375. // We use this.eventCode == ScriptEventCode.None to indicate we are idle.
  376. // So trying to execute ScriptEventCode.None might make a mess.
  377. if(newEventCode == ScriptEventCode.None)
  378. return new Exception("Can't process ScriptEventCode.None");
  379. // Silly to even try if there is no handler defined for this event.
  380. if(((int)newEventCode >= 0) && (m_ObjCode.scriptEventHandlerTable[this.stateCode, (int)newEventCode] == null))
  381. return null;
  382. // The microthread shouldn't be processing any event code.
  383. // These are assert checks so we throw them directly as exceptions.
  384. if(this.eventCode != ScriptEventCode.None)
  385. throw new Exception("still processing event " + this.eventCode.ToString());
  386. // Save eventCode so we know what event handler to run in the microthread.
  387. // And it also marks us busy so we can't be started again and this event lost.
  388. this.eventCode = newEventCode;
  389. this.ehArgs = newEhArgs;
  390. // This calls ScriptUThread.Main() directly, and returns when Main() [indirectly]
  391. // calls Suspend() or when Main() returns, whichever occurs first.
  392. // Setting stackFrames = null means run the event handler from the beginning
  393. // without doing any stack frame restores first.
  394. this.stackFrames = null;
  395. return StartEx();
  396. }
  397. /**
  398. * @brief There was an exception whilst starting/running a script event handler.
  399. * Maybe we handle it directly or just print an error message.
  400. */
  401. private void HandleScriptException(Exception e)
  402. {
  403. // The script threw some kind of exception that was not caught at
  404. // script level, so the script is no longer running an event handler.
  405. ScriptEventCode curevent = eventCode;
  406. eventCode = ScriptEventCode.None;
  407. stackFrames = null;
  408. if (e is ScriptDeleteException)
  409. {
  410. // Script did something like llRemoveInventory(llGetScriptName());
  411. // ... to delete itself from the object.
  412. m_SleepUntil = DateTime.MaxValue;
  413. Verbose("[YEngine]: script self-delete {0}", m_ItemID);
  414. m_Part.Inventory.RemoveInventoryItem(m_ItemID);
  415. }
  416. else if(e is ScriptDieException)
  417. {
  418. // Script did an llDie()
  419. m_RunOnePhase = "dying...";
  420. m_SleepUntil = DateTime.MaxValue;
  421. m_Engine.World.DeleteSceneObject(m_Part.ParentGroup, false);
  422. }
  423. else if (e is ScriptResetException)
  424. {
  425. // Script did an llResetScript().
  426. m_RunOnePhase = "resetting...";
  427. ResetLocked("HandleScriptResetException");
  428. }
  429. else if (e is ScriptException)
  430. {
  431. // Some general script error.
  432. SendScriptErrorMessage(e, curevent);
  433. }
  434. else
  435. {
  436. // Some general script error.
  437. SendErrorMessage(e);
  438. }
  439. }
  440. private void SendScriptErrorMessage(Exception e, ScriptEventCode ev)
  441. {
  442. StringBuilder msg = new StringBuilder();
  443. msg.Append("YEngine: ");
  444. if (e.Message != null)
  445. msg.Append(e.Message);
  446. msg.Append(" (script: ");
  447. msg.Append(m_Item.Name);
  448. msg.Append(" event: ");
  449. msg.Append(ev.ToString());
  450. msg.Append(" primID: ");
  451. msg.Append(m_Part.UUID.ToString());
  452. msg.Append(" at: <");
  453. Vector3 pos = m_Part.AbsolutePosition;
  454. msg.Append((int)Math.Floor(pos.X));
  455. msg.Append(',');
  456. msg.Append((int)Math.Floor(pos.Y));
  457. msg.Append(',');
  458. msg.Append((int)Math.Floor(pos.Z));
  459. msg.Append(">) Script must be Reset to re-enable.\n");
  460. string msgst = msg.ToString();
  461. if (msgst.Length > 1000)
  462. msgst = msgst.Substring(0, 1000);
  463. m_Engine.World.SimChat(Utils.StringToBytes(msgst),
  464. ChatTypeEnum.DebugChannel, 2147483647,
  465. m_Part.AbsolutePosition,
  466. m_Part.Name, m_Part.UUID, false);
  467. m_log.Debug(string.Format(
  468. "[SCRIPT ERROR]: {0} (at event {1}, part {2} {3} at {4} in {5}",
  469. (e.Message == null)? "" : e.Message,
  470. ev.ToString(),
  471. m_Part.Name,
  472. m_Part.UUID,
  473. m_Part.AbsolutePosition,
  474. m_Part.ParentGroup.Scene.Name));
  475. m_SleepUntil = DateTime.MaxValue;
  476. }
  477. /**
  478. * @brief There was an exception running script event handler.
  479. * Display error message and disable script (in a way
  480. * that the script can be reset to be restarted).
  481. */
  482. private void SendErrorMessage(Exception e)
  483. {
  484. StringBuilder msg = new StringBuilder();
  485. msg.Append("[YEngine]: Exception while running ");
  486. msg.Append(m_ItemID);
  487. msg.Append('\n');
  488. // Add exception message.
  489. string des = e.Message;
  490. des = (des == null) ? "" : (": " + des);
  491. msg.Append(e.GetType().Name + des + "\n");
  492. // Tell script owner what to do.
  493. msg.Append("Prim: <");
  494. msg.Append(m_Part.Name);
  495. msg.Append(">, Script: <");
  496. msg.Append(m_Item.Name);
  497. msg.Append(">, Location: ");
  498. msg.Append(m_Engine.World.RegionInfo.RegionName);
  499. msg.Append(" <");
  500. Vector3 pos = m_Part.AbsolutePosition;
  501. msg.Append((int)Math.Floor(pos.X));
  502. msg.Append(',');
  503. msg.Append((int)Math.Floor(pos.Y));
  504. msg.Append(',');
  505. msg.Append((int)Math.Floor(pos.Z));
  506. msg.Append(">\nScript must be Reset to re-enable.\n");
  507. // Display full exception message in log.
  508. m_log.Info(msg.ToString() + XMRExceptionStackString(e), e);
  509. // Give script owner the stack dump.
  510. msg.Append(XMRExceptionStackString(e));
  511. // Send error message to owner.
  512. // Suppress internal code stack trace lines.
  513. string msgst = msg.ToString();
  514. if(!msgst.EndsWith("\n"))
  515. msgst += '\n';
  516. int j = 0;
  517. StringBuilder imstr = new StringBuilder();
  518. for(int i = 0; (i = msgst.IndexOf('\n', i)) >= 0; j = ++i)
  519. {
  520. string line = msgst.Substring(j, i - j);
  521. if(line.StartsWith("at "))
  522. {
  523. if(line.StartsWith("at (wrapper"))
  524. continue; // at (wrapper ...
  525. int k = line.LastIndexOf(".cs:"); // ... .cs:linenumber
  526. if(Int32.TryParse(line.Substring(k + 4), out k))
  527. continue;
  528. }
  529. this.llOwnerSay(line);
  530. }
  531. // Say script is sleeping for a very long time.
  532. // Reset() is able to cancel this sleeping.
  533. m_SleepUntil = DateTime.MaxValue;
  534. }
  535. /**
  536. * @brief The user clicked the Reset Script button.
  537. * We want to reset the script to a never-has-ever-run-before state.
  538. */
  539. public void Reset()
  540. {
  541. checkstate:
  542. XMRInstState iState = m_IState;
  543. switch(iState)
  544. {
  545. // If it's really being constructed now, that's about as reset as we get.
  546. case XMRInstState.CONSTRUCT:
  547. return;
  548. // If it's idle, that means it is ready to receive a new event.
  549. // So we lock the event queue to prevent another thread from taking
  550. // it out of idle, verify that it is still in idle then transition
  551. // it to resetting so no other thread will touch it.
  552. case XMRInstState.IDLE:
  553. lock(m_QueueLock)
  554. {
  555. if(m_IState == XMRInstState.IDLE)
  556. {
  557. m_IState = XMRInstState.RESETTING;
  558. break;
  559. }
  560. }
  561. goto checkstate;
  562. // If it's on the start queue, that means it is about to dequeue an
  563. // event and start processing it. So we lock the start queue so it
  564. // can't be started and transition it to resetting so no other thread
  565. // will touch it.
  566. case XMRInstState.ONSTARTQ:
  567. lock(m_Engine.m_StartQueue)
  568. {
  569. if(m_IState == XMRInstState.ONSTARTQ)
  570. {
  571. m_Engine.m_StartQueue.Remove(this);
  572. m_IState = XMRInstState.RESETTING;
  573. break;
  574. }
  575. }
  576. goto checkstate;
  577. // If it's running, tell CheckRun() to suspend the thread then go back
  578. // to see what it got transitioned to.
  579. case XMRInstState.RUNNING:
  580. suspendOnCheckRunHold = true;
  581. lock(m_QueueLock)
  582. {
  583. }
  584. goto checkstate;
  585. // If it's sleeping, remove it from sleep queue and transition it to
  586. // resetting so no other thread will touch it.
  587. case XMRInstState.ONSLEEPQ:
  588. lock(m_Engine.m_SleepQueue)
  589. {
  590. if(m_IState == XMRInstState.ONSLEEPQ)
  591. {
  592. m_Engine.m_SleepQueue.Remove(this);
  593. m_IState = XMRInstState.RESETTING;
  594. break;
  595. }
  596. }
  597. goto checkstate;
  598. // It was just removed from the sleep queue and is about to be put
  599. // on the yield queue (ie, is being woken up).
  600. // Let that thread complete transition and try again.
  601. case XMRInstState.REMDFROMSLPQ:
  602. Sleep(10);
  603. goto checkstate;
  604. // If it's yielding, remove it from yield queue and transition it to
  605. // resetting so no other thread will touch it.
  606. case XMRInstState.ONYIELDQ:
  607. lock(m_Engine.m_YieldQueue)
  608. {
  609. if(m_IState == XMRInstState.ONYIELDQ)
  610. {
  611. m_Engine.m_YieldQueue.Remove(this);
  612. m_IState = XMRInstState.RESETTING;
  613. break;
  614. }
  615. }
  616. goto checkstate;
  617. // If it just finished running something, let that thread transition it
  618. // to its next state then check again.
  619. case XMRInstState.FINISHED:
  620. Sleep(10);
  621. goto checkstate;
  622. // If it's disposed, that's about as reset as it gets.
  623. case XMRInstState.DISPOSED:
  624. return;
  625. // Some other thread is already resetting it, let it finish.
  626. case XMRInstState.RESETTING:
  627. return;
  628. default:
  629. throw new Exception("bad state");
  630. }
  631. // This thread transitioned the instance to RESETTING so reset it.
  632. lock(m_RunLock)
  633. {
  634. CheckRunLockInvariants(true);
  635. // No other thread should have transitioned it from RESETTING.
  636. if(m_IState != XMRInstState.RESETTING)
  637. throw new Exception("bad state");
  638. // Mark it idle now so it can get queued to process new stuff.
  639. m_IState = XMRInstState.IDLE;
  640. // Reset everything and queue up default's start_entry() event.
  641. ClearQueue();
  642. ResetLocked("external Reset");
  643. CheckRunLockInvariants(true);
  644. }
  645. }
  646. private void ClearQueueExceptLinkMessages()
  647. {
  648. lock(m_QueueLock)
  649. {
  650. EventParams[] linkMessages = new EventParams[m_EventQueue.Count];
  651. int n = 0;
  652. foreach(EventParams evt2 in m_EventQueue)
  653. {
  654. if(evt2.EventName == "link_message")
  655. linkMessages[n++] = evt2;
  656. }
  657. m_EventQueue.Clear();
  658. for(int i = m_EventCounts.Length; --i >= 0;)
  659. m_EventCounts[i] = 0;
  660. for(int i = 0; i < n; i++)
  661. m_EventQueue.AddLast(linkMessages[i]);
  662. m_EventCounts[(int)ScriptEventCode.link_message] = n;
  663. }
  664. }
  665. private void ClearQueue()
  666. {
  667. lock(m_QueueLock)
  668. {
  669. m_EventQueue.Clear(); // no events queued
  670. for(int i = m_EventCounts.Length; --i >= 0;)
  671. m_EventCounts[i] = 0;
  672. }
  673. }
  674. /**
  675. * @brief The script called llResetScript() while it was running and
  676. * has suspended. We want to reset the script to a never-has-
  677. * ever-run-before state.
  678. *
  679. * Caller must have m_RunLock locked so we know script isn't
  680. * running.
  681. */
  682. private void ResetLocked(string from)
  683. {
  684. m_RunOnePhase = "ResetLocked: releasing controls";
  685. ReleaseControls();
  686. m_RunOnePhase = "ResetLocked: removing script";
  687. m_Part.Inventory.GetInventoryItem(m_ItemID).PermsMask = 0;
  688. m_Part.Inventory.GetInventoryItem(m_ItemID).PermsGranter = UUID.Zero;
  689. IUrlModule urlModule = m_Engine.World.RequestModuleInterface<IUrlModule>();
  690. if(urlModule != null)
  691. urlModule.ScriptRemoved(m_ItemID);
  692. AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
  693. m_RunOnePhase = "ResetLocked: clearing current event";
  694. this.eventCode = ScriptEventCode.None; // not processing an event
  695. m_DetectParams = null; // not processing an event
  696. m_SleepUntil = DateTime.MinValue; // not doing llSleep()
  697. m_ResetCount++; // has been reset once more
  698. // Tell next call to 'default state_entry()' to reset all global
  699. // vars to their initial values.
  700. doGblInit = true;
  701. // Throw away all its stack frames.
  702. // If the script is resetting itself, there shouldn't be any stack frames.
  703. // If the script is being reset by something else, we throw them away cuz we want to start from the beginning of an event handler.
  704. stackFrames = null;
  705. // Set script to 'default' state and queue call to its
  706. // 'state_entry()' event handler.
  707. m_RunOnePhase = "ResetLocked: posting default:state_entry() event";
  708. stateCode = 0;
  709. m_Part.SetScriptEvents(m_ItemID, GetStateEventFlags(0));
  710. PostEvent(new EventParams("state_entry",
  711. zeroObjectArray,
  712. zeroDetectParams));
  713. // Tell CheckRun() to let script run.
  714. suspendOnCheckRunHold = false;
  715. suspendOnCheckRunTemp = false;
  716. m_RunOnePhase = "ResetLocked: reset complete";
  717. }
  718. private void ReleaseControls()
  719. {
  720. if(m_Part != null)
  721. {
  722. bool found;
  723. int permsMask;
  724. UUID permsGranter;
  725. try
  726. {
  727. permsGranter = m_Part.TaskInventory[m_ItemID].PermsGranter;
  728. permsMask = m_Part.TaskInventory[m_ItemID].PermsMask;
  729. found = true;
  730. }
  731. catch
  732. {
  733. permsGranter = UUID.Zero;
  734. permsMask = 0;
  735. found = false;
  736. }
  737. if(found && ((permsMask & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) != 0))
  738. {
  739. ScenePresence presence = m_Engine.World.GetScenePresence(permsGranter);
  740. if(presence != null)
  741. presence.UnRegisterControlEventsToScript(m_LocalID, m_ItemID);
  742. }
  743. }
  744. }
  745. /**
  746. * @brief The script code should call this routine whenever it is
  747. * convenient to perform a migation or switch microthreads.
  748. */
  749. public override void CheckRunWork()
  750. {
  751. if(!suspendOnCheckRunHold && !suspendOnCheckRunTemp)
  752. {
  753. if(Util.GetTimeStampMS() - m_SliceStart < 60.0)
  754. return;
  755. suspendOnCheckRunTemp = true;
  756. }
  757. m_CheckRunPhase = "entered";
  758. // Stay stuck in this loop as long as something wants us suspended.
  759. while(suspendOnCheckRunHold || suspendOnCheckRunTemp)
  760. {
  761. m_CheckRunPhase = "top of while";
  762. suspendOnCheckRunTemp = false;
  763. switch(this.callMode)
  764. {
  765. // Now we are ready to suspend or resume.
  766. case CallMode_NORMAL:
  767. m_CheckRunPhase = "suspending";
  768. callMode = XMRInstance.CallMode_SAVE;
  769. stackFrames = null;
  770. throw new StackHibernateException(); // does not return
  771. // We get here when the script state has been read in by MigrateInEventHandler().
  772. // Since the stack is completely restored at this point, any subsequent calls
  773. // within the functions should do their normal processing instead of trying to
  774. // restore their state.
  775. // the stack has been restored as a result of calling ResumeEx()
  776. // tell script code to process calls normally
  777. case CallMode_RESTORE:
  778. this.callMode = CallMode_NORMAL;
  779. break;
  780. default:
  781. throw new Exception("callMode=" + callMode);
  782. }
  783. m_CheckRunPhase = "resumed";
  784. }
  785. m_CheckRunPhase = "returning";
  786. // Upon return from CheckRun() it should always be the case that the script is
  787. // going to process calls normally, neither saving nor restoring stack frame state.
  788. if(callMode != CallMode_NORMAL)
  789. throw new Exception("bad callMode " + callMode);
  790. }
  791. /**
  792. * @brief Allow script to dequeue events.
  793. */
  794. public void ResumeIt()
  795. {
  796. lock(m_QueueLock)
  797. {
  798. m_Suspended = false;
  799. if((m_EventQueue != null) &&
  800. (m_EventQueue.First != null) &&
  801. (m_IState == XMRInstState.IDLE))
  802. {
  803. m_IState = XMRInstState.ONSTARTQ;
  804. m_Engine.QueueToStart(this);
  805. }
  806. m_HasRun = true;
  807. }
  808. }
  809. /**
  810. * @brief Block script from dequeuing events.
  811. */
  812. public void SuspendIt()
  813. {
  814. lock(m_QueueLock)
  815. {
  816. m_Suspended = true;
  817. }
  818. }
  819. }
  820. /**
  821. * @brief Thrown by CheckRun() to unwind the script stack, capturing frames to
  822. * instance.stackFrames as it unwinds. We don't want scripts to be able
  823. * to intercept this exception as it would block the stack capture
  824. * functionality.
  825. */
  826. public class StackCaptureException: Exception, IXMRUncatchable
  827. {
  828. }
  829. }