ThreadUtility
ThreadUtility provides a set of static methods convenient
for working with threads.
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;
namespace NUnit.Framework.Internal
{
public static class ThreadUtility
{
private sealed class DelayClosure
{
private readonly Timer _timer;
private readonly WaitCallback _threadPoolWork;
private readonly object _state;
public DelayClosure(int milliseconds, WaitCallback threadPoolWork, object state)
{
_threadPoolWork = threadPoolWork;
_state = state;
_timer = new Timer(Callback, null, -1, -1);
_timer.Change(milliseconds, -1);
}
private void Callback(object _)
{
_timer.Dispose();
_threadPoolWork(_state);
}
}
private sealed class CheckOnAbortingThreadState
{
public Thread Thread { get; }
public int NativeId { get; }
public CheckOnAbortingThreadState(Thread thread, int nativeId)
{
Thread = thread;
NativeId = nativeId;
}
}
private enum WM : uint
{
CLOSE = 16
}
private const int ThreadAbortedCheckDelay = 100;
private static bool isNotOnWindows;
public static void Delay(int milliseconds, WaitCallback threadPoolWork, object state = null)
{
new DelayClosure(milliseconds, threadPoolWork, state);
}
public static void Abort(Thread thread, int nativeId = 0)
{
if (nativeId != 0)
DislodgeThreadInNativeMessageWait(thread, nativeId);
thread.Abort();
}
public static void Kill(Thread thread, int nativeId = 0)
{
Kill(thread, null, nativeId);
}
public static void Kill(Thread thread, object stateInfo, int nativeId = 0)
{
if (nativeId != 0)
DislodgeThreadInNativeMessageWait(thread, nativeId);
try {
if (stateInfo == null)
thread.Abort();
else
thread.Abort(stateInfo);
} catch (ThreadStateException) {
thread.Resume();
}
if ((thread.ThreadState & ThreadState.WaitSleepJoin) != 0)
thread.Interrupt();
}
private static void DislodgeThreadInNativeMessageWait(Thread thread, int nativeId)
{
if (nativeId == 0)
throw new ArgumentOutOfRangeException("nativeId", "Native thread ID must not be zero.");
Delay(100, CheckOnAbortingThread, new CheckOnAbortingThreadState(thread, nativeId));
}
private static void CheckOnAbortingThread(object state)
{
CheckOnAbortingThreadState checkOnAbortingThreadState = (CheckOnAbortingThreadState)state;
switch (checkOnAbortingThreadState.Thread.ThreadState) {
case ThreadState.Aborted:
return;
case ThreadState.AbortRequested:
PostThreadCloseMessage(checkOnAbortingThreadState.NativeId);
break;
}
Delay(100, CheckOnAbortingThread, state);
}
[SecuritySafeCritical]
public static int GetCurrentThreadNativeId()
{
if (!isNotOnWindows)
try {
return GetCurrentThreadId();
} catch (EntryPointNotFoundException) {
isNotOnWindows = true;
return 0;
}
return 0;
}
[SecuritySafeCritical]
private static void PostThreadCloseMessage(int nativeId)
{
if (!PostThreadMessage(nativeId, WM.CLOSE, IntPtr.Zero, IntPtr.Zero)) {
int lastWin32Error = Marshal.GetLastWin32Error();
if (lastWin32Error != 1444)
throw new Win32Exception(lastWin32Error);
}
}
[DllImport("kernel32.dll")]
private static extern int GetCurrentThreadId();
[DllImport("user32.dll", SetLastError = true)]
private static extern bool PostThreadMessage(int id, WM msg, IntPtr wParam, IntPtr lParam);
}
}