<PackageReference Include="NUnit" Version="3.6.0" />

EventPump

public class EventPump : IDisposable
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.
using NUnit.Framework.Interfaces; using System; using System.Threading; namespace NUnit.Framework.Internal.Execution { public class EventPump : IDisposable { private static readonly Logger log = InternalTrace.GetLogger("EventPump"); private readonly ITestListener _eventListener; private readonly EventQueue _events; private Thread _pumpThread; private int _pumpState; public EventPumpState PumpState => (EventPumpState)_pumpState; public string Name { get; set; } public EventPump(ITestListener eventListener, EventQueue events) { _eventListener = eventListener; _events = events; } public void Dispose() { Stop(); } public void Start() { if (Interlocked.CompareExchange(ref _pumpState, 1, 0) == 0) { _pumpThread = new Thread(PumpThreadProc) { Name = "EventPumpThread" + Name, Priority = ThreadPriority.Highest }; _pumpThread.Start(); } } public void Stop() { if (Interlocked.CompareExchange(ref _pumpState, 2, 1) == 1) { _events.Stop(); _pumpThread.Join(); } } private void PumpThreadProc() { try { while (true) { Event event = _events.Dequeue(PumpState == EventPumpState.Pumping); if (event == null) break; try { event.Send(_eventListener); } catch (Exception ex) { log.Error("Exception in event handler\r\n {0}", ex); } } } catch (Exception ex2) { log.Error("Exception in pump thread", ex2); } finally { _pumpState = 0; if (_events.Count > 0) log.Error("Event pump thread exiting with {0} events remaining"); } } } }