SingleThreadedTestSynchronizationContext
using System;
using System.Collections.Generic;
using System.Threading;
namespace NUnit.Framework.Internal
{
internal sealed class SingleThreadedTestSynchronizationContext : SynchronizationContext, IDisposable
{
private enum Status
{
NotStarted,
Running,
ShutDown
}
private struct ScheduledWork
{
private readonly SendOrPostCallback _callback;
private readonly object _state;
private readonly System.Threading.ManualResetEventSlim _finished;
public ScheduledWork(SendOrPostCallback callback, object state, System.Threading.ManualResetEventSlim finished)
{
_callback = callback;
_state = state;
_finished = finished;
}
public void Execute()
{
_callback(_state);
_finished?.Set();
}
}
private readonly Queue<ScheduledWork> _queue = new Queue<ScheduledWork>();
private Status status;
public override void Post(SendOrPostCallback d, object state)
{
Guard.ArgumentNotNull(d, "d");
AddWork(new ScheduledWork(d, state, null));
}
public override void Send(SendOrPostCallback d, object state)
{
Guard.ArgumentNotNull(d, "d");
if (SynchronizationContext.Current == this)
d(state);
else {
using (System.Threading.ManualResetEventSlim manualResetEventSlim = new System.Threading.ManualResetEventSlim()) {
AddWork(new ScheduledWork(d, state, manualResetEventSlim));
manualResetEventSlim.Wait();
}
}
}
private void AddWork(ScheduledWork work)
{
lock (_queue) {
if (status == Status.ShutDown)
throw CreateInvalidWhenShutDownException();
_queue.Enqueue(work);
Monitor.Pulse(_queue);
}
}
public void ShutDown()
{
lock (_queue) {
status = Status.ShutDown;
Monitor.Pulse(_queue);
if (_queue.Count != 0)
throw new InvalidOperationException("Shutting down SingleThreadedTestSynchronizationContext with work still in the queue.");
}
}
private static InvalidOperationException CreateInvalidWhenShutDownException()
{
return new InvalidOperationException("This SingleThreadedTestSynchronizationContext has been shut down.");
}
public void Run()
{
lock (_queue) {
switch (status) {
case Status.Running:
throw new InvalidOperationException("SingleThreadedTestSynchronizationContext.Run may not be reentered.");
case Status.ShutDown:
throw CreateInvalidWhenShutDownException();
}
status = Status.Running;
}
ScheduledWork scheduledWork;
while (TryTake(out scheduledWork)) {
scheduledWork.Execute();
}
}
private bool TryTake(out ScheduledWork scheduledWork)
{
lock (_queue) {
while (true) {
if (status == Status.ShutDown) {
scheduledWork = default(ScheduledWork);
return false;
}
if (_queue.Count != 0)
break;
Monitor.Wait(_queue);
}
scheduledWork = _queue.Dequeue();
}
return true;
}
public void Dispose()
{
ShutDown();
}
}
}