public static void SetRemoteStackTrace_IncludedInExceptionStackTrace() { Exception e; e = new Exception(); Assert.Same(e, ExceptionDispatchInfo.SetRemoteStackTrace(e, "pumpkin-anaconda-maritime")); // 3 randomly selected words Assert.Contains("pumpkin-anaconda-maritime", e.StackTrace, StringComparison.Ordinal); Assert.DoesNotContain("pumpkin-anaconda-maritime", new StackTrace(e).ToString(), StringComparison.Ordinal); // we shouldn't attempt to parse it in a StackTrace object e = new Exception(); Assert.Same(e, ExceptionDispatchInfo.SetRemoteStackTrace(e, "pumpkin-anaconda-maritime")); try { throw e; } catch { } Assert.Contains("pumpkin-anaconda-maritime", e.StackTrace, StringComparison.Ordinal); Assert.DoesNotContain("pumpkin-anaconda-maritime", new StackTrace(e).ToString(), StringComparison.Ordinal); }
public static void SetCurrentOrRemoteStackTrace_Invalid_Throws() { Exception e; // Null argument e = null; AssertExtensions.Throws <ArgumentNullException>("source", () => ExceptionDispatchInfo.SetCurrentStackTrace(e)); AssertExtensions.Throws <ArgumentNullException>("source", () => ExceptionDispatchInfo.SetRemoteStackTrace(e, "Hello")); AssertExtensions.Throws <ArgumentNullException>("stackTrace", () => ExceptionDispatchInfo.SetRemoteStackTrace(new Exception(), stackTrace: null)); // Previously set current stack e = new Exception(); ExceptionDispatchInfo.SetCurrentStackTrace(e); Assert.Throws <InvalidOperationException>(() => ExceptionDispatchInfo.SetCurrentStackTrace(e)); Assert.Throws <InvalidOperationException>(() => ExceptionDispatchInfo.SetRemoteStackTrace(e, "Hello")); // Previously thrown e = new Exception(); try { throw e; } catch { } Assert.Throws <InvalidOperationException>(() => ExceptionDispatchInfo.SetCurrentStackTrace(e)); Assert.Throws <InvalidOperationException>(() => ExceptionDispatchInfo.SetRemoteStackTrace(e, "Hello")); }
public static void Run(Action action, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(action); // ControlledExecution.Run does not support nested invocations. If there's one already in flight // on this thread, fail. if (t_executing) { throw new InvalidOperationException(SR.InvalidOperation_NestedControlledExecutionRun); } // Store the current thread so that it may be referenced by the Canceler.Cancel callback if one occurs. Canceler canceler = new(Thread.CurrentThread); try { // Mark this thread as now running a ControlledExecution.Run to prevent recursive usage. t_executing = true; // Register for aborting. From this moment until ctr.Unregister is called, this thread is subject to being // interrupted at any moment. This could happen during the call to UnsafeRegister if cancellation has // already been requested at the time of the registration. CancellationTokenRegistration ctr = cancellationToken.UnsafeRegister(e => ((Canceler)e !).Cancel(), canceler); try { // Invoke the caller's code. action(); } finally { // This finally block may be cloned by JIT for the non-exceptional code flow. In that case the code // below is not guarded against aborting. That is OK as the outer try block will catch the // ThreadAbortException and call ResetAbortThread. // Unregister the callback. Unlike Dispose, Unregister will not block waiting for an callback in flight // to complete, and will instead return false if the callback has already been invoked or is currently // in flight. if (!ctr.Unregister()) { // Wait until the callback has completed. Either the callback is already invoked and completed // (in which case IsCancelCompleted will be true), or it may still be in flight. If it's in flight, // the AbortThread call may be waiting for this thread to exit this finally block to exit, so while // spinning waiting for the callback to complete, we also need to call ResetAbortThread in order to // reset the flag the AbortThread call is polling in its waiting loop. SpinWait sw = default; while (!canceler.IsCancelCompleted) { ResetAbortThread(); sw.SpinOnce(); } } } } catch (ThreadAbortException tae) { // We don't want to leak ThreadAbortExceptions to user code. Instead, translate the exception into // an OperationCanceledException, preserving stack trace details from the ThreadAbortException in // order to aid in diagnostics and debugging. OperationCanceledException e = cancellationToken.IsCancellationRequested ? new(cancellationToken) : new(); if (tae.StackTrace is string stackTrace) { ExceptionDispatchInfo.SetRemoteStackTrace(e, stackTrace); } throw e; } finally { // Unmark this thread for recursion detection. t_executing = false; if (cancellationToken.IsCancellationRequested) { // Reset an abort request that may still be pending on this thread. ResetAbortThread(); } } }