/// <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); }
/// <summary> /// Initializes a new instance of the <see cref="_instance"/> class. /// </summary> private Jvm(IntPtr jvmPtr) { Debug.Assert(jvmPtr != IntPtr.Zero); _jvmPtr = jvmPtr; var funcPtr = (JvmInterface **)jvmPtr; var func = **funcPtr; GetDelegate(func.AttachCurrentThread, out _attachCurrentThread); // JVM is a singleton, so this is one-time subscription. // This is a shortcut - we pass DetachCurrentThread pointer directly as a thread exit callback, // because signatures happen to match exactly. _threadExitCallbackId = UnmanagedThread.SetThreadExitCallback(func.DetachCurrentThread); var env = AttachCurrentThread(); _methodId = new MethodId(env); // Keep AppDomain check here to avoid JITting GetCallbacksFromDefaultDomain method on .NET Core // (which fails due to _AppDomain usage). _callbacks = AppDomain.CurrentDomain.IsDefaultAppDomain() ? new Callbacks(env, this) : GetCallbacksFromDefaultDomain(); }
public void TestInvalidCallbackIdThrowsException() { Assert.Throws <InvalidOperationException>(() => UnmanagedThread.EnableCurrentThreadExitEvent(int.MaxValue, new IntPtr(1))); Assert.Throws <InvalidOperationException>(() => UnmanagedThread.RemoveThreadExitCallback(int.MaxValue)); }
/// <summary> /// Attaches current thread to the JVM and returns JNIEnv. /// </summary> public Env AttachCurrentThread() { if (_env == null) { IntPtr envPtr; var res = _attachCurrentThread(_jvmPtr, out envPtr, IntPtr.Zero); if (res != JniResult.Success) { throw new IgniteException("AttachCurrentThread failed: " + res); } _env = new Env(envPtr, this); UnmanagedThread.EnableCurrentThreadExitEvent(_threadExitCallbackId, _jvmPtr); } return(_env); }
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); } } }