// ****************************************************************
// 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
}
}