XMRInstRun.cs 41 KB

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