public void Stop() { const int thread_join_timeout = 30000; Threads.ForEach(t => t.Exit()); Threads.Where(t => t.Running).ForEach(t => { var thread = t.Thread; if (thread == null) { // has already been cleaned up (or never started) return; } if (!thread.Join(thread_join_timeout)) { Logger.Log($"Thread {t.Name} failed to exit in allocated time ({thread_join_timeout}ms).", LoggingTarget.Runtime, LogLevel.Important); } }); // as the input thread isn't actually handled by a thread, the above join does not necessarily mean it has been completed to an exiting state. while (!mainThread.Exited) { mainThread.ProcessFrame(); } ThreadSafety.ResetAllForCurrentThread(); }
public virtual void RunMainLoop() { // propagate any requested change in execution mode at a safe point in frame execution ensureCorrectExecutionMode(); Debug.Assert(activeExecutionMode != null); switch (activeExecutionMode.Value) { case ExecutionMode.SingleThread: { lock (threads) { foreach (var t in threads) { t.RunSingleFrame(); } } break; } case ExecutionMode.MultiThreaded: // still need to run the main/input thread on the window loop mainThread.RunSingleFrame(); break; } ThreadSafety.ResetAllForCurrentThread(); }
public void Stop() { const int thread_join_timeout = 30000; // exit in reverse order so AudioThread is exited last (UpdateThread depends on AudioThread) Threads.Reverse().ForEach(t => { // save the native thread to a local variable as Thread gets set to null when exiting. // WaitForState(Exited) appears to be unsafe in multithreaded. var thread = t.Thread; t.Exit(); if (thread != null) { if (!thread.Join(thread_join_timeout)) { throw new TimeoutException($"Thread {t.Name} failed to exit in allocated time ({thread_join_timeout}ms)."); } } else { t.WaitForState(GameThreadState.Exited); } Debug.Assert(t.Exited); }); ThreadSafety.ResetAllForCurrentThread(); }
private void ensureCorrectExecutionMode() { if (ExecutionMode == activeExecutionMode) { return; } if (activeExecutionMode == null) { // in the case we have not yet got an execution mode, set this early to allow usage in GameThread.Initialize overrides. activeExecutionMode = ThreadSafety.ExecutionMode = ExecutionMode; } // shut down threads in reverse to ensure audio stops last (other threads may be waiting on a queued event otherwise) foreach (var t in Threads.Reverse()) { t.Pause(); } switch (ExecutionMode) { case ExecutionMode.MultiThreaded: { // switch to multi-threaded foreach (var t in Threads) { t.Start(); t.Clock.Throttling = true; } break; } case ExecutionMode.SingleThread: { // switch to single-threaded. foreach (var t in Threads) { // only throttle for the main thread t.Initialize(withThrottling: t == mainThread); } // this is usually done in the execution loop, but required here for the initial game startup, // which would otherwise leave values in an incorrect state. ThreadSafety.ResetAllForCurrentThread(); break; } } activeExecutionMode = ThreadSafety.ExecutionMode = ExecutionMode; updateMainThreadRates(); }
private void ensureCorrectExecutionMode() { // locking is required as this method may be called from two different threads. lock (startStopLock) { // pull into a local variable as the property is not locked during writes. var executionMode = ExecutionMode; if (executionMode == activeExecutionMode) { return; } activeExecutionMode = ThreadSafety.ExecutionMode = executionMode; Logger.Log($"Execution mode changed to {activeExecutionMode}"); } pauseAllThreads(); switch (activeExecutionMode) { case ExecutionMode.MultiThreaded: { // switch to multi-threaded foreach (var t in Threads) { t.Start(); } break; } case ExecutionMode.SingleThread: { // switch to single-threaded. foreach (var t in Threads) { // only throttle for the main thread t.Initialize(withThrottling: t == mainThread); } // this is usually done in the execution loop, but required here for the initial game startup, // which would otherwise leave values in an incorrect state. ThreadSafety.ResetAllForCurrentThread(); break; } } updateMainThreadRates(); }
private void ensureCorrectExecutionMode() { if (ExecutionMode == activeExecutionMode) { return; } // if null, we have not yet got an execution mode, so set this early to allow usage in GameThread.Initialize overrides. activeExecutionMode ??= ThreadSafety.ExecutionMode = ExecutionMode; Logger.Log($"Execution mode changed to {activeExecutionMode}"); pauseAllThreads(); switch (ExecutionMode) { case ExecutionMode.MultiThreaded: { // switch to multi-threaded foreach (var t in Threads) { t.Start(); t.Clock.Throttling = true; } break; } case ExecutionMode.SingleThread: { // switch to single-threaded. foreach (var t in Threads) { // only throttle for the main thread t.Initialize(withThrottling: t == mainThread); } // this is usually done in the execution loop, but required here for the initial game startup, // which would otherwise leave values in an incorrect state. ThreadSafety.ResetAllForCurrentThread(); break; } } activeExecutionMode = ThreadSafety.ExecutionMode = ExecutionMode; updateMainThreadRates(); }