Skip to content

Commit

Permalink
Enabling extension of events (#4698)
Browse files Browse the repository at this point in the history
* Enabling extension of events

* Adding new types as templates, or base classes

* Fixing comments from Manfred.

* Fix SA1001

* Fix comment

* Fix dispose issue

* Fixed suggested changes.
  • Loading branch information
OsirisTerje committed Apr 23, 2024
1 parent 7b9497d commit 3525df5
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 19 deletions.
41 changes: 32 additions & 9 deletions src/NUnitFramework/framework/Internal/Execution/EventPump.cs
Expand Up @@ -29,12 +29,32 @@ public enum EventPumpState
}

/// <summary>
/// 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.
/// </summary>
public class EventPump : IDisposable
public sealed class EventPump : EventPump<Event, ITestListener>, IDisposable
{
/// <summary>
/// Constructor for standard EventPump
/// </summary>
/// <param name="eventListener">The EventListener to receive events</param>
/// <param name="events">The event queue to pull events from</param>
public EventPump(ITestListener eventListener, EventQueue<Event> events)
: base(eventListener, events, "Standard")
{
}
}

/// <summary>
/// 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.
/// </summary>
public abstract class EventPump<TEvent, TListener> : IDisposable
where TEvent : IEvent<TListener>
{
private static readonly Logger Log = InternalTrace.GetLogger("EventPump");

Expand All @@ -43,12 +63,12 @@ public class EventPump : IDisposable
/// <summary>
/// The downstream listener to which we send events
/// </summary>
private readonly ITestListener _eventListener;
private readonly TListener _eventListener;

/// <summary>
/// The queue that holds our events
/// </summary>
private readonly EventQueue _events;
private readonly EventQueue<TEvent> _events;

/// <summary>
/// Thread to do the pumping
Expand All @@ -63,15 +83,18 @@ public class EventPump : IDisposable
#endregion

#region Constructor

/// <summary>
/// Constructor
/// </summary>
/// <param name="eventListener">The EventListener to receive events</param>
/// <param name="events">The event queue to pull events from</param>
public EventPump(ITestListener eventListener, EventQueue events)
/// <param name="name">Name of the thread and pump</param>
protected EventPump(TListener eventListener, EventQueue<TEvent> events, string name = "Standard")
{
_eventListener = eventListener;
_events = events;
Name = name;
}

#endregion
Expand Down Expand Up @@ -111,7 +134,7 @@ public void Start()
{
_pumpThread = new Thread(PumpThreadProc)
{
Name = "EventPumpThread" + Name,
Name = $"{Name}EventPumpThread",
Priority = ThreadPriority.Highest
};

Expand Down Expand Up @@ -142,14 +165,14 @@ public void Stop()
/// </summary>
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
Expand Down
42 changes: 32 additions & 10 deletions src/NUnitFramework/framework/Internal/Execution/EventQueue.cs
Expand Up @@ -6,16 +6,29 @@

namespace NUnit.Framework.Internal.Execution
{
/// <summary>
/// Interface for ALL event types that can be queued for processing.
/// </summary>
/// <typeparam name="TListener"></typeparam>
public interface IEvent<in TListener>
{
/// <summary>
/// The Send method is implemented by derived classes to send the event to the specified listener.
/// </summary>
/// <param name="listener">The listener.</param>
void Send(TListener listener);
}

#region Individual Event Classes

/// <summary>
/// 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.
/// </summary>
public abstract class Event
public abstract class Event : IEvent<ITestListener>
{
/// <summary>
/// The Send method is implemented by derived classes to send the event to the specified listener.
Expand Down Expand Up @@ -134,16 +147,25 @@ public override void Send(ITestListener listener)
#endregion

/// <summary>
/// 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.
/// </summary>
public sealed class EventQueue : EventQueue<Event>
{
}

/// <summary>
/// Implements a template for a queue of work items each of which
/// is queued as a WaitCallback.
/// It can handle any event types.
/// </summary>
public class EventQueue
public abstract class EventQueue<T>
{
private const int SpinCount = 5;

// static readonly Logger log = InternalTrace.GetLogger("EventQueue");

private readonly ConcurrentQueue<Event> _queue = new();
private readonly ConcurrentQueue<T> _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)
Expand Down Expand Up @@ -171,7 +193,7 @@ public class EventQueue
/// Enqueues the specified event
/// </summary>
/// <param name="e">The event to enqueue.</param>
public void Enqueue(Event e)
public void Enqueue(T e)
{
do
{
Expand Down Expand Up @@ -216,7 +238,7 @@ public void Enqueue(Event e)
/// </item>
/// </list>
/// </returns>
public Event? Dequeue(bool blockWhenEmpty)
public T? Dequeue(bool blockWhenEmpty)
{
SpinWait sw = new SpinWait();

Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 3525df5

Please sign in to comment.