From e50a02c609533fdd9a228041cbf26bf0724ff61a Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 15 Sep 2026 17:13:37 -0500 Subject: [PATCH 1/6] Fix Wasm async continuation stack walk Use the caller shadow stack virtual IP for the interpreter continuation transition frame and exercise both cold and warm FCall paths. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8a57fff8-6c30-4d3c-ac72-a80975122a96 --- src/coreclr/vm/interpexec.cpp | 3 +- src/tests/async/async-wasm.csproj | 11 +++ src/tests/async/async.csproj | 5 +- .../diagnostic-ip-stacktrace.cs | 70 ++++++++++--------- 4 files changed, 51 insertions(+), 38 deletions(-) create mode 100644 src/tests/async/async-wasm.csproj diff --git a/src/coreclr/vm/interpexec.cpp b/src/coreclr/vm/interpexec.cpp index 4753eacf58c814..b19d3a575a910f 100644 --- a/src/coreclr/vm/interpexec.cpp +++ b/src/coreclr/vm/interpexec.cpp @@ -1288,7 +1288,8 @@ FCIMPL2(ContinuationObject*, AsyncHelpers_ResumeInterpreterContinuation, Continu TransitionBlock transitionBlock{}; transitionBlock.m_StackPointer = callersStackPointer; - transitionBlock.m_ReturnAddress = (TADDR)&AsyncHelpers_ResumeInterpreterContinuation; + // Keep the return address consistent with the managed R2R frame represented by the caller's stack pointer. + transitionBlock.m_ReturnAddress = GetWasmVirtualIPFromStackPointer(callersStackPointer); return AsyncHelpers_ResumeInterpreterContinuationWorker(cont, resultStorage, &transitionBlock); } diff --git a/src/tests/async/async-wasm.csproj b/src/tests/async/async-wasm.csproj new file mode 100644 index 00000000000000..5c9949ee5e01c5 --- /dev/null +++ b/src/tests/async/async-wasm.csproj @@ -0,0 +1,11 @@ + + + true + true + + + + + + + diff --git a/src/tests/async/async.csproj b/src/tests/async/async.csproj index c7c5eb790b4efc..838e11a3ffe49b 100644 --- a/src/tests/async/async.csproj +++ b/src/tests/async/async.csproj @@ -1,9 +1,6 @@ - - true - - + diff --git a/src/tests/async/diagnostic-ip-stacktrace/diagnostic-ip-stacktrace.cs b/src/tests/async/diagnostic-ip-stacktrace/diagnostic-ip-stacktrace.cs index 8f092ed3dff93e..71458c3f6c96d5 100644 --- a/src/tests/async/diagnostic-ip-stacktrace/diagnostic-ip-stacktrace.cs +++ b/src/tests/async/diagnostic-ip-stacktrace/diagnostic-ip-stacktrace.cs @@ -11,44 +11,48 @@ public class RuntimeAsyncDiagnosticIPStackTrace [Fact] public static void TestEntryPoint() { - TaskCompletionSource suspension = new(); - Task task = Level1(suspension.Task); + // Exercise both the cold and warm ResumeInterpreterContinuation FCall paths. + for (int i = 0; i < 2; i++) + { + TaskCompletionSource suspension = new(); + Task task = Level1(suspension.Task); - Assert.False(task.IsCompleted); - suspension.SetResult(); - Assert.True(task.IsCompleted); + Assert.False(task.IsCompleted); + suspension.SetResult(); + Assert.True(task.IsCompleted); - InvalidOperationException? exception = null; - try - { - task.GetAwaiter().GetResult(); - } - catch (InvalidOperationException ex) - { - exception = ex; - } + InvalidOperationException? exception = null; + try + { + task.GetAwaiter().GetResult(); + } + catch (InvalidOperationException ex) + { + exception = ex; + } - Assert.NotNull(exception); - string stackTrace = exception.StackTrace; - Assert.NotNull(stackTrace); - Console.WriteLine(stackTrace); + Assert.NotNull(exception); + string stackTrace = exception.StackTrace; + Assert.NotNull(stackTrace); + Console.WriteLine(stackTrace); - string[] expectedMethods = - [ - nameof(ThrowAfterResume), - nameof(Level4), - nameof(Level3), - nameof(Level2), - nameof(Level1), - ]; + string[] expectedMethods = + [ + nameof(ThrowAfterResume), + nameof(Level4), + nameof(Level3), + nameof(Level2), + nameof(Level1), + ]; - int previousFrame = -1; - foreach (string method in expectedMethods) - { - string frame = $"{nameof(RuntimeAsyncDiagnosticIPStackTrace)}.{method}("; - int currentFrame = stackTrace.IndexOf(frame, StringComparison.Ordinal); - Assert.True(currentFrame > previousFrame, $"Expected '{frame}' after offset {previousFrame} in:{Environment.NewLine}{stackTrace}"); - previousFrame = currentFrame; + int previousFrame = -1; + foreach (string method in expectedMethods) + { + string frame = $"{nameof(RuntimeAsyncDiagnosticIPStackTrace)}.{method}("; + int currentFrame = stackTrace.IndexOf(frame, StringComparison.Ordinal); + Assert.True(currentFrame > previousFrame, $"Expected '{frame}' after offset {previousFrame} in:{Environment.NewLine}{stackTrace}"); + previousFrame = currentFrame; + } } } From b3b2ec9faa2d21b2d16fc2fb3d2f4618bdf93d06 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 15 Sep 2026 17:17:23 -0500 Subject: [PATCH 2/6] Link Wasm async test quarantine Add the active re-enable tracker beside the project-level WebAssembly exclusion. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8a57fff8-6c30-4d3c-ac72-a80975122a96 --- src/tests/async/Directory.Build.targets | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tests/async/Directory.Build.targets b/src/tests/async/Directory.Build.targets index dde2d82849f3d4..213e0f57ee7518 100644 --- a/src/tests/async/Directory.Build.targets +++ b/src/tests/async/Directory.Build.targets @@ -1,6 +1,7 @@ + true From f4d626b3684084197abc03360dcd0af2d145c6c8 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 15 Sep 2026 17:27:39 -0500 Subject: [PATCH 3/6] Revert "Link Wasm async test quarantine" This reverts commit b3b2ec9faa2d21b2d16fc2fb3d2f4618bdf93d06. --- src/tests/async/Directory.Build.targets | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tests/async/Directory.Build.targets b/src/tests/async/Directory.Build.targets index 213e0f57ee7518..dde2d82849f3d4 100644 --- a/src/tests/async/Directory.Build.targets +++ b/src/tests/async/Directory.Build.targets @@ -1,7 +1,6 @@ - true From c0b5d125b5fb961f15c3dbcd693c10ab9c0b98ff Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 15 Sep 2026 18:17:56 -0500 Subject: [PATCH 4/6] Enable supported runtime async tests on Wasm Guard synchronous-wait tests with the multithreading capability, quarantine the two exact unsupported Wasm cases, and preserve all native coverage. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8a57fff8-6c30-4d3c-ac72-a80975122a96 --- src/tests/async/Directory.Build.targets | 5 ++++- .../async-versions-task-adapters.cs | 4 ++-- src/tests/async/async-versions/async-versions.il | 12 ++++++++++-- src/tests/async/async-wasm.csproj | 11 ----------- src/tests/async/async.csproj | 3 +++ .../asyncversion-inline/asyncversion-inline.cs | 1 + .../async/awaitingnotasync/awaitingnotasync.cs | 2 +- src/tests/async/byref-param/byref-param.cs | 2 +- src/tests/async/collectible-alc/collectible-alc.cs | 1 + .../async/covariant-return/covariant-returns.cs | 10 +++++----- .../cse-array-index-byref/cse-array-index-byref.cs | 2 +- .../custom-struct-awaiters.cs | 2 +- src/tests/async/devirtualize/devirtualize.cs | 2 +- .../async/execution-context/execution-context.cs | 6 +++--- .../fibonacci-with-yields-struct-return.cs | 2 +- .../fibonacci-with-yields/fibonacci-with-yields.cs | 2 +- src/tests/async/implement/implement.cs | 2 +- .../inlined-asyncversion-frames.cs | 1 + .../inlined-frame-configureawait.cs | 1 + .../inlined-frame-contexts.cs | 1 + .../inlined-frame-exceptions.cs | 1 + .../inlined-frame-ldvirtftn-await.cs | 1 + .../inlined-frame-transition-suspends.cs | 1 + .../async/inst-unbox-thunks/inst-unbox-thunks.cs | 1 + src/tests/async/object/object.cs | 2 +- .../async/objects-captured/objects-captured.cs | 2 +- .../osr-inlined-contexts/osr-inlined-contexts.cs | 4 ++-- src/tests/async/override/override.cs | 2 +- .../parallel-continuations.cs | 2 +- src/tests/async/pgo/pgo.cs | 2 +- src/tests/async/pinvoke/pinvoke.cs | 1 + src/tests/async/reflection/reflection.cs | 14 +++++++------- src/tests/async/regression/125615.cs | 2 +- src/tests/async/regression/125805.cs | 2 +- src/tests/async/regression/132015.cs | 1 + src/tests/async/regression/132016.cs | 2 +- src/tests/async/regression/132017.cs | 2 +- src/tests/async/regression/managed-thread-id.cs | 1 + .../async/resumed-known-true/resumed-known-true.cs | 1 + src/tests/async/returns/returns.cs | 2 +- .../runtime-wrapped-exception.cs | 6 +++--- src/tests/async/shared-generic/shared-generic.cs | 2 +- src/tests/async/simple-eh/simple-eh.cs | 1 + src/tests/async/small/small.cs | 2 +- src/tests/async/staticvirtual/staticvirtual.cs | 2 +- .../async/strength-reduction/strength-reduction.cs | 2 +- src/tests/async/struct/struct.cs | 2 +- .../synchronization-context.cs | 8 ++++---- .../valuetask-source/valuetask-source-stub.cs | 2 +- .../async/valuetask-source/valuetask-source.cs | 6 +++--- src/tests/async/valuetask/valuetask.cs | 1 + src/tests/async/varying-yields/varying-yields.cs | 2 +- src/tests/async/void/void.cs | 2 +- .../async/widening-tailcall/widening-tailcall.cs | 2 +- src/tests/async/with-yields/with-yields.cs | 2 +- 55 files changed, 89 insertions(+), 71 deletions(-) delete mode 100644 src/tests/async/async-wasm.csproj diff --git a/src/tests/async/Directory.Build.targets b/src/tests/async/Directory.Build.targets index dde2d82849f3d4..3d52b6c9800968 100644 --- a/src/tests/async/Directory.Build.targets +++ b/src/tests/async/Directory.Build.targets @@ -1,7 +1,10 @@ - + true + + + diff --git a/src/tests/async/async-versions-task-adapters/async-versions-task-adapters.cs b/src/tests/async/async-versions-task-adapters/async-versions-task-adapters.cs index 509874dc4e14e5..a7179faa948358 100644 --- a/src/tests/async/async-versions-task-adapters/async-versions-task-adapters.cs +++ b/src/tests/async/async-versions-task-adapters/async-versions-task-adapters.cs @@ -12,7 +12,7 @@ public class Async2TaskAdapters { // Wrapping a runtime-async Task in a ValueTask (ValueTask(Task) constructor) and // awaiting the resulting ValueTask should suspend and resume correctly. - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestTaskToValueTask() { SynchronizationContext prevContext = SynchronizationContext.Current; @@ -83,7 +83,7 @@ private static ValueTask SourceValueTask() // to the ValueTask(TResult result) constructor, wrapping the Task as a value. // - return new ValueTask>(TaskOfTaskOfIntReturningFunction()) passes a Task>, // which binds to the ValueTask(Task task) constructor, an actual async call. - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestValueTaskOfTaskValueVersusAsync() { WrapValueVersusAsync().GetAwaiter().GetResult(); diff --git a/src/tests/async/async-versions/async-versions.il b/src/tests/async/async-versions/async-versions.il index da57c8fe7886a7..74edd05dcd2440 100644 --- a/src/tests/async/async-versions/async-versions.il +++ b/src/tests/async/async-versions/async-versions.il @@ -8,6 +8,8 @@ // completes synchronously (returns an already-completed task). .assembly extern System.Runtime { .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A) .ver 4:0:0:0 } +.assembly extern Microsoft.DotNet.XUnitExtensions { .publickeytoken = (31 BF 38 56 AD 36 4E 35) } +.assembly extern TestLibrary { .ver 0:0:0:0 } .assembly extern xunit.core {} .assembly 'async-versions' @@ -157,7 +159,10 @@ .method public hidebysig static void JmpToSuspendingTest() cil managed { - .custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [Microsoft.DotNet.XUnitExtensions]Xunit.ConditionalFactAttribute::.ctor(class [System.Runtime]System.Type, string[]) = { + type([TestLibrary]TestLibrary.PlatformDetection) + string[1] ('IsMultithreadingSupported') + } .maxstack 2 call class [System.Runtime]System.Threading.Tasks.Task`1 AsyncVersions::AwaitJmpToSuspending() @@ -185,7 +190,10 @@ .method public hidebysig static void TailToSuspendingTest() cil managed { - .custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = ( 01 00 00 00 ) + .custom instance void [Microsoft.DotNet.XUnitExtensions]Xunit.ConditionalFactAttribute::.ctor(class [System.Runtime]System.Type, string[]) = { + type([TestLibrary]TestLibrary.PlatformDetection) + string[1] ('IsMultithreadingSupported') + } .maxstack 2 call class [System.Runtime]System.Threading.Tasks.Task`1 AsyncVersions::AwaitTailToSuspending() diff --git a/src/tests/async/async-wasm.csproj b/src/tests/async/async-wasm.csproj deleted file mode 100644 index 5c9949ee5e01c5..00000000000000 --- a/src/tests/async/async-wasm.csproj +++ /dev/null @@ -1,11 +0,0 @@ - - - true - true - - - - - - - diff --git a/src/tests/async/async.csproj b/src/tests/async/async.csproj index 838e11a3ffe49b..d55e9b65fa87b9 100644 --- a/src/tests/async/async.csproj +++ b/src/tests/async/async.csproj @@ -1,4 +1,7 @@ + + true + diff --git a/src/tests/async/asyncversion-inline/asyncversion-inline.cs b/src/tests/async/asyncversion-inline/asyncversion-inline.cs index 0197eb8e3e507a..10d31539829800 100644 --- a/src/tests/async/asyncversion-inline/asyncversion-inline.cs +++ b/src/tests/async/asyncversion-inline/asyncversion-inline.cs @@ -14,6 +14,7 @@ // // These tests pin down that the caller still observes its own context after a suspension // that happened inside such an inlined frame. +[ConditionalClass(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public class Async2AsyncVersionInline { private sealed class NamedContext : SynchronizationContext diff --git a/src/tests/async/awaitingnotasync/awaitingnotasync.cs b/src/tests/async/awaitingnotasync/awaitingnotasync.cs index bf8ad8c9cc12a0..6c0972e4a6f5e7 100644 --- a/src/tests/async/awaitingnotasync/awaitingnotasync.cs +++ b/src/tests/async/awaitingnotasync/awaitingnotasync.cs @@ -7,7 +7,7 @@ public class AwaitNotAsync { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { AsyncEntryPoint().Wait(); diff --git a/src/tests/async/byref-param/byref-param.cs b/src/tests/async/byref-param/byref-param.cs index 0a005d1b4f6913..2b87f8f5bdfd20 100644 --- a/src/tests/async/byref-param/byref-param.cs +++ b/src/tests/async/byref-param/byref-param.cs @@ -24,7 +24,7 @@ private static Task HasByrefParam(out int foo) return Task.FromResult(53); } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static int TestByrefParamWithSuspension() { return TestWithSuspension().GetAwaiter().GetResult(); diff --git a/src/tests/async/collectible-alc/collectible-alc.cs b/src/tests/async/collectible-alc/collectible-alc.cs index e6ac383f23292e..fb0cc46e500c3e 100644 --- a/src/tests/async/collectible-alc/collectible-alc.cs +++ b/src/tests/async/collectible-alc/collectible-alc.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Xunit; +[ActiveIssue("https://github.com/dotnet/runtime/issues/134001", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsWasm))] public class Async2CollectibleAlc { [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsCollectibleAssembliesSupported))] diff --git a/src/tests/async/covariant-return/covariant-returns.cs b/src/tests/async/covariant-return/covariant-returns.cs index f94d1e1678a1a4..f01801c2c6c45c 100644 --- a/src/tests/async/covariant-return/covariant-returns.cs +++ b/src/tests/async/covariant-return/covariant-returns.cs @@ -20,13 +20,13 @@ public static void Test1EntryPoint() Test1().Wait(); } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void Test2EntryPoint() { Test2().Wait(); } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void Test2AEntryPoint() { Test2A().Wait(); @@ -183,7 +183,7 @@ public class Program { internal static int Result; - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestCovariantReturnWithoutRuntimeAsync() { Result = 0; @@ -223,7 +223,7 @@ public class Program [MethodImpl(MethodImplOptions.NoInlining)] public static async Task CallInstanceValueType(Base b) => await b.InstanceMethod(); - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestGenericVirtualMethod() { CallInstance(new Derived()).GetAwaiter().GetResult(); @@ -286,7 +286,7 @@ static async Task RunValueType() Assert.Equal(typeof(int).FullName.Length, x); } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestAsyncInterfaceGenericMethod() { Run().GetAwaiter().GetResult(); diff --git a/src/tests/async/cse-array-index-byref/cse-array-index-byref.cs b/src/tests/async/cse-array-index-byref/cse-array-index-byref.cs index d208c4e7a12c7c..f01679f0821855 100644 --- a/src/tests/async/cse-array-index-byref/cse-array-index-byref.cs +++ b/src/tests/async/cse-array-index-byref/cse-array-index-byref.cs @@ -8,7 +8,7 @@ public class CseArrayIndexByref { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { int[] arr = new int[1]; diff --git a/src/tests/async/custom-struct-awaiters/custom-struct-awaiters.cs b/src/tests/async/custom-struct-awaiters/custom-struct-awaiters.cs index 6b99f21aed5900..bcbd1ebfbc26dc 100644 --- a/src/tests/async/custom-struct-awaiters/custom-struct-awaiters.cs +++ b/src/tests/async/custom-struct-awaiters/custom-struct-awaiters.cs @@ -12,7 +12,7 @@ public class CustomStructAwaiters private static int s_safeAwaiterValue; private static int s_unsafeAwaiterValue; - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { Run().Wait(); diff --git a/src/tests/async/devirtualize/devirtualize.cs b/src/tests/async/devirtualize/devirtualize.cs index 5e7af86a5ee3ee..c10da7f46dcb91 100644 --- a/src/tests/async/devirtualize/devirtualize.cs +++ b/src/tests/async/devirtualize/devirtualize.cs @@ -129,7 +129,7 @@ static async Task CallOnInterface(T obj) where T : IAsyncInterface return await obj.GetValue(); } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { Assert.Equal(42, CallOnNewOpenDerived().Result); diff --git a/src/tests/async/execution-context/execution-context.cs b/src/tests/async/execution-context/execution-context.cs index 2a63225ded9a9e..8861d8e076a774 100644 --- a/src/tests/async/execution-context/execution-context.cs +++ b/src/tests/async/execution-context/execution-context.cs @@ -9,13 +9,13 @@ public class Async2ExecutionContext { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestDefaultFlow() { Test().GetAwaiter().GetResult(); } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestSuppressedFlow() { TestNoFlowOuter().GetAwaiter().GetResult(); @@ -120,7 +120,7 @@ private static async Task ChangeThenReturnInlined() s_local.Value = 123; } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static int TestRestoreTier0ContextInOsr() { return TestRestoreTier0ContextInOsrAsync().GetAwaiter().GetResult(); diff --git a/src/tests/async/fibonacci-with-yields-struct-return/fibonacci-with-yields-struct-return.cs b/src/tests/async/fibonacci-with-yields-struct-return/fibonacci-with-yields-struct-return.cs index a4e7f4a0c072a9..6b38929c6c6b12 100644 --- a/src/tests/async/fibonacci-with-yields-struct-return/fibonacci-with-yields-struct-return.cs +++ b/src/tests/async/fibonacci-with-yields-struct-return/fibonacci-with-yields-struct-return.cs @@ -13,7 +13,7 @@ public class Async2FibonacceWithYields { const int iterations = 3; - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void Test() { long allocated = GC.GetTotalAllocatedBytes(precise: true); diff --git a/src/tests/async/fibonacci-with-yields/fibonacci-with-yields.cs b/src/tests/async/fibonacci-with-yields/fibonacci-with-yields.cs index 34375bcbbf4fab..0db6b65117c020 100644 --- a/src/tests/async/fibonacci-with-yields/fibonacci-with-yields.cs +++ b/src/tests/async/fibonacci-with-yields/fibonacci-with-yields.cs @@ -12,7 +12,7 @@ public class Async2FibonacciWithYields const int iterations = 3; const bool doYields = true; - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void Test() { long allocated = GC.GetTotalAllocatedBytes(precise: true); diff --git a/src/tests/async/implement/implement.cs b/src/tests/async/implement/implement.cs index ddd20d0570c391..767908b1be0474 100644 --- a/src/tests/async/implement/implement.cs +++ b/src/tests/async/implement/implement.cs @@ -56,7 +56,7 @@ public async Task M1() } } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { IBase1 b1 = new Derived1(); diff --git a/src/tests/async/inlined-asyncversion-frames/inlined-asyncversion-frames.cs b/src/tests/async/inlined-asyncversion-frames/inlined-asyncversion-frames.cs index 7650ae6b0808eb..ab140c724c8c47 100644 --- a/src/tests/async/inlined-asyncversion-frames/inlined-asyncversion-frames.cs +++ b/src/tests/async/inlined-asyncversion-frames/inlined-asyncversion-frames.cs @@ -14,6 +14,7 @@ // await has to take the enclosing frame's contexts, so that a suspension records that the // frame resumed. Getting that wrong leaves the enclosing frame believing it never resumed // and running its context handling again on stale state. +[ConditionalClass(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public class Async2InlinedAsyncVersionFrames { // Completes synchronously for even values and asynchronously for odd ones, so a loop diff --git a/src/tests/async/inlined-frame-configureawait/inlined-frame-configureawait.cs b/src/tests/async/inlined-frame-configureawait/inlined-frame-configureawait.cs index 360bb471892174..fced0dcce96388 100644 --- a/src/tests/async/inlined-frame-configureawait/inlined-frame-configureawait.cs +++ b/src/tests/async/inlined-frame-configureawait/inlined-frame-configureawait.cs @@ -10,6 +10,7 @@ // A frame that awaited with ConfigureAwait(false) does not want to be brought back onto // the SynchronizationContext that was current when it suspended. That has to hold for the // logical return of an inlined async frame too. +[ConditionalClass(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public class Async2InlinedFrameConfigureAwait { private sealed class TrackingContext : SynchronizationContext diff --git a/src/tests/async/inlined-frame-contexts/inlined-frame-contexts.cs b/src/tests/async/inlined-frame-contexts/inlined-frame-contexts.cs index 133d3e5031354e..25717f257510cd 100644 --- a/src/tests/async/inlined-frame-contexts/inlined-frame-contexts.cs +++ b/src/tests/async/inlined-frame-contexts/inlined-frame-contexts.cs @@ -17,6 +17,7 @@ // // The inner frames are marked AggressiveInlining so that the inlining actually happens // rather than being left to the profitability heuristic, which rejects these callees. +[ConditionalClass(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public class Async2InlinedFrameContexts { private sealed class NamedContext : SynchronizationContext diff --git a/src/tests/async/inlined-frame-exceptions/inlined-frame-exceptions.cs b/src/tests/async/inlined-frame-exceptions/inlined-frame-exceptions.cs index 4d4d07c565387c..218c2d5fb6b202 100644 --- a/src/tests/async/inlined-frame-exceptions/inlined-frame-exceptions.cs +++ b/src/tests/async/inlined-frame-exceptions/inlined-frame-exceptions.cs @@ -10,6 +10,7 @@ // the frame suspended first. A suspension makes the resumption skip the entry code that // captured the frame's contexts, so the context restores that run while the exception // unwinds must see that the frame resumed. +[ConditionalClass(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public class Async2InlinedFrameExceptions { private static async Task SuspendOnlyAsync() => await Task.Yield(); diff --git a/src/tests/async/inlined-frame-ldvirtftn-await/inlined-frame-ldvirtftn-await.cs b/src/tests/async/inlined-frame-ldvirtftn-await/inlined-frame-ldvirtftn-await.cs index 4bede1ad37e2b4..b83aeebe3c8d77 100644 --- a/src/tests/async/inlined-frame-ldvirtftn-await/inlined-frame-ldvirtftn-await.cs +++ b/src/tests/async/inlined-frame-ldvirtftn-await/inlined-frame-ldvirtftn-await.cs @@ -12,6 +12,7 @@ // caller's instead: doing so leaves the inlined frame's own resumed indicator unset, so // after a suspension its context restore runs as if the frame had never resumed, over // state that the resumption skipped capturing. +[ConditionalClass(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public class Async2InlinedFrameLdvirtftnAwait { private class Dispatcher diff --git a/src/tests/async/inlined-frame-transition-suspends/inlined-frame-transition-suspends.cs b/src/tests/async/inlined-frame-transition-suspends/inlined-frame-transition-suspends.cs index 9c06caa8bad47d..d6d49be4cb6a3e 100644 --- a/src/tests/async/inlined-frame-transition-suspends/inlined-frame-transition-suspends.cs +++ b/src/tests/async/inlined-frame-transition-suspends/inlined-frame-transition-suspends.cs @@ -12,6 +12,7 @@ // to get back onto the caller's continuation context. If that means switching contexts the // transition itself suspends, so everything the caller still needs afterwards has to // survive that suspension -- including the record of which frames have resumed. +[ConditionalClass(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public class Async2InlinedFrameTransitionSuspends { // Never runs the callback inline: every Post goes to a dedicated thread, so getting diff --git a/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs index e169ed14b550c3..6d6ab78cea1756 100644 --- a/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs +++ b/src/tests/async/inst-unbox-thunks/inst-unbox-thunks.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Xunit; +[ConditionalClass(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public class InstUnBoxThunks { class Utility diff --git a/src/tests/async/object/object.cs b/src/tests/async/object/object.cs index 7ebec6207edf91..a0c6239faaa577 100644 --- a/src/tests/async/object/object.cs +++ b/src/tests/async/object/object.cs @@ -8,7 +8,7 @@ public class Async2Object { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static int TestEntryPoint() { return (int)AsyncTestEntryPoint(100).Result; diff --git a/src/tests/async/objects-captured/objects-captured.cs b/src/tests/async/objects-captured/objects-captured.cs index 07c502b12ee61c..6b2155d6c6195f 100644 --- a/src/tests/async/objects-captured/objects-captured.cs +++ b/src/tests/async/objects-captured/objects-captured.cs @@ -38,7 +38,7 @@ private static async Task AsyncEntry() return (int)result - 90; } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static int Test() { return (int)AsyncEntry().Result; diff --git a/src/tests/async/osr-inlined-contexts/osr-inlined-contexts.cs b/src/tests/async/osr-inlined-contexts/osr-inlined-contexts.cs index a89a8ed548dc29..aaefe793682894 100644 --- a/src/tests/async/osr-inlined-contexts/osr-inlined-contexts.cs +++ b/src/tests/async/osr-inlined-contexts/osr-inlined-contexts.cs @@ -113,7 +113,7 @@ private static async Task LoopWithSuspendingInlinedCallee() return total; } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void ResumingInsideInlinedFrameOfOsrMethodKeepsContexts() { SynchronizationContext original = SynchronizationContext.Current; @@ -127,7 +127,7 @@ public static void ResumingInsideInlinedFrameOfOsrMethodKeepsContexts() } } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TailAwaitOnlyMethodCanResumeInOsrCode() { Run().GetAwaiter().GetResult(); diff --git a/src/tests/async/override/override.cs b/src/tests/async/override/override.cs index 2d886f01d60d64..1270db7afbb04f 100644 --- a/src/tests/async/override/override.cs +++ b/src/tests/async/override/override.cs @@ -114,7 +114,7 @@ public override async Task M2(T first, T second) [MethodImpl(MethodImplOptions.NoInlining)] static async Task AwaitBaseM2(Base1 b, T first, T second) => await b.M2(first, second); - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { Base b = new Derived1(); diff --git a/src/tests/async/parallel-continuations/parallel-continuations.cs b/src/tests/async/parallel-continuations/parallel-continuations.cs index dc4ba3969c39a0..206a39e3a1d5ad 100644 --- a/src/tests/async/parallel-continuations/parallel-continuations.cs +++ b/src/tests/async/parallel-continuations/parallel-continuations.cs @@ -8,7 +8,7 @@ public class Async2ParallelContinuations { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestParallelContinuations() { TaskCompletionSource tcs = new TaskCompletionSource(); diff --git a/src/tests/async/pgo/pgo.cs b/src/tests/async/pgo/pgo.cs index 637b5bbde4507f..982b9d7c3075fb 100644 --- a/src/tests/async/pgo/pgo.cs +++ b/src/tests/async/pgo/pgo.cs @@ -12,7 +12,7 @@ public class Async2Pgo { [SkipOnCoreClr("This test is not compatible with GC stress.", RuntimeTestModes.AnyGCStress)] - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void EntryPoint() { AsyncEntryPoint().Wait(); diff --git a/src/tests/async/pinvoke/pinvoke.cs b/src/tests/async/pinvoke/pinvoke.cs index bbe9e939ad7f8f..d70df41b03b429 100644 --- a/src/tests/async/pinvoke/pinvoke.cs +++ b/src/tests/async/pinvoke/pinvoke.cs @@ -8,6 +8,7 @@ public class Async2PInvoke { + [ActiveIssue("https://github.com/dotnet/runtime/issues/124219", typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsWasm))] [Fact] public static void TestEntryPoint() { diff --git a/src/tests/async/reflection/reflection.cs b/src/tests/async/reflection/reflection.cs index db08b050071965..25c4a816d77658 100644 --- a/src/tests/async/reflection/reflection.cs +++ b/src/tests/async/reflection/reflection.cs @@ -14,7 +14,7 @@ public class Async2Reflection { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void MethodInfo_Invoke_TaskReturning() { var mi = typeof(Async2Reflection).GetMethod("Foo", BindingFlags.Static | BindingFlags.NonPublic)!; @@ -234,7 +234,7 @@ public class Accessors2 public extern static Task accessor(PrivateAsync2 o, int i); } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void UnsafeAccessors() { PrivateAsync1.s = 0; @@ -249,7 +249,7 @@ public static void UnsafeAccessors() Assert.Equal(8, PrivateAsync2.s); } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void UnsafeAccessorsAsync() { UnsafeAccessorsAsyncInner().GetAwaiter().GetResult(); @@ -269,7 +269,7 @@ private static async Task UnsafeAccessorsAsyncInner() Assert.Equal(8, PrivateAsync2.s); } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void CurrentMethod() { // Note: async1 leaks implementation details here and returns "Void MoveNext()" @@ -439,14 +439,14 @@ public static void EnumerateAll() string[] expected = {"Boolean Equals(System.Object)", "Void Finalize()", - "System.Threading.Tasks.Task`1[System.Int32] get_P1()", "System.String[] GetAll()", "Int32 GetHashCode()", "System.Type GetType()", "System.Threading.Tasks.Task`1[System.Int32] M1()", "System.Threading.Tasks.Task`1[System.Int32] M2()", "System.Object MemberwiseClone()", - "System.String ToString()" }; + "System.String ToString()", + "System.Threading.Tasks.Task`1[System.Int32] get_P1()" }; Assert.Equal(expected.Length, actual.Length); for (int i = 0; i < actual.Length; i++) @@ -467,7 +467,7 @@ public static string[] GetAll() { Type t = typeof(EnumAll); List names = new(); - foreach (MethodInfo mi in t.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static).OrderBy(it => it.Name)) + foreach (MethodInfo mi in t.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static).OrderBy(it => it.Name, StringComparer.Ordinal)) { names.Add(mi.ToString()!); } diff --git a/src/tests/async/regression/125615.cs b/src/tests/async/regression/125615.cs index aed3567722ba2b..ec1fdcc9d22005 100644 --- a/src/tests/async/regression/125615.cs +++ b/src/tests/async/regression/125615.cs @@ -6,7 +6,7 @@ public class Runtime_125615 { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { long result = MutateImplicitByRef(default).GetAwaiter().GetResult(); diff --git a/src/tests/async/regression/125805.cs b/src/tests/async/regression/125805.cs index 3f2f0e043ed2c3..d646f4f7c697a7 100644 --- a/src/tests/async/regression/125805.cs +++ b/src/tests/async/regression/125805.cs @@ -8,7 +8,7 @@ public class Runtime_125805 { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { ExceptionReuse().GetAwaiter().GetResult(); diff --git a/src/tests/async/regression/132015.cs b/src/tests/async/regression/132015.cs index 9c344da229a963..2edf2663a92376 100644 --- a/src/tests/async/regression/132015.cs +++ b/src/tests/async/regression/132015.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Xunit; +[ConditionalClass(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public class Runtime_132015 { [Fact] diff --git a/src/tests/async/regression/132016.cs b/src/tests/async/regression/132016.cs index 030f9be14d28af..b9b4c3c725a7f1 100644 --- a/src/tests/async/regression/132016.cs +++ b/src/tests/async/regression/132016.cs @@ -11,7 +11,7 @@ public class Runtime_132016 { private static readonly List s_log = new List(); - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { s_log.Clear(); diff --git a/src/tests/async/regression/132017.cs b/src/tests/async/regression/132017.cs index 091e9397b57753..a08f69a1dbf7f5 100644 --- a/src/tests/async/regression/132017.cs +++ b/src/tests/async/regression/132017.cs @@ -15,7 +15,7 @@ public class Runtime_132017 // that the test can wait for it deterministically. private static Task s_pending; - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { s_log.Clear(); diff --git a/src/tests/async/regression/managed-thread-id.cs b/src/tests/async/regression/managed-thread-id.cs index 04102b536e596d..cc31829dff7b80 100644 --- a/src/tests/async/regression/managed-thread-id.cs +++ b/src/tests/async/regression/managed-thread-id.cs @@ -11,6 +11,7 @@ using System.Threading.Tasks; using Xunit; +[ConditionalClass(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public class Async2ManagedThreadId { // Verify that Environment.CurrentManagedThreadId == Thread.CurrentThread.ManagedThreadId diff --git a/src/tests/async/resumed-known-true/resumed-known-true.cs b/src/tests/async/resumed-known-true/resumed-known-true.cs index f00aab77f620b8..a8d7d51f108da0 100644 --- a/src/tests/async/resumed-known-true/resumed-known-true.cs +++ b/src/tests/async/resumed-known-true/resumed-known-true.cs @@ -10,6 +10,7 @@ // no merge in between. The helpers emitted on the later await's suspension path // (RestoreContextsOnSuspension, the inlined frame transition captures) no-op // when the indicator is set, so they could be folded away entirely. +[ConditionalClass(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public class Async2ResumedKnownTrue { [Fact] diff --git a/src/tests/async/returns/returns.cs b/src/tests/async/returns/returns.cs index 3cc1262694b39b..0cc0981380f848 100644 --- a/src/tests/async/returns/returns.cs +++ b/src/tests/async/returns/returns.cs @@ -8,7 +8,7 @@ public class Async2Returns { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { Returns(new C()).Wait(); diff --git a/src/tests/async/runtime-wrapped-exception/runtime-wrapped-exception.cs b/src/tests/async/runtime-wrapped-exception/runtime-wrapped-exception.cs index 1de34df3001157..e557c55d7c23d7 100644 --- a/src/tests/async/runtime-wrapped-exception/runtime-wrapped-exception.cs +++ b/src/tests/async/runtime-wrapped-exception/runtime-wrapped-exception.cs @@ -27,11 +27,11 @@ public class RuntimeAsyncNonExceptionThrows // WrapNonExceptionThrows = true (this assembly): the non-Exception is observed // as a RuntimeWrappedException by the awaiting caller for both async forms. - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void CatchAfterYield_Async2() => AssertNonExceptionWrapped(ThrowAfterYieldAsync2); - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void CatchAfterYield_Async1() => AssertNonExceptionWrapped(ThrowAfterYieldAsync1); @@ -45,7 +45,7 @@ public static void CatchBeforeYield_Async1() // WrapNonExceptionThrows = false (NoWrapThrowers assembly). - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void CatchAfterYield_NoWrap_Async2() => AssertNonExceptionWrapped(NoWrapThrowers.ThrowAfterYieldAsync2); diff --git a/src/tests/async/shared-generic/shared-generic.cs b/src/tests/async/shared-generic/shared-generic.cs index 007cbf1f0cc382..7d7823b47a9c3e 100644 --- a/src/tests/async/shared-generic/shared-generic.cs +++ b/src/tests/async/shared-generic/shared-generic.cs @@ -17,7 +17,7 @@ public struct S1 public T t; } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { // simple cases diff --git a/src/tests/async/simple-eh/simple-eh.cs b/src/tests/async/simple-eh/simple-eh.cs index f5f467f6dc1e3e..04123713d1ea52 100644 --- a/src/tests/async/simple-eh/simple-eh.cs +++ b/src/tests/async/simple-eh/simple-eh.cs @@ -11,6 +11,7 @@ using TestLibrary; using Xunit; +[ConditionalClass(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public class Async2SimpleEH { [Fact] diff --git a/src/tests/async/small/small.cs b/src/tests/async/small/small.cs index bdce432c39b869..2381c136e878ab 100644 --- a/src/tests/async/small/small.cs +++ b/src/tests/async/small/small.cs @@ -8,7 +8,7 @@ public class Async2Small { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { SmallType(123).Wait(); diff --git a/src/tests/async/staticvirtual/staticvirtual.cs b/src/tests/async/staticvirtual/staticvirtual.cs index d74644de6f3e59..71793500c96fd9 100644 --- a/src/tests/async/staticvirtual/staticvirtual.cs +++ b/src/tests/async/staticvirtual/staticvirtual.cs @@ -20,7 +20,7 @@ class ClassWithStaticVirtuals : IHaveStaticVirtuals static async Task CallDoTask() where T : IHaveStaticVirtuals => await T.DoTask(); - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { CallDoTask().Wait(); diff --git a/src/tests/async/strength-reduction/strength-reduction.cs b/src/tests/async/strength-reduction/strength-reduction.cs index 175a01f09a5af0..79a770b4e259d2 100644 --- a/src/tests/async/strength-reduction/strength-reduction.cs +++ b/src/tests/async/strength-reduction/strength-reduction.cs @@ -9,7 +9,7 @@ public class StrengthReductionTest { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static int TestEntryPoint() { return StrengthReduction(Enumerable.Range(0, 1000).ToArray()).Result - 499400; diff --git a/src/tests/async/struct/struct.cs b/src/tests/async/struct/struct.cs index df393687e5216e..4906f15a86dca1 100644 --- a/src/tests/async/struct/struct.cs +++ b/src/tests/async/struct/struct.cs @@ -10,7 +10,7 @@ public class Async2Struct { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { Async().Wait(); diff --git a/src/tests/async/synchronization-context/synchronization-context.cs b/src/tests/async/synchronization-context/synchronization-context.cs index 9ddda0d0cf9fb4..1a4475809e48de 100644 --- a/src/tests/async/synchronization-context/synchronization-context.cs +++ b/src/tests/async/synchronization-context/synchronization-context.cs @@ -10,7 +10,7 @@ public class Async2SynchronizationContext { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestSyncContextContinue() { SynchronizationContext prevContext = SynchronizationContext.Current; @@ -138,7 +138,7 @@ private static async Task ClearSyncContext() SynchronizationContext.SetSynchronizationContext(null); } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestSyncContextNotRestored() { SynchronizationContext prevContext = SynchronizationContext.Current; @@ -185,7 +185,7 @@ public override void Post(SendOrPostCallback d, object state) } } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestContinueOnCorrectSyncContext() { SynchronizationContext prevContext = SynchronizationContext.Current; @@ -220,7 +220,7 @@ private static async Task SetContext(SynchronizationContext context, bool suspen await Task.Yield(); } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestNoSyncContextInRuntimeCallableThunk() { SynchronizationContext prevContext = SynchronizationContext.Current; diff --git a/src/tests/async/valuetask-source/valuetask-source-stub.cs b/src/tests/async/valuetask-source/valuetask-source-stub.cs index 4b95656558de98..9877dc84dc70c6 100644 --- a/src/tests/async/valuetask-source/valuetask-source-stub.cs +++ b/src/tests/async/valuetask-source/valuetask-source-stub.cs @@ -9,7 +9,7 @@ public class ValueTaskSourceAndStubs { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void EntryPoint() { SynchronizationContext? original = SynchronizationContext.Current; diff --git a/src/tests/async/valuetask-source/valuetask-source.cs b/src/tests/async/valuetask-source/valuetask-source.cs index 9311c3ef69ae52..28dbe4ed000a23 100644 --- a/src/tests/async/valuetask-source/valuetask-source.cs +++ b/src/tests/async/valuetask-source/valuetask-source.cs @@ -10,7 +10,7 @@ public class Async2ValueTaskSource { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void AwaitValueTaskDefaultContext() { SynchronizationContext currentCtx = SynchronizationContext.Current; @@ -52,7 +52,7 @@ public static void AwaitValueTaskDefaultContext() } } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void AwaitValueTaskCustomContext() { SynchronizationContext currentCtx = SynchronizationContext.Current; @@ -83,7 +83,7 @@ public static void AwaitValueTaskCustomContext() } } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void AwaitValueTaskCustomContextExtraCall() { SynchronizationContext currentCtx = SynchronizationContext.Current; diff --git a/src/tests/async/valuetask/valuetask.cs b/src/tests/async/valuetask/valuetask.cs index 1bdd69f9e6120a..f8a2db9e82e894 100644 --- a/src/tests/async/valuetask/valuetask.cs +++ b/src/tests/async/valuetask/valuetask.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Xunit; +[ConditionalClass(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public class Async2ValueTask { [Fact] diff --git a/src/tests/async/varying-yields/varying-yields.cs b/src/tests/async/varying-yields/varying-yields.cs index 6194d1890d838a..950798ce0e22cd 100644 --- a/src/tests/async/varying-yields/varying-yields.cs +++ b/src/tests/async/varying-yields/varying-yields.cs @@ -16,7 +16,7 @@ public class Async2VaryingYields { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { Task.Run(AsyncEntry).Wait(); diff --git a/src/tests/async/void/void.cs b/src/tests/async/void/void.cs index 566dbd8e7ccc77..b7982b92a5e15f 100644 --- a/src/tests/async/void/void.cs +++ b/src/tests/async/void/void.cs @@ -8,7 +8,7 @@ public class Async2Void { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { int[] arr = new int[1]; diff --git a/src/tests/async/widening-tailcall/widening-tailcall.cs b/src/tests/async/widening-tailcall/widening-tailcall.cs index 1d3cfafc500587..346813fef131af 100644 --- a/src/tests/async/widening-tailcall/widening-tailcall.cs +++ b/src/tests/async/widening-tailcall/widening-tailcall.cs @@ -8,7 +8,7 @@ public class Async2WideningTailcall { - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static void TestEntryPoint() { uint vr0 = (uint)M29().GetAwaiter().GetResult(); diff --git a/src/tests/async/with-yields/with-yields.cs b/src/tests/async/with-yields/with-yields.cs index 7c382b671092d7..ce0eb7b393cc81 100644 --- a/src/tests/async/with-yields/with-yields.cs +++ b/src/tests/async/with-yields/with-yields.cs @@ -45,7 +45,7 @@ private static async Task AsyncEntry() return result; } - [Fact] + [ConditionalFact(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] public static int Test() { return AsyncEntry().Result; From 85afcea37b8d7d4ff2b75303b2a6d9eecb92d04a Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 15 Sep 2026 18:47:39 -0500 Subject: [PATCH 5/6] Refine Wasm runtime async coverage Enable the full runner, gate multithreading-dependent tests by capability, and quarantine only exact unsupported Wasm scenarios. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8a57fff8-6c30-4d3c-ac72-a80975122a96 --- src/tests/async/reflection/reflection.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tests/async/reflection/reflection.cs b/src/tests/async/reflection/reflection.cs index 25c4a816d77658..72e58b05a5a7ae 100644 --- a/src/tests/async/reflection/reflection.cs +++ b/src/tests/async/reflection/reflection.cs @@ -307,7 +307,7 @@ private static async Task GetCurrentMethodAwaitTask() return await GetCurrentMethodTask(); } - [Theory] + [ConditionalTheory(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] [InlineData(0)] [InlineData(1)] [ActiveIssue("https://github.com/dotnet/runtime/issues/122547", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsCoreClrInterpreter))] @@ -374,7 +374,7 @@ private static async Task FromStackAwaitTask(int level) [ActiveIssue("https://github.com/dotnet/runtime/issues/122547", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsNativeAot))] [ActiveIssue("https://github.com/dotnet/runtime/issues/122547", typeof(TestLibrary.Utilities), nameof(TestLibrary.Utilities.IsCoreClrInterpreter))] - [Theory] + [ConditionalTheory(typeof(TestLibrary.PlatformDetection), nameof(TestLibrary.PlatformDetection.IsMultithreadingSupported))] [InlineData(0)] [InlineData(1)] [MethodImpl(MethodImplOptions.NoInlining)] From 3604189f131d4e61ecba8fe67c9c5edbd3856637 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 15 Sep 2026 18:48:33 -0500 Subject: [PATCH 6/6] Avoid duplicate async test library reference Keep the shared TestLibrary reference on standalone async children while relying on the parent test targets for the merged runner. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8a57fff8-6c30-4d3c-ac72-a80975122a96 --- src/tests/async/Directory.Build.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/async/Directory.Build.targets b/src/tests/async/Directory.Build.targets index 3d52b6c9800968..198cd0f7813fcc 100644 --- a/src/tests/async/Directory.Build.targets +++ b/src/tests/async/Directory.Build.targets @@ -3,7 +3,7 @@ true - +