diff --git a/src/NUnitFramework/framework/Internal/Execution/EventPump.cs b/src/NUnitFramework/framework/Internal/Execution/EventPump.cs index 02834aeb40..63c55b95e9 100644 --- a/src/NUnitFramework/framework/Internal/Execution/EventPump.cs +++ b/src/NUnitFramework/framework/Internal/Execution/EventPump.cs @@ -29,12 +29,32 @@ public enum EventPumpState } /// - /// EventPump pulls events out of an EventQueue and sends - /// them to a listener. It is used to send events back to + /// EventPump pulls Event instances out of an EventQueue and sends + /// them to a ITestListener. It is used to send these events back to /// the client without using the CallContext of the test /// runner thread. /// - public class EventPump : IDisposable + public sealed class EventPump : EventPump, IDisposable + { + /// + /// Constructor for standard EventPump + /// + /// The EventListener to receive events + /// The event queue to pull events from + public EventPump(ITestListener eventListener, EventQueue events) + : base(eventListener, events, "Standard") + { + } + } + + /// + /// EventPump base class pulls events of any type out of an EventQueue and sends + /// them to any listener. It is used to send events back to + /// the client without using the CallContext of the test + /// runner thread. + /// + public abstract class EventPump : IDisposable + where TEvent : IEvent { private static readonly Logger Log = InternalTrace.GetLogger("EventPump"); @@ -43,12 +63,12 @@ public class EventPump : IDisposable /// /// The downstream listener to which we send events /// - private readonly ITestListener _eventListener; + private readonly TListener _eventListener; /// /// The queue that holds our events /// - private readonly EventQueue _events; + private readonly EventQueue _events; /// /// Thread to do the pumping @@ -63,15 +83,18 @@ public class EventPump : IDisposable #endregion #region Constructor + /// /// Constructor /// /// The EventListener to receive events /// The event queue to pull events from - public EventPump(ITestListener eventListener, EventQueue events) + /// Name of the thread and pump + protected EventPump(TListener eventListener, EventQueue events, string name = "Standard") { _eventListener = eventListener; _events = events; + Name = name; } #endregion @@ -111,7 +134,7 @@ public void Start() { _pumpThread = new Thread(PumpThreadProc) { - Name = "EventPumpThread" + Name, + Name = $"{Name}EventPumpThread", Priority = ThreadPriority.Highest }; @@ -142,14 +165,14 @@ public void Stop() /// private void PumpThreadProc() { - Log.Debug("Starting EventPump"); + Log.Debug($"Starting {Name}"); //ITestListener hostListeners = CoreExtensions.Host.Listeners; try { while (true) { - Event? e = _events.Dequeue(PumpState == EventPumpState.Pumping); + var e = _events.Dequeue(PumpState == EventPumpState.Pumping); if (e is null) break; try diff --git a/src/NUnitFramework/framework/Internal/Execution/EventQueue.cs b/src/NUnitFramework/framework/Internal/Execution/EventQueue.cs index c9308fa9d4..595c707993 100644 --- a/src/NUnitFramework/framework/Internal/Execution/EventQueue.cs +++ b/src/NUnitFramework/framework/Internal/Execution/EventQueue.cs @@ -6,16 +6,29 @@ namespace NUnit.Framework.Internal.Execution { + /// + /// Interface for ALL event types that can be queued for processing. + /// + /// + public interface IEvent + { + /// + /// The Send method is implemented by derived classes to send the event to the specified listener. + /// + /// The listener. + void Send(TListener listener); + } + #region Individual Event Classes /// - /// NUnit.Core.Event is the abstract base for all stored events. + /// NUnit.Core.Event is the abstract base for all stored standard events. /// An Event is the stored representation of a call to the /// ITestListener interface and is used to record such calls /// or to queue them for forwarding on another thread or at /// a later time. /// - public abstract class Event + public abstract class Event : IEvent { /// /// The Send method is implemented by derived classes to send the event to the specified listener. @@ -134,16 +147,25 @@ public override void Send(ITestListener listener) #endregion /// - /// Implements a queue of work items each of which + /// Implements a queue of work items for the Event type each of which + /// is queued as a WaitCallback. + /// + public sealed class EventQueue : EventQueue + { + } + + /// + /// Implements a template for a queue of work items each of which /// is queued as a WaitCallback. + /// It can handle any event types. /// - public class EventQueue + public abstract class EventQueue { private const int SpinCount = 5; // static readonly Logger log = InternalTrace.GetLogger("EventQueue"); - private readonly ConcurrentQueue _queue = new(); + private readonly ConcurrentQueue _queue = new(); /* This event is used solely for the purpose of having an optimized sleep cycle when * we have to wait on an external event (Add or Remove for instance) @@ -171,7 +193,7 @@ public class EventQueue /// Enqueues the specified event /// /// The event to enqueue. - public void Enqueue(Event e) + public void Enqueue(T e) { do { @@ -216,7 +238,7 @@ public void Enqueue(Event e) /// /// /// - public Event? Dequeue(bool blockWhenEmpty) + public T? Dequeue(bool blockWhenEmpty) { SpinWait sw = new SpinWait(); @@ -229,7 +251,7 @@ public void Enqueue(Event e) if (cachedRemoveId == cachedAddId) { if (!blockWhenEmpty || _stopped != 0) - return null; + return default(T); // Spin a few times to see if something changes if (sw.Count <= SpinCount) @@ -261,11 +283,11 @@ public void Enqueue(Event e) continue; // Dequeue our work item - Event? e; + T? e; while (!_queue.TryDequeue(out e)) { if (!blockWhenEmpty || _stopped != 0) - return null; + return default(T); } return e;