XMRInstRun.cs 43 KB

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