diff --git a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ControlledExecution.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ControlledExecution.CoreCLR.cs
index 3b419f87f8b521..b6c437b351b7b1 100644
--- a/src/coreclr/System.Private.CoreLib/src/System/Runtime/ControlledExecution.CoreCLR.cs
+++ b/src/coreclr/System.Private.CoreLib/src/System/Runtime/ControlledExecution.CoreCLR.cs
@@ -42,11 +42,6 @@ public static partial class ControlledExecution
[Obsolete(Obsoletions.ControlledExecutionRunMessage, DiagnosticId = Obsoletions.ControlledExecutionRunDiagId, UrlFormat = Obsoletions.SharedUrlFormat)]
public static void Run(Action action, CancellationToken cancellationToken)
{
- if (!OperatingSystem.IsWindows())
- {
- throw new PlatformNotSupportedException();
- }
-
ArgumentNullException.ThrowIfNull(action);
// ControlledExecution.Run does not support nested invocations. If there's one already in flight
diff --git a/src/coreclr/pal/src/exception/seh-unwind.cpp b/src/coreclr/pal/src/exception/seh-unwind.cpp
index 9fe6c61d0f8286..f718be6af54c27 100644
--- a/src/coreclr/pal/src/exception/seh-unwind.cpp
+++ b/src/coreclr/pal/src/exception/seh-unwind.cpp
@@ -558,6 +558,9 @@ void GetContextPointers(unw_cursor_t *cursor, unw_context_t *unwContext, KNONVOL
// Frame pointer relative offset of a local containing a pointer to the windows style context of a location
// where a hardware exception occurred.
int g_hardware_exception_context_locvar_offset = 0;
+// Frame pointer relative offset of a local containing a pointer to the windows style context of a location
+// where an activation signal interrupted the thread.
+int g_inject_activation_context_locvar_offset = 0;
BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextPointers)
{
@@ -579,6 +582,18 @@ BOOL PAL_VirtualUnwind(CONTEXT *context, KNONVOLATILE_CONTEXT_POINTERS *contextP
return TRUE;
}
+ // Check if the PC is the return address from the InvokeActivationHandler.
+ // If that's the case, extract its local variable containing a pointer to the windows style context of the activation
+ // injection location and return that. This skips the signal handler trampoline that the libunwind
+ // cannot cross on some systems.
+ if ((void*)curPc == g_InvokeActivationHandlerReturnAddress)
+ {
+ CONTEXT* activationContext = (CONTEXT*)(CONTEXTGetFP(context) + g_inject_activation_context_locvar_offset);
+ memcpy_s(context, sizeof(CONTEXT), activationContext, sizeof(CONTEXT));
+
+ return TRUE;
+ }
+
if ((context->ContextFlags & CONTEXT_EXCEPTION_ACTIVE) != 0)
{
// The current frame is a source of hardware exception. Due to the fact that
diff --git a/src/coreclr/pal/src/exception/seh.cpp b/src/coreclr/pal/src/exception/seh.cpp
index 6b02ba34e26f8e..2045084a5683e8 100644
--- a/src/coreclr/pal/src/exception/seh.cpp
+++ b/src/coreclr/pal/src/exception/seh.cpp
@@ -61,6 +61,7 @@ PGET_GCMARKER_EXCEPTION_CODE g_getGcMarkerExceptionCode = NULL;
// Return address of the SEHProcessException, which is used to enable walking over
// the signal handler trampoline on some Unixes where the libunwind cannot do that.
void* g_SEHProcessExceptionReturnAddress = NULL;
+void* g_InvokeActivationHandlerReturnAddress = NULL;
/* Internal function definitions **********************************************/
diff --git a/src/coreclr/pal/src/exception/signal.cpp b/src/coreclr/pal/src/exception/signal.cpp
index 604c9ffb1c069c..108b64ae9954d1 100644
--- a/src/coreclr/pal/src/exception/signal.cpp
+++ b/src/coreclr/pal/src/exception/signal.cpp
@@ -73,6 +73,7 @@ typedef void (*SIGFUNC)(int, siginfo_t *, void *);
static void sigterm_handler(int code, siginfo_t *siginfo, void *context);
#ifdef INJECT_ACTIVATION_SIGNAL
static void inject_activation_handler(int code, siginfo_t *siginfo, void *context);
+extern void* g_InvokeActivationHandlerReturnAddress;
#endif
static void sigill_handler(int code, siginfo_t *siginfo, void *context);
@@ -775,6 +776,29 @@ static void sigterm_handler(int code, siginfo_t *siginfo, void *context)
}
#ifdef INJECT_ACTIVATION_SIGNAL
+
+/*++
+Function :
+ InvokeActivationHandler
+
+ Invoke the registered activation handler.
+ It also saves the return address (inject_activation_handler) so that PAL_VirtualUnwind can detect that
+ it has reached that method and use the context stored in the winContext there to unwind to the code
+ where the activation was injected. This is necessary on Alpine Linux where the libunwind cannot correctly
+ unwind past the signal frame.
+
+Parameters :
+ Windows style context of the location where the activation was injected
+
+(no return value)
+--*/
+__attribute__((noinline))
+static void InvokeActivationHandler(CONTEXT *pWinContext)
+{
+ g_InvokeActivationHandlerReturnAddress = __builtin_return_address(0);
+ g_activationFunction(pWinContext);
+}
+
/*++
Function :
inject_activation_handler
@@ -803,15 +827,26 @@ static void inject_activation_handler(int code, siginfo_t *siginfo, void *contex
native_context_t *ucontext = (native_context_t *)context;
CONTEXT winContext;
+ // Pre-populate context with data from current frame, because ucontext doesn't have some data (e.g. SS register)
+ // which is required for restoring context
+ RtlCaptureContext(&winContext);
+
+ ULONG contextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT;
+
+#if defined(HOST_AMD64)
+ contextFlags |= CONTEXT_XSTATE;
+#endif
+
CONTEXTFromNativeContext(
ucontext,
&winContext,
- CONTEXT_CONTROL | CONTEXT_INTEGER);
+ contextFlags);
if (g_safeActivationCheckFunction(CONTEXTGetPC(&winContext), /* checkingCurrentThread */ TRUE))
{
+ g_inject_activation_context_locvar_offset = (int)((char*)&winContext - (char*)__builtin_frame_address(0));
int savedErrNo = errno; // Make sure that errno is not modified
- g_activationFunction(&winContext);
+ InvokeActivationHandler(&winContext);
errno = savedErrNo;
// Activation function may have modified the context, so update it.
diff --git a/src/coreclr/pal/src/include/pal/seh.hpp b/src/coreclr/pal/src/include/pal/seh.hpp
index 327fe0d7fb03e2..5109eca56d79bd 100644
--- a/src/coreclr/pal/src/include/pal/seh.hpp
+++ b/src/coreclr/pal/src/include/pal/seh.hpp
@@ -148,7 +148,9 @@ CorUnix::PAL_ERROR SEHDisable(CorUnix::CPalThread *pthrCurrent);
// Offset of the local variable containing pointer to windows style context in the common_signal_handler / PAL_DispatchException function.
// This offset is relative to the frame pointer.
extern int g_hardware_exception_context_locvar_offset;
-
+// Offset of the local variable containing pointer to windows style context in the inject_activation_handler.
+// This offset is relative to the frame pointer.
+extern int g_inject_activation_context_locvar_offset;
#endif /* _PAL_SEH_HPP_ */
diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h
index 59588b5dc6af9b..e8888207bd5ee4 100644
--- a/src/coreclr/vm/threads.h
+++ b/src/coreclr/vm/threads.h
@@ -3837,7 +3837,7 @@ class Thread
#endif
}
- void UnmarkRedirectContextInUse(PTR_CONTEXT pCtx)
+ bool UnmarkRedirectContextInUse(PTR_CONTEXT pCtx)
{
LIMITED_METHOD_CONTRACT;
#ifdef _DEBUG
@@ -3848,6 +3848,7 @@ class Thread
m_RedirectContextInUse = false;
}
#endif
+ return (pCtx == m_pSavedRedirectContext);
}
#endif //DACCESS_COMPILE
diff --git a/src/coreclr/vm/threadsuspend.cpp b/src/coreclr/vm/threadsuspend.cpp
index 558d1775976acf..29332af32e493e 100644
--- a/src/coreclr/vm/threadsuspend.cpp
+++ b/src/coreclr/vm/threadsuspend.cpp
@@ -1167,6 +1167,15 @@ void Thread::SetAbortEndTime(ULONGLONG endTime, BOOL fRudeAbort)
}
+bool UseActivationInjection()
+{
+#ifdef TARGET_UNIX
+ return true;
+#else
+ return Thread::UseSpecialUserModeApc();
+#endif
+}
+
#ifdef _PREFAST_
#pragma warning(push)
#pragma warning(disable:21000) // Suppress PREFast warning about overly large function
@@ -1227,176 +1236,190 @@ Thread::UserAbort(EEPolicy::ThreadAbortTypes abortType, DWORD timeout)
_ASSERTE(this != pCurThread); // Aborting another thread.
- if (!UseSpecialUserModeApc())
- {
#ifdef _DEBUG
- DWORD elapsed_time = 0;
+ DWORD elapsed_time = 0;
#endif
- // We do not want this thread to be alerted.
- ThreadPreventAsyncHolder preventAsync(pCurThread != NULL);
+ // We do not want this thread to be alerted.
+ ThreadPreventAsyncHolder preventAsync(pCurThread != NULL);
#ifdef _DEBUG
- // If UserAbort times out, put up msgbox once.
- BOOL fAlreadyAssert = FALSE;
+ // If UserAbort times out, put up msgbox once.
+ BOOL fAlreadyAssert = FALSE;
#endif
#if !defined(DISABLE_THREADSUSPEND)
- DWORD dwSwitchCount = 0;
+ DWORD dwSwitchCount = 0;
#endif // !defined(DISABLE_THREADSUSPEND)
- while (true)
+ while (true)
+ {
+ // Lock the thread store
+ LOG((LF_SYNC, INFO3, "UserAbort obtain lock\n"));
+
+ ULONGLONG abortEndTime = GetAbortEndTime();
+ if (abortEndTime != MAXULONGLONG)
{
- // Lock the thread store
- LOG((LF_SYNC, INFO3, "UserAbort obtain lock\n"));
+ ULONGLONG now_time = CLRGetTickCount64();
- ULONGLONG abortEndTime = GetAbortEndTime();
- if (abortEndTime != MAXULONGLONG)
+ if (now_time >= abortEndTime)
{
- ULONGLONG now_time = CLRGetTickCount64();
-
- if (now_time >= abortEndTime)
- {
- // timeout, but no action on timeout.
- // Debugger can call this function to abort func-eval with a timeout
- return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
- }
+ // timeout, but no action on timeout.
+ // Debugger can call this function to abort func-eval with a timeout
+ return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
}
+ }
- // Thread abort needs to walk stack to decide if thread abort can proceed.
- // It is unsafe to crawl a stack of thread if the thread is OS-suspended which we do during
- // thread abort. For example, Thread T1 aborts thread T2. T2 is suspended by T1. Inside SQL
- // this means that no thread sharing the same scheduler with T2 can run. If T1 needs a lock which
- // is owned by one thread on the scheduler, T1 will wait forever.
- // Our solution is to move T2 to a safe point, resume it, and then do stack crawl.
+ // Thread abort needs to walk stack to decide if thread abort can proceed.
+ // It is unsafe to crawl a stack of thread if the thread is OS-suspended which we do during
+ // thread abort. For example, Thread T1 aborts thread T2. T2 is suspended by T1. Inside SQL
+ // this means that no thread sharing the same scheduler with T2 can run. If T1 needs a lock which
+ // is owned by one thread on the scheduler, T1 will wait forever.
+ // Our solution is to move T2 to a safe point, resume it, and then do stack crawl.
- // We need to make sure that ThreadStoreLock is released after CheckForAbort. This makes sure
- // that ThreadAbort does not race against GC.
- class CheckForAbort
+ // We need to make sure that ThreadStoreLock is released after CheckForAbort. This makes sure
+ // that ThreadAbort does not race against GC.
+ class CheckForAbort
+ {
+ private:
+ Thread *m_pThread;
+ BOOL m_fHoldingThreadStoreLock;
+ BOOL m_NeedRelease;
+ public:
+ CheckForAbort(Thread *pThread, BOOL fHoldingThreadStoreLock)
+ : m_pThread(pThread),
+ m_fHoldingThreadStoreLock(fHoldingThreadStoreLock),
+ m_NeedRelease(FALSE)
{
- private:
- Thread *m_pThread;
- BOOL m_fHoldingThreadStoreLock;
- BOOL m_NeedRelease;
- public:
- CheckForAbort(Thread *pThread, BOOL fHoldingThreadStoreLock)
- : m_pThread(pThread),
- m_fHoldingThreadStoreLock(fHoldingThreadStoreLock),
- m_NeedRelease(TRUE)
- {
- if (!fHoldingThreadStoreLock)
- {
- ThreadSuspend::LockThreadStore(ThreadSuspend::SUSPEND_OTHER);
- }
- ThreadStore::ResetStackCrawlEvent();
-
- // The thread being aborted may clear the TS_AbortRequested bit and the matching increment
- // of g_TrapReturningThreads behind our back. Increment g_TrapReturningThreads here
- // to ensure that we stop for the stack crawl even if the TS_AbortRequested bit is cleared.
- ThreadStore::TrapReturningThreads(TRUE);
- }
- void NeedStackCrawl()
- {
- m_pThread->SetThreadState(Thread::TS_StackCrawlNeeded);
- }
- ~CheckForAbort()
+ }
+ void Activate()
+ {
+ m_NeedRelease = TRUE;
+ if (!m_fHoldingThreadStoreLock)
{
- Release();
+ ThreadSuspend::LockThreadStore(ThreadSuspend::SUSPEND_OTHER);
}
- void Release()
+ ThreadStore::ResetStackCrawlEvent();
+
+ // The thread being aborted may clear the TS_AbortRequested bit and the matching increment
+ // of g_TrapReturningThreads behind our back. Increment g_TrapReturningThreads here
+ // to ensure that we stop for the stack crawl even if the TS_AbortRequested bit is cleared.
+ ThreadStore::TrapReturningThreads(TRUE);
+ }
+ void NeedStackCrawl()
+ {
+ m_pThread->SetThreadState(Thread::TS_StackCrawlNeeded);
+ }
+ ~CheckForAbort()
+ {
+ Release();
+ }
+ void Release()
+ {
+ if (m_NeedRelease)
{
- if (m_NeedRelease)
+ m_NeedRelease = FALSE;
+ ThreadStore::TrapReturningThreads(FALSE);
+ ThreadStore::SetStackCrawlEvent();
+ m_pThread->ResetThreadState(TS_StackCrawlNeeded);
+ if (!m_fHoldingThreadStoreLock)
{
- m_NeedRelease = FALSE;
- ThreadStore::TrapReturningThreads(FALSE);
- ThreadStore::SetStackCrawlEvent();
- m_pThread->ResetThreadState(TS_StackCrawlNeeded);
- if (!m_fHoldingThreadStoreLock)
- {
- ThreadSuspend::UnlockThreadStore();
- }
+ ThreadSuspend::UnlockThreadStore();
}
}
- };
- CheckForAbort checkForAbort(this, fHoldingThreadStoreLock);
+ }
+ };
+ CheckForAbort checkForAbort(this, fHoldingThreadStoreLock);
+ if (!UseActivationInjection())
+ {
+ checkForAbort.Activate();
+ }
- // We own TS lock. The state of the Thread can not be changed.
- if (m_State & TS_Unstarted)
- {
- // This thread is not yet started.
+ // We own TS lock. The state of the Thread can not be changed.
+ if (m_State & TS_Unstarted)
+ {
+ // This thread is not yet started.
#ifdef _DEBUG
- m_dwAbortPoint = 2;
+ m_dwAbortPoint = 2;
#endif
- return S_OK;
- }
+ return S_OK;
+ }
- if (GetThreadHandle() == INVALID_HANDLE_VALUE &&
- (m_State & TS_Unstarted) == 0)
- {
- // The thread is going to die or is already dead.
- UnmarkThreadForAbort();
+ if (GetThreadHandle() == INVALID_HANDLE_VALUE &&
+ (m_State & TS_Unstarted) == 0)
+ {
+ // The thread is going to die or is already dead.
+ UnmarkThreadForAbort();
#ifdef _DEBUG
- m_dwAbortPoint = 3;
+ m_dwAbortPoint = 3;
#endif
- return S_OK;
- }
+ return S_OK;
+ }
- // What if someone else has this thread suspended already? It'll depend where the
- // thread got suspended.
- //
- // User Suspend:
- // We'll just set the abort bit and hope for the best on the resume.
- //
- // GC Suspend:
- // If it's suspended in jitted code, we'll hijack the IP.
- // Consider race w/ GC suspension
- // If it's suspended but not in jitted code, we'll get suspended for GC, the GC
- // will complete, and then we'll abort the target thread.
- //
+ // What if someone else has this thread suspended already? It'll depend where the
+ // thread got suspended.
+ //
+ // User Suspend:
+ // We'll just set the abort bit and hope for the best on the resume.
+ //
+ // GC Suspend:
+ // If it's suspended in jitted code, we'll hijack the IP.
+ // Consider race w/ GC suspension
+ // If it's suspended but not in jitted code, we'll get suspended for GC, the GC
+ // will complete, and then we'll abort the target thread.
+ //
- // It's possible that the thread has completed the abort already.
- //
- if (!(m_State & TS_AbortRequested))
- {
+ // It's possible that the thread has completed the abort already.
+ //
+ if (!(m_State & TS_AbortRequested))
+ {
#ifdef _DEBUG
- m_dwAbortPoint = 4;
+ m_dwAbortPoint = 4;
#endif
- return S_OK;
- }
+ return S_OK;
+ }
- // If a thread is Dead or Detached, abort is a NOP.
- //
- if (m_State & (TS_Dead | TS_Detached | TS_TaskReset))
- {
- UnmarkThreadForAbort();
+ // If a thread is Dead or Detached, abort is a NOP.
+ //
+ if (m_State & (TS_Dead | TS_Detached | TS_TaskReset))
+ {
+ UnmarkThreadForAbort();
#ifdef _DEBUG
- m_dwAbortPoint = 5;
+ m_dwAbortPoint = 5;
#endif
- return S_OK;
- }
+ return S_OK;
+ }
- // It's possible that some stub notices the AbortRequested bit -- even though we
- // haven't done any real magic yet. If the thread has already started it's abort, we're
- // done.
- //
- // Two more cases can be folded in here as well. If the thread is unstarted, it'll
- // abort when we start it.
- //
- // If the thread is user suspended (SyncSuspended) -- we're out of luck. Set the bit and
- // hope for the best on resume.
- //
- if ((m_State & TS_AbortInitiated) && !IsRudeAbort())
- {
+ // It's possible that some stub notices the AbortRequested bit -- even though we
+ // haven't done any real magic yet. If the thread has already started it's abort, we're
+ // done.
+ //
+ // Two more cases can be folded in here as well. If the thread is unstarted, it'll
+ // abort when we start it.
+ //
+ // If the thread is user suspended (SyncSuspended) -- we're out of luck. Set the bit and
+ // hope for the best on resume.
+ //
+ if ((m_State & TS_AbortInitiated) && !IsRudeAbort())
+ {
#ifdef _DEBUG
- m_dwAbortPoint = 6;
+ m_dwAbortPoint = 6;
#endif
- break;
- }
+ break;
+ }
+#ifdef FEATURE_THREAD_ACTIVATION
+ if (UseActivationInjection())
+ {
+ InjectActivation(ActivationReason::ThreadAbort);
+ }
+ else
+#endif // FEATURE_THREAD_ACTIVATION
+ {
BOOL fOutOfRuntime = FALSE;
BOOL fNeedStackCrawl = FALSE;
@@ -1595,32 +1618,33 @@ Thread::UserAbort(EEPolicy::ThreadAbortTypes abortType, DWORD timeout)
LPrepareRetry:
checkForAbort.Release();
+ }
- // Don't do a Sleep. It's possible that the thread we are trying to abort is
- // stuck in unmanaged code trying to get into the apartment that we are supposed
- // to be pumping! Instead, ping the current thread's handle. Obviously this
- // will time out, but it will pump if we need it to.
- if (pCurThread)
- {
- pCurThread->Join(ABORT_POLL_TIMEOUT, TRUE);
- }
- else
- {
- ClrSleepEx(ABORT_POLL_TIMEOUT, FALSE);
- }
+ // Don't do a Sleep. It's possible that the thread we are trying to abort is
+ // stuck in unmanaged code trying to get into the apartment that we are supposed
+ // to be pumping! Instead, ping the current thread's handle. Obviously this
+ // will time out, but it will pump if we need it to.
+ if (pCurThread)
+ {
+ pCurThread->Join(ABORT_POLL_TIMEOUT, TRUE);
+ }
+ else
+ {
+ ClrSleepEx(ABORT_POLL_TIMEOUT, FALSE);
+ }
#ifdef _DEBUG
- elapsed_time += ABORT_POLL_TIMEOUT;
- if (g_pConfig->GetGCStressLevel() == 0 && !fAlreadyAssert)
- {
- _ASSERTE(elapsed_time < ABORT_FAIL_TIMEOUT);
- fAlreadyAssert = TRUE;
- }
+ elapsed_time += ABORT_POLL_TIMEOUT;
+ if (g_pConfig->GetGCStressLevel() == 0 && !fAlreadyAssert)
+ {
+ _ASSERTE(elapsed_time < ABORT_FAIL_TIMEOUT);
+ fAlreadyAssert = TRUE;
+ }
#endif
- } // while (true)
- }
+ } // while (true)
+
if ((GetAbortEndTime() != MAXULONGLONG) && IsAbortRequested())
{
while (TRUE)
@@ -1630,13 +1654,6 @@ Thread::UserAbort(EEPolicy::ThreadAbortTypes abortType, DWORD timeout)
return S_OK;
}
-#ifdef FEATURE_THREAD_ACTIVATION
- if (UseSpecialUserModeApc())
- {
- InjectActivation(ActivationReason::ThreadAbort);
- }
-#endif // FEATURE_THREAD_ACTIVATION
-
ULONGLONG curTime = CLRGetTickCount64();
if (curTime >= GetAbortEndTime())
{
@@ -2479,8 +2496,10 @@ void RedirectedThreadFrame::ExceptionUnwind()
Thread* pThread = GetThread();
// Allow future use to avoid repeatedly new'ing
- pThread->UnmarkRedirectContextInUse(m_Regs);
- m_Regs = NULL;
+ if (pThread->UnmarkRedirectContextInUse(m_Regs))
+ {
+ m_Regs = NULL;
+ }
}
#ifndef TARGET_UNIX
@@ -5862,9 +5881,15 @@ void HandleSuspensionForInterruptedThread(CONTEXT *interruptedContext)
pThread->PulseGCMode();
- frame.Pop(pThread);
+ INSTALL_MANAGED_EXCEPTION_DISPATCHER;
+ INSTALL_UNWIND_AND_CONTINUE_HANDLER;
pThread->HandleThreadAbort();
+
+ UNINSTALL_UNWIND_AND_CONTINUE_HANDLER;
+ UNINSTALL_MANAGED_EXCEPTION_DISPATCHER;
+
+ frame.Pop(pThread);
}
else
{
@@ -5981,7 +6006,7 @@ bool Thread::InjectActivation(ActivationReason reason)
_ASSERTE(success);
return true;
#elif defined(TARGET_UNIX)
- _ASSERTE(reason == ActivationReason::SuspendForGC);
+ _ASSERTE((reason == ActivationReason::SuspendForGC) || (reason == ActivationReason::ThreadAbort));
static ConfigDWORD injectionEnabled;
if (injectionEnabled.val(CLRConfig::INTERNAL_ThreadSuspendInjection) == 0)
diff --git a/src/libraries/System.Runtime/tests/System/Runtime/ControlledExecutionTests.cs b/src/libraries/System.Runtime/tests/System/Runtime/ControlledExecutionTests.cs
index 2d615b36ce9042..469e3cfd60b823 100644
--- a/src/libraries/System.Runtime/tests/System/Runtime/ControlledExecutionTests.cs
+++ b/src/libraries/System.Runtime/tests/System/Runtime/ControlledExecutionTests.cs
@@ -33,7 +33,6 @@ public void CancelOnTimeout()
// Tests that catch blocks are not aborted. The action catches the ThreadAbortException and throws an exception of a different type.
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoRuntime), nameof(PlatformDetection.IsNotNativeAot))]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/72703", TestPlatforms.AnyUnix)]
public void CancelOnTimeout_ThrowFromCatch()
{
var cts = new CancellationTokenSource();
@@ -48,7 +47,6 @@ public void CancelOnTimeout_ThrowFromCatch()
// Tests that finally blocks are not aborted. The action throws an exception from a finally block.
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoRuntime), nameof(PlatformDetection.IsNotNativeAot))]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/72703", TestPlatforms.AnyUnix)]
public void CancelOnTimeout_ThrowFromFinally()
{
var cts = new CancellationTokenSource();
@@ -61,7 +59,6 @@ public void CancelOnTimeout_ThrowFromFinally()
// Tests that finally blocks are not aborted. The action throws an exception from a try block.
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoRuntime), nameof(PlatformDetection.IsNotNativeAot))]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/72703", TestPlatforms.AnyUnix)]
public void CancelOnTimeout_Finally()
{
var cts = new CancellationTokenSource();
@@ -75,7 +72,6 @@ public void CancelOnTimeout_Finally()
// Tests cancellation before calling the Run method
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoRuntime), nameof(PlatformDetection.IsNotNativeAot))]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/72703", TestPlatforms.AnyUnix)]
public void CancelBeforeRun()
{
var cts = new CancellationTokenSource();
@@ -88,7 +84,6 @@ public void CancelBeforeRun()
// Tests cancellation by the action itself
[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotMonoRuntime), nameof(PlatformDetection.IsNotNativeAot))]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/72703", TestPlatforms.AnyUnix)]
public void CancelItself()
{
_cts = new CancellationTokenSource();