XMRScriptThread.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  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 OpenSim.Framework.Monitoring;
  28. using System;
  29. using System.Collections.Generic;
  30. using System.Threading;
  31. namespace OpenSim.Region.ScriptEngine.Yengine
  32. {
  33. public partial class Yengine
  34. {
  35. private int m_WakeUpOne = 0;
  36. public object m_WakeUpLock = new object();
  37. private Dictionary<int, XMRInstance> m_RunningInstances = new Dictionary<int, XMRInstance>();
  38. private bool m_SuspendScriptThreadFlag = false;
  39. private bool m_WakeUpThis = false;
  40. public DateTime m_LastRanAt = DateTime.MinValue;
  41. public long m_ScriptExecTime = 0;
  42. [ThreadStatic]
  43. private static int m_ScriptThreadTID;
  44. public static bool IsScriptThread
  45. {
  46. get
  47. {
  48. return m_ScriptThreadTID != 0;
  49. }
  50. }
  51. public void StartThreadWorker(int i, ThreadPriority priority, string sceneName)
  52. {
  53. Thread thd;
  54. if(i >= 0)
  55. thd = Yengine.StartMyThread(RunScriptThread, "YScript" + i.ToString() + " (" + sceneName +")", priority);
  56. else
  57. thd = Yengine.StartMyThread(RunScriptThread, "YScript", priority);
  58. lock(m_WakeUpLock)
  59. m_RunningInstances.Add(thd.ManagedThreadId, null);
  60. }
  61. public void StopThreadWorkers()
  62. {
  63. lock(m_WakeUpLock)
  64. {
  65. while(m_RunningInstances.Count != 0)
  66. {
  67. Monitor.PulseAll(m_WakeUpLock);
  68. Monitor.Wait(m_WakeUpLock, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2);
  69. }
  70. }
  71. }
  72. /**
  73. * @brief Something was just added to the Start or Yield queue so
  74. * wake one of the RunScriptThread() instances to run it.
  75. */
  76. public void WakeUpOne()
  77. {
  78. lock(m_WakeUpLock)
  79. {
  80. m_WakeUpOne++;
  81. Monitor.Pulse(m_WakeUpLock);
  82. }
  83. }
  84. public void SuspendThreads()
  85. {
  86. lock(m_WakeUpLock)
  87. {
  88. m_SuspendScriptThreadFlag = true;
  89. Monitor.PulseAll(m_WakeUpLock);
  90. }
  91. }
  92. public void ResumeThreads()
  93. {
  94. lock(m_WakeUpLock)
  95. {
  96. m_SuspendScriptThreadFlag = false;
  97. Monitor.PulseAll(m_WakeUpLock);
  98. }
  99. }
  100. /**
  101. * @brief Thread that runs the scripts.
  102. *
  103. * There are NUMSCRIPTHREADWKRS of these.
  104. * Each sits in a loop checking the Start and Yield queues for
  105. * a script to run and calls the script as a microthread.
  106. */
  107. private void RunScriptThread()
  108. {
  109. int tid = System.Threading.Thread.CurrentThread.ManagedThreadId;
  110. ThreadStart thunk;
  111. XMRInstance inst;
  112. bool didevent;
  113. m_ScriptThreadTID = tid;
  114. while(!m_Exiting)
  115. {
  116. Yengine.UpdateMyThread();
  117. lock(m_WakeUpLock)
  118. {
  119. // Maybe there are some scripts waiting to be migrated in or out.
  120. thunk = null;
  121. if(m_ThunkQueue.Count > 0)
  122. thunk = m_ThunkQueue.Dequeue();
  123. // Handle 'xmr resume/suspend' commands.
  124. else if(m_SuspendScriptThreadFlag && !m_Exiting)
  125. {
  126. Monitor.Wait(m_WakeUpLock, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2);
  127. Yengine.UpdateMyThread();
  128. continue;
  129. }
  130. }
  131. if(thunk != null)
  132. {
  133. thunk();
  134. continue;
  135. }
  136. if(m_StartProcessing)
  137. {
  138. // If event just queued to any idle scripts
  139. // start them right away. But only start so
  140. // many so we can make some progress on yield
  141. // queue.
  142. int numStarts;
  143. didevent = false;
  144. for(numStarts = 5; numStarts >= 0; --numStarts)
  145. {
  146. lock(m_StartQueue)
  147. inst = m_StartQueue.RemoveHead();
  148. if(inst == null)
  149. break;
  150. if (inst.m_IState == XMRInstState.SUSPENDED)
  151. continue;
  152. if (inst.m_IState != XMRInstState.ONSTARTQ)
  153. throw new Exception("bad state");
  154. RunInstance(inst, tid);
  155. if(m_SuspendScriptThreadFlag || m_Exiting)
  156. continue;
  157. didevent = true;
  158. }
  159. // If there is something to run, run it
  160. // then rescan from the beginning in case
  161. // a lot of things have changed meanwhile.
  162. //
  163. // These are considered lower priority than
  164. // m_StartQueue as they have been taking at
  165. // least one quantum of CPU time and event
  166. // handlers are supposed to be quick.
  167. lock(m_YieldQueue)
  168. inst = m_YieldQueue.RemoveHead();
  169. if(inst != null)
  170. {
  171. if (inst.m_IState == XMRInstState.SUSPENDED)
  172. continue;
  173. if (inst.m_IState != XMRInstState.ONYIELDQ)
  174. throw new Exception("bad state");
  175. RunInstance(inst, tid);
  176. continue;
  177. }
  178. // If we left something dangling in the m_StartQueue or m_YieldQueue, go back to check it.
  179. if(didevent)
  180. continue;
  181. }
  182. // Nothing to do, sleep.
  183. lock(m_WakeUpLock)
  184. {
  185. if(!m_WakeUpThis && (m_WakeUpOne <= 0) && !m_Exiting)
  186. Monitor.Wait(m_WakeUpLock, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2);
  187. m_WakeUpThis = false;
  188. if((m_WakeUpOne > 0) && (--m_WakeUpOne > 0))
  189. Monitor.Pulse(m_WakeUpLock);
  190. }
  191. }
  192. lock(m_WakeUpLock)
  193. m_RunningInstances.Remove(tid);
  194. Yengine.MyThreadExiting();
  195. }
  196. /**
  197. * @brief A script instance was just removed from the Start or Yield Queue.
  198. * So run it for a little bit then stick in whatever queue it should go in.
  199. */
  200. private void RunInstance(XMRInstance inst, int tid)
  201. {
  202. m_LastRanAt = DateTime.UtcNow;
  203. m_ScriptExecTime -= (long)(m_LastRanAt - DateTime.MinValue).TotalMilliseconds;
  204. inst.m_IState = XMRInstState.RUNNING;
  205. lock(m_WakeUpLock)
  206. m_RunningInstances[tid] = inst;
  207. XMRInstState newIState = inst.RunOne();
  208. lock(m_WakeUpLock)
  209. m_RunningInstances[tid] = null;
  210. HandleNewIState(inst, newIState);
  211. m_ScriptExecTime += (long)(DateTime.UtcNow - DateTime.MinValue).TotalMilliseconds;
  212. }
  213. }
  214. }