MessagePumpStrategy
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
namespace NUnit.Framework.Internal
{
[NullableContext(1)]
[Nullable(0)]
internal abstract class MessagePumpStrategy
{
[Nullable(0)]
private sealed class NoMessagePumpStrategy : MessagePumpStrategy
{
public static readonly NoMessagePumpStrategy Instance = new NoMessagePumpStrategy();
private NoMessagePumpStrategy()
{
}
public override void WaitForCompletion(AwaitAdapter awaiter)
{
awaiter.BlockUntilCompleted();
}
}
[Nullable(0)]
private sealed class WindowsFormsMessagePumpStrategy : MessagePumpStrategy
{
[Nullable(2)]
private static WindowsFormsMessagePumpStrategy _instance;
private readonly Action _applicationRun;
private readonly Action _applicationExit;
private WindowsFormsMessagePumpStrategy(Action applicationRun, Action applicationExit)
{
_applicationRun = applicationRun;
_applicationExit = applicationExit;
}
[NullableContext(2)]
public static MessagePumpStrategy GetIfApplicable()
{
if (!IsApplicable(SynchronizationContext.Current))
return null;
if (_instance == null) {
Type type = SynchronizationContext.Current.GetType().Assembly.GetType("System.Windows.Forms.Application", true);
Action applicationRun = (Action)type.GetMethod("Run", BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null).CreateDelegate(typeof(Action));
Action applicationExit = (Action)type.GetMethod("Exit", BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null).CreateDelegate(typeof(Action));
_instance = new WindowsFormsMessagePumpStrategy(applicationRun, applicationExit);
}
return _instance;
}
[NullableContext(2)]
private static bool IsApplicable([NotNullWhen(true)] SynchronizationContext context)
{
return context?.GetType().FullName == "System.Windows.Forms.WindowsFormsSynchronizationContext";
}
public override void WaitForCompletion(AwaitAdapter awaiter)
{
SynchronizationContext current = SynchronizationContext.Current;
if (!IsApplicable(current))
throw new InvalidOperationException("This strategy must only be used from a WindowsFormsSynchronizationContext.");
if (!awaiter.IsCompleted) {
current.Post(delegate {
ContinueOnSameSynchronizationContext(awaiter, _applicationExit);
}, awaiter);
try {
_applicationRun();
} finally {
SynchronizationContext.SetSynchronizationContext(current);
}
}
}
}
[Nullable(0)]
private sealed class WpfMessagePumpStrategy : MessagePumpStrategy
{
[Nullable(2)]
private static WpfMessagePumpStrategy _instance;
private readonly MethodInfo _dispatcherPushFrame;
private readonly MethodInfo _dispatcherFrameSetContinueProperty;
private readonly Type _dispatcherFrameType;
private WpfMessagePumpStrategy(MethodInfo dispatcherPushFrame, MethodInfo dispatcherFrameSetContinueProperty, Type dispatcherFrameType)
{
_dispatcherPushFrame = dispatcherPushFrame;
_dispatcherFrameSetContinueProperty = dispatcherFrameSetContinueProperty;
_dispatcherFrameType = dispatcherFrameType;
}
[NullableContext(2)]
public static MessagePumpStrategy GetIfApplicable()
{
SynchronizationContext current = SynchronizationContext.Current;
if (!IsApplicable(current))
return null;
if (_instance == null) {
Assembly assembly = current.GetType().Assembly;
Type type = assembly.GetType("System.Windows.Threading.Dispatcher", true);
Type type2 = assembly.GetType("System.Windows.Threading.DispatcherFrame", true);
MethodInfo method = type.GetMethod("PushFrame", BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public, null, new Type[1] {
type2
}, null);
MethodInfo dispatcherFrameSetContinueProperty = type2.GetProperty("Continue")?.GetSetMethod();
_instance = new WpfMessagePumpStrategy(method, dispatcherFrameSetContinueProperty, type2);
}
return _instance;
}
[NullableContext(2)]
private static bool IsApplicable([NotNullWhen(true)] SynchronizationContext context)
{
return context?.GetType().FullName == "System.Windows.Threading.DispatcherSynchronizationContext";
}
public override void WaitForCompletion(AwaitAdapter awaiter)
{
SynchronizationContext current = SynchronizationContext.Current;
if (!IsApplicable(current))
throw new InvalidOperationException("This strategy must only be used from a DispatcherSynchronizationContext.");
if (!awaiter.IsCompleted) {
object frame = Activator.CreateInstance(_dispatcherFrameType, true);
current.Post(delegate {
ContinueOnSameSynchronizationContext(awaiter, delegate {
_dispatcherFrameSetContinueProperty.Invoke(frame, new object[1] {
false
});
});
}, awaiter);
_dispatcherPushFrame.Invoke(null, new object[1] {
frame
});
}
}
}
[Nullable(0)]
private sealed class SingleThreadedTestMessagePumpStrategy : MessagePumpStrategy
{
public static readonly SingleThreadedTestMessagePumpStrategy Instance = new SingleThreadedTestMessagePumpStrategy();
private SingleThreadedTestMessagePumpStrategy()
{
}
public override void WaitForCompletion(AwaitAdapter awaiter)
{
SingleThreadedTestSynchronizationContext obj = SynchronizationContext.Current as SingleThreadedTestSynchronizationContext;
if (obj == null)
throw new InvalidOperationException("This strategy must only be used from a SingleThreadedTestSynchronizationContext.");
SingleThreadedTestSynchronizationContext context = obj;
if (!awaiter.IsCompleted) {
context.Post(delegate {
ContinueOnSameSynchronizationContext(awaiter, context.ShutDown);
}, awaiter);
context.Run();
}
}
}
public abstract void WaitForCompletion(AwaitAdapter awaiter);
public static MessagePumpStrategy FromCurrentSynchronizationContext()
{
SynchronizationContext current = SynchronizationContext.Current;
if (current is SingleThreadedTestSynchronizationContext)
return SingleThreadedTestMessagePumpStrategy.Instance;
return WindowsFormsMessagePumpStrategy.GetIfApplicable() ?? WpfMessagePumpStrategy.GetIfApplicable() ?? NoMessagePumpStrategy.Instance;
}
private static void ContinueOnSameSynchronizationContext(AwaitAdapter awaiter, Action continuation)
{
if (awaiter == null)
throw new ArgumentNullException("awaiter");
if (continuation == null)
throw new ArgumentNullException("continuation");
SynchronizationContext context = SynchronizationContext.Current;
awaiter.OnCompleted(delegate {
if (context == null || SynchronizationContext.Current == context)
continuation();
else
context.Post(delegate {
continuation();
}, continuation);
});
}
}
}