// **************************************************************** // This is free software licensed under the NUnit license. You // may obtain a copy of the license as well as information regarding // copyright ownership at http://nunit.org/?p=license&r=2.4. // **************************************************************** namespace NUnit.Core { using System; using System.Threading; /// /// EventPump pulls events out of an EventQueue and sends /// them to a listener. It is used to send events back to /// the client without using the CallContext of the test /// runner thread. /// public class EventPump : IDisposable { #region Instance Variables /// /// The downstream listener to which we send events /// EventListener eventListener; /// /// The queue that holds our events /// EventQueue events; /// /// Thread to do the pumping /// Thread pumpThread; /// /// Indicator that we are pumping events /// private bool pumping; /// /// Indicator that we are stopping /// private bool stopping; /// /// If true, stop after sending RunFinished /// private bool autostop; #endregion #region Constructor /// /// Constructor /// /// The EventListener to receive events /// The event queue to pull events from /// Set to true to stop pump after RunFinished public EventPump( EventListener eventListener, EventQueue events, bool autostop) { this.eventListener = eventListener; this.events = events; this.autostop = autostop; } #endregion #region Properties // TODO: Replace booleans with a state? /// /// Returns true if we are pumping events /// public bool Pumping { get { return pumping; } } /// /// Returns true if a stop is in progress /// public bool Stopping { get { return stopping; } } #endregion #region Public Methods /// /// Dispose stops the pump /// public void Dispose() { Stop(); } /// /// Start the pump /// public void Start() { if ( !this.Pumping ) // Ignore if already started { this.pumpThread = new Thread( new ThreadStart( PumpThreadProc ) ); this.pumpThread.Name = "EventPumpThread"; pumping = true; this.pumpThread.Start(); } } /// /// Tell the pump to stop after emptying the queue. /// public void Stop() { if ( this.Pumping && !this.Stopping ) // Ignore extra calls { lock( events ) { stopping = true; Monitor.Pulse( events ); // In case thread is waiting } this.pumpThread.Join(); } } #endregion #region PumpThreadProc /// /// Our thread proc for removing items from the event /// queue and sending them on. Note that this would /// need to do more locking if any other thread were /// removing events from the queue. /// private void PumpThreadProc() { EventListener hostListeners = CoreExtensions.Host.Listeners; Monitor.Enter( events ); try { while (this.events.Count > 0 || !stopping) { while (this.events.Count > 0) { Event e = this.events.Dequeue(); e.Send(this.eventListener); e.Send(hostListeners); if (autostop && e is RunFinishedEvent) stopping = true; } // Will be pulsed if there are any events added // or if it's time to stop the pump. if (!stopping) Monitor.Wait(events); } } catch (Exception ex) { throw new ApplicationException("Exception in pump thread", ex); } finally { Monitor.Exit( events ); pumping = false; stopping = false; //pumpThread = null; } } #endregion } }