context: // This file is part of nMars - Core War MARS for .NET 
  // Whole solution including it's license could be found at
  // http://sourceforge.net/projects/nmars/
  // 2006 Pavel Savara
  
  using System;
  using System.Collections.Generic;
  using System.Threading;
  using nMars.RedCode;
  using nMars.RedCode.Modules;
  
  namespace nMars.RedCode
  {
      /// <summary>
      /// Asynchronous wrapper around IDebuggerEngine
      /// </summary>
      public class AsyncEngine : BaseComponent, IEngine, IDisposable, IDebuggerEngine, IAsyncEngine
      {
          #region Construction
  
          public AsyncEngine(IDebuggerEngine aEngine)
          {
              engine = aEngine;
              pause = true;
              running = false;
              live = false;
              brake = 0;
          }
  
          private void InitWorker()
          {
              worker = new Thread(new ThreadStart(OuterLoop));
              signalRun = new ManualResetEvent(false);
              signalPaused = new ManualResetEvent(true);
              signalRunning = new ManualResetEvent(false);
              pause = true;
              quit = false;
              running = false;
              worker.Start();
          }
  
          public void Dispose()
          {
              worker.Interrupt();
              signalRun.Close();
              signalPaused.Close();
              signalRunning.Close();
          }
  
          #endregion
  
          #region Synchronization
  
          public void Continue(bool wait)
          {
              lock (this)
              {
                  pause = false;
                  signalRun.Set();
              }
              if (wait)
                  signalRunning.WaitOne();
          }
  
          public void Pause(bool wait)
          {
              lock (this)
              {
                  signalRun.Reset();
                  pause = true;
              }
              if (wait)
                  signalPaused.WaitOne();
          }
  
          public void Wait()
          {
              while (true)
              {
                  signalPaused.WaitOne();
                  lock (this)
                  {
                      if (!running || stepResult == StepResult.Finished)
                          break;
                  }
                  signalRun.WaitOne();
              }
          }
  
          public void Quit(bool wait)
          {
              lock (this)
              {
                  quit = true;
              }
              Continue(wait);
          }
  
          public void Kill()
          {
              lock (this)
              {
                  worker.Interrupt();
                  running = false;
                  live = false;
              }
          }
  
          public void Run(IProject project, EngineStoppedCallback callback)
          {
              BeginMatch(project, callback);
              Continue(false);
          }
  
          public MatchResult Run(IProject project, ISimpleOutput console)
          {
              BeginMatch(project);
              Continue(false);
              return EndMatch(console);
          }
  
          public void BeginMatch(IProject project)
          {
              BeginMatch(project, null);
          }
  
          public void BeginMatch(IProject project, EngineStoppedCallback callback)
          {
              engineStoppedCallback = callback;
              brake = project.EngineOptions.Brake;
              lock (this)
              {
                  engine.BeginMatch(project);
              }
              InitWorker();
          }
  
          public MatchResult EndMatch(ISimpleOutput console)
          {
              Wait();
              lock (this)
              {
                  return engine.EndMatch(console);
              }
          }
  
          public StepResult PrevStep()
          {
              lock (this)
              {
                  if (running)
                      throw new InvalidOperationException("Can't make steps on running engine");
                  stepResult = engine.PrevStep();
                  return stepResult;
              }
          }
  
  
          public StepResult NextStep()
          {
              lock (this)
              {
                  if (running)
                      throw new InvalidOperationException("Can't make steps on running engine");
  
                  //TODO
                  stepResult = engine.NextStep();
                  return stepResult;
              }
          }
  
          public bool IsLive 
          { 
              get
              {
                  return live;
              }
          }
  
          public bool IsPaused
          {
              get
              {
                  return pause;
              }
          }
  
          #endregion
  
          #region Loop
  
          /// <summary>
          /// External loop, solving ThreadInterrupted problems
          /// </summary>
          private void OuterLoop()
          {
              try
              {
                  Loop();
              }
              catch (ThreadInterruptedException)
              {
                  live = false;
                  running = false;
                  signalPaused.Set();
                  signalRunning.Reset();
                  //swallow
              }
              catch (Exception)
              {
                  live = false;
                  running = false;
                  signalPaused.Set();
                  signalRunning.Reset();
                  throw;
              }
          }
  
          /// <summary>
          /// Loop body + stoping
          /// </summary>
          private void Loop()
          {
              live = true;
              signalRun.WaitOne();
  
              running = true;
              signalRunning.Set();
              do
              {
                  if (LoopBody())
                      break;
              } while (stepResult != StepResult.Finished);
              lock (this)
              {
                  live = false;
                  running = false;
                  signalPaused.Set();
                  signalRunning.Reset();
              }
              if (engineStoppedCallback != null)
              {
                  engineStoppedCallback.Invoke(true, null, null);
              }
          }
  
          /// <returns>True to quit loop</returns>
          private bool LoopBody()
          {
              lock (this)
              {
  
gt:                 SlowdownLoop(); // warning! intentional inversed lock inside