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();
                }
            }
        }