/// <summary> /// Entry point. /// </summary> public static void Main() { var threadExitCallbackDelegatePtr = Marshal.GetFunctionPointerForDelegate(ThreadExitCallbackDelegate); var callbackId = UnmanagedThread.SetThreadExitCallback(threadExitCallbackDelegatePtr); for (var i = 1; i <= ThreadCount; i++) { var threadLocalVal = i; var thread = new Thread(_ => { Console.WriteLine($"Managed thread #{threadLocalVal} started."); UnmanagedThread.EnableCurrentThreadExitEvent(callbackId, new IntPtr(threadLocalVal)); Thread.Sleep(100); Console.WriteLine($"Managed thread #{threadLocalVal} ended."); }); thread.Start(); } UnmanagedThreadsExitEvent.Wait(); Console.WriteLine("All unmanaged threads have exited."); UnmanagedThread.RemoveThreadExitCallback(callbackId); }
public void TestInvalidCallbackIdThrowsException() { Assert.Throws <InvalidOperationException>(() => UnmanagedThread.EnableCurrentThreadExitEvent(int.MaxValue, new IntPtr(1))); Assert.Throws <InvalidOperationException>(() => UnmanagedThread.RemoveThreadExitCallback(int.MaxValue)); }
public void TestThreadExitFiresWhenEnabled([Values(true, false)] bool enableThreadExitCallback) { using (var evt = new ManualResetEventSlim()) { var threadLocalVal = new IntPtr(42); var resultThreadLocalVal = IntPtr.Zero; UnmanagedThread.ThreadExitCallback callback = val => { // ReSharper disable once AccessToDisposedClosure evt.Set(); resultThreadLocalVal = val; }; GC.KeepAlive(callback); var callbackId = UnmanagedThread.SetThreadExitCallback(Marshal.GetFunctionPointerForDelegate(callback)); try { ParameterizedThreadStart threadStart = _ => { if (enableThreadExitCallback) { UnmanagedThread.EnableCurrentThreadExitEvent(callbackId, threadLocalVal); } }; var t = new Thread(threadStart); t.Start(); t.Join(); var threadExitCallbackCalled = evt.Wait(TimeSpan.FromSeconds(1)); Assert.AreEqual(enableThreadExitCallback, threadExitCallbackCalled); Assert.AreEqual(enableThreadExitCallback ? threadLocalVal : IntPtr.Zero, resultThreadLocalVal); } finally { UnmanagedThread.RemoveThreadExitCallback(callbackId); } } }