From 6f42291a8f0d0df5bdba8362dc980d40b011dd3b Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 21 Aug 2026 11:11:24 +0100 Subject: [PATCH 01/12] chore(Test): Inhibit RunSynchronouslyImmedate; reduce suite concurrency --- .../AsyncModuleFunctions.fs | 9 +- .../Microsoft.FSharp.Control/AsyncType.fs | 119 ++++++++++-------- 2 files changed, 76 insertions(+), 52 deletions(-) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModuleFunctions.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModuleFunctions.fs index 3335e6910ac..beded1df0c6 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModuleFunctions.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModuleFunctions.fs @@ -1,7 +1,10 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. // Tests for camelCase functions in module Async -module FSharp.Core.UnitTests.Controa.AsyncModuleFunctionsTestsl + +// Intentionally in same collection to help rule out potential flakiness due to concurrency re #20306 +[] +module FSharp.Core.UnitTests.Control.AsyncModuleFunctionsTests open System open System.Threading @@ -19,7 +22,9 @@ let cancelWithToken (tcs: TaskCompletionSource<'T>) = ct #endif -let asyncWait (a: Async<'T>): 'T = Async.RunSynchronouslyImmediate a +// TEMP disabled Immediate re #20306 // let asyncWait (a: Async<'T>): 'T = Async.RunSynchronouslyImmediate a +// TEMP disabled Immediate re #20306 // let asyncWaitWithCt (ct: CancellationToken) (a: Async<'T>): 'T = Async.RunSynchronouslyImmediate(a, cancellationToken = ct) +let asyncWait (a: Async<'T>): 'T = Async.RunSynchronously a let asyncWaitWithCt (ct: CancellationToken) (a: Async<'T>): 'T = Async.RunSynchronously(a, cancellationToken = ct) [] diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs index e77154860d1..778a25c868c 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs @@ -40,6 +40,23 @@ module AsyncType = async { return () } |> expect Success +module Helpers = + + (* TEMP disabled Immediate re 20306 + let asyncWait immediate (a: Async<'T>): 'T = + if immediate then Async.RunSynchronouslyImmediate a + else Async.RunSynchronously a + let asyncWaitWithCt immediate (ct: CancellationToken) (a: Async<'T>): 'T = + if immediate then Async.RunSynchronouslyImmediate(a, cancellationToken = ct) + else Async.RunSynchronously(a, cancellationToken = ct) + *) + let asyncWait (_immediate: bool) (a: Async<'T>): 'T = Async.RunSynchronously a + let asyncWaitWithCt (_immediate: bool) (ct: CancellationToken) (a: Async<'T>): 'T = Async.RunSynchronously(a, cancellationToken = ct) + let asyncWaitImm a = asyncWait true a + let asyncWaitWithCtImm ct a = asyncWaitWithCt true ct a + +open Helpers + // Multiple tests affect global state via Async.CancelDefaultToken [] type AsyncType() = @@ -57,15 +74,14 @@ type AsyncType() = [] member _.AsyncRunSynchronouslyReusesThreadPoolThread(immediate) = - let run a = if immediate then Async.RunSynchronouslyImmediate(a) else Async.RunSynchronously(a) + let run a = asyncWait immediate a let action _ = async { return async { return Thread.CurrentThread.ManagedThreadId } |> run } - // This test needs approximately 1000 ThreadPool threads - // if Async.RunSynchronously doesn't reuse them. + // This test needs approximately 1000 ThreadPool threads (if Async.RunSynchronously[Immediate] doesn't reuse them) let usedThreads = Seq.init 1000 action |> Async.Parallel @@ -93,7 +109,7 @@ type AsyncType() = (fun _ -> result <- "Cancel"), cts.Token) cts.Cancel() - Async.Sleep(1000) |> Async.RunSynchronouslyImmediate + Async.Sleep 1000 |> asyncWaitImm Assert.AreEqual("Cancel", result) ) @@ -135,7 +151,7 @@ type AsyncType() = failwith "Expected ArgumentOutOfRangeException" with | :? ArgumentOutOfRangeException -> () - } |> Async.RunSynchronouslyImmediate + } |> asyncWaitImm [] member _.AsyncSleepInfinitely() = @@ -148,7 +164,7 @@ type AsyncType() = (fun _ -> result.TrySetResult("Exception") |> ignore), (fun _ -> result.TrySetResult("Cancel") |> ignore), cts.Token) - let result = result.Task |> Async.Await |> Async.RunSynchronouslyImmediate + let result = result.Task |> Async.Await |> asyncWaitImm Assert.AreEqual("Cancel", result) ) @@ -194,7 +210,7 @@ type AsyncType() = | :? TaskCanceledException -> () | _ -> reraise() - Assert.True (t.IsCompleted, "Task is not completed") + Assert.True(t.IsCompleted, "Task is not completed") [] @@ -220,7 +236,7 @@ type AsyncType() = match a.InnerException with | :? TaskCanceledException -> () | _ -> reraise() - Assert.True (tcs.Task.IsCompleted, "Task is not completed") + Assert.True(tcs.Task.IsCompleted, "Task is not completed") [] member _.RunSynchronouslyCancellationWithDelayedResult(newAwait: bool) = @@ -234,7 +250,7 @@ type AsyncType() = let cancelled = try - Async.RunSynchronouslyImmediate(a, cancellationToken = cts.Token) |> ignore + asyncWaitWithCtImm cts.Token a |> ignore false with :? OperationCanceledException as o -> true | _ -> false @@ -377,7 +393,7 @@ type AsyncType() = let! s1 = t |> if newAwait then Async.Await else Async.AwaitTask return s = s1 } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.AwaitTaskCancellation(newAwait: bool) = @@ -389,7 +405,7 @@ type AsyncType() = return false with :? OperationCanceledException -> return true } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.AwaitCompletedTask() = @@ -399,7 +415,7 @@ type AsyncType() = let threadIdAfter = Thread.CurrentThread.ManagedThreadId return threadIdBefore = threadIdAfter } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.AwaitTaskCancellationUntyped(newAwait: bool) = @@ -411,7 +427,7 @@ type AsyncType() = return false with :? OperationCanceledException -> return true } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.TaskAsyncValueException(newAwait: bool) = @@ -421,7 +437,7 @@ type AsyncType() = return false with e -> return true } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.TaskAsyncValueCancellation(newAwait: bool) = @@ -454,8 +470,7 @@ type AsyncType() = do! t |> if newAwait then Async.Await else Async.AwaitTask return true } - let ok = Async.RunSynchronouslyImmediate a - Assert.True(hasBeenCalled && ok) + Assert.True(asyncWaitImm a && hasBeenCalled) [] member _.NonGenericTaskAsyncValueException(newAwait: bool) = @@ -466,7 +481,7 @@ type AsyncType() = return false with e -> return true } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.NonGenericTaskAsyncValueCancellation(newAwait: bool) = @@ -515,7 +530,7 @@ type AsyncType() = return! loop(x+1) } - try Async.RunSynchronously(loop 0) + try asyncWait false (loop 0) hasThrown <- false with Failure "finish" -> hasThrown <- true @@ -557,7 +572,7 @@ type AsyncType() = return false with :? AggregateException as ae -> return ae.InnerExceptions.Count = 2 } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.``Await and AwaitTask(Task) valid AggregateException is surfaced``(newAwait) = @@ -569,7 +584,7 @@ type AsyncType() = return false with :? AggregateException as ae -> return ae.InnerExceptions.Count = 2 } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) (* Async.Await behavioral differences @@ -585,7 +600,7 @@ type AsyncType() = return false with :? AggregateException -> return true } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) // ... whereas Async.Await(Task) surfaces the inner exception directly. [] @@ -597,7 +612,7 @@ type AsyncType() = return false with :? ArgumentException as ae -> return ae.Message = "original" } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) // Async.AwaitTask(Task<'T>) surfaces the wrapping AggregateException ... [] @@ -609,7 +624,7 @@ type AsyncType() = return false with :? AggregateException -> return true } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) // ... whereas Async.Await(Task<'T>) surfaces the inner exception directly. [] @@ -621,7 +636,7 @@ type AsyncType() = return false with :? ArgumentException as ae -> return ae.Message = "original" } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) (* Await(Task/Task<'T>) overloads happy path *) @@ -631,7 +646,7 @@ type AsyncType() = let! v = Async.Await(Task.FromResult(42)) return v = 42 } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.``Await(Task) happy path``() = @@ -639,7 +654,7 @@ type AsyncType() = do! Async.Await(Task.CompletedTask) return true } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) (* StartTaskImmediate(Task/Task<'T>) *) @@ -649,7 +664,7 @@ type AsyncType() = let! v = Async.StartTaskImmediate(fun _ct -> Task.result 42) return v = 42 } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.``StartTaskImmediate(Task) happy path``() = @@ -657,7 +672,7 @@ type AsyncType() = let a = async { do! Async.StartTaskImmediate(fun _ct -> task { called <- true }) } - Async.RunSynchronouslyImmediate a + asyncWaitImm a Assert.True called [] @@ -667,7 +682,7 @@ type AsyncType() = let a = async { do! Async.StartTaskImmediate(fun ct -> task { capturedCt <- ct }) } - Async.RunSynchronouslyImmediate(a, cancellationToken = cts.Token) + asyncWaitWithCtImm cts.Token a Assert.Equal(cts.Token, capturedCt) [] @@ -679,7 +694,7 @@ type AsyncType() = return false with :? ArgumentException as ae -> return ae.Message = "original" } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.``StartTaskImmediate(Task) exception unwraps``() = @@ -690,7 +705,7 @@ type AsyncType() = return false with :? ArgumentException as ae -> return ae.Message = "original" } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.``StartTaskImmediate(Task<'T>) cancellation raises TaskCanceledException``() = @@ -701,7 +716,7 @@ type AsyncType() = return false with :? TaskCanceledException -> return true } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) #if !NETFRAMEWORK (* Await(ValueTask and ValueTask<'T>) overloads coverage of mainline behaviors *) @@ -712,7 +727,7 @@ type AsyncType() = do! Async.Await(ValueTask()) return true } - Assert.True (Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.``Await(ValueTask<'T>) happy path``() = @@ -720,7 +735,7 @@ type AsyncType() = let! v = Async.Await(ValueTask(42)) return v = 42 } - Assert.True (Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.``Await(ValueTask) exception unwraps``() = @@ -732,7 +747,7 @@ type AsyncType() = return false with :? ArgumentException as ae -> return ae.Message = "original" } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.``Await(ValueTask<'T>) exception unwraps``() = @@ -743,7 +758,7 @@ type AsyncType() = return false with :? ArgumentException as ae -> return ae.Message = "original" } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) (* StartTaskImmediate(ValueTask/ValueTask<'T>) *) @@ -753,7 +768,7 @@ type AsyncType() = let! v = Async.StartTaskImmediate(fun _ct -> ValueTask(42)) return v = 42 } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.``StartTaskImmediate(ValueTask) happy path``() = @@ -761,7 +776,7 @@ type AsyncType() = do! Async.StartTaskImmediate(fun _ct -> ValueTask()) return true } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.``StartTaskImmediate(ValueTask<'T>) exception unwraps``() = @@ -773,7 +788,7 @@ type AsyncType() = return false with :? ArgumentException as ae -> return ae.Message = "original" } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] member _.``StartTaskImmediate(ValueTask) exception unwraps``() = @@ -785,9 +800,11 @@ type AsyncType() = return false with :? ArgumentException as ae -> return ae.Message = "original" } - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) #endif +// Intentionally in same collection to help rule out potential flakiness due to concurrency re #20306 +[] module AsyncTaskLikeAwaitTests = // Minimal custom task-like type wrapping Task<'T> @@ -805,7 +822,7 @@ module AsyncTaskLikeAwaitTests = let! v = Async.Await(MyTask(Task.FromResult 42)) return v } - Assert.Equal(42, Async.RunSynchronouslyImmediate a) + Assert.Equal(42, asyncWaitImm a) [] @@ -813,7 +830,7 @@ module AsyncTaskLikeAwaitTests = async { do! Async.Await(MyUnitTask(Task.CompletedTask)) } - |> Async.RunSynchronouslyImmediate + |> asyncWaitImm [] let ``Await(task-like) deferred completion``() = @@ -865,7 +882,7 @@ module AsyncTaskLikeAwaitTests = return e.Message = "boom" } tcs.SetException(InvalidOperationException "boom") - Assert.True(Async.RunSynchronouslyImmediate a) + Assert.True(asyncWaitImm a) [] let ``Await(YieldAwaitable) yields and resumes``() = @@ -876,7 +893,7 @@ module AsyncTaskLikeAwaitTests = do! Async.Await(Task.Yield()) after <- true } - |> Async.RunSynchronouslyImmediate + |> asyncWaitImm Assert.True(before && after) [] @@ -887,9 +904,11 @@ module AsyncTaskLikeAwaitTests = let! v = Async.Await(Task.FromResult(42).ConfigureAwait(false)) return v } - |> Async.RunSynchronouslyImmediate + |> asyncWaitImm Assert.Equal(42, result) +// Intentionally in same collection to mitigate potential flakiness due to concurrency re #20306 +[] module AsyncStartTaskImmediateTaskLikeTests = [] @@ -902,8 +921,7 @@ module AsyncStartTaskImmediateTaskLikeTests = do! Async.StartTaskImmediate(fun _ -> Task.Yield()) after <- true } - |> Async.RunSynchronouslyImmediate - + |> asyncWaitImm Assert.True(before && after) [] @@ -913,8 +931,7 @@ module AsyncStartTaskImmediateTaskLikeTests = async { return! Async.StartTaskImmediate(fun _ -> Task.FromResult(42).ConfigureAwait(false)) } - |> Async.RunSynchronouslyImmediate - + |> asyncWaitImm Assert.Equal(42, result) [] @@ -928,10 +945,12 @@ module AsyncStartTaskImmediateTaskLikeTests = capturedCt <- ct Task.CompletedTask.ConfigureAwait(false)) } - Async.RunSynchronouslyImmediate(a, cancellationToken = cts.Token) + asyncWaitWithCtImm cts.Token a Assert.Equal(cts.Token, capturedCt) +// Intentionally in same collection to mitigate potential flakiness due to concurrency re #20306 +[] module AsyncAwaitStackTraceTests = open System.Runtime.CompilerServices From 153fdb4c4e4210b1075fbf09eabab0911477b36e Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 21 Aug 2026 12:49:51 +0100 Subject: [PATCH 02/12] chore(Tests): Extend timeout to 30s for CI/heavy load --- .../Microsoft.FSharp.Control/AsyncType.fs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs index 778a25c868c..3bfb4b780a3 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs @@ -42,6 +42,11 @@ module AsyncType = module Helpers = + // Use a generous timeout to avoid flaky failures on loaded CI machines where the thread pool may be saturated. + let verifyTaskCompletion (t: Task) = + let completed = t.Wait(TimeSpan.FromSeconds 30.0) + Assert.True(completed, "Task did not finish after waiting for 30 seconds.") + (* TEMP disabled Immediate re 20306 let asyncWait immediate (a: Async<'T>): 'T = if immediate then Async.RunSynchronouslyImmediate a @@ -67,11 +72,6 @@ type AsyncType() = [] let mutable spinloop = true - // Use a generous timeout to avoid flaky failures on loaded CI machines where the thread pool may be saturated. - let waitForCompletion (t: Task) = - let result = t.Wait(TimeSpan.FromSeconds(30.0)) - Assert.True(result, "Task did not finish after waiting for 30 seconds.") - [] member _.AsyncRunSynchronouslyReusesThreadPoolThread(immediate) = let run a = asyncWait immediate a @@ -158,7 +158,7 @@ type AsyncType() = ignoreSynchCtx (fun () -> let computation = Async.Sleep(Timeout.Infinite) let result = TaskCompletionSource() - use cts = new CancellationTokenSource(TimeSpan.FromSeconds(1.0)) // there's a long way from 1 sec to infinity, but it'll have to do. + use cts = new CancellationTokenSource(TimeSpan.FromSeconds 1.0) // there's a long way from 1 sec to infinity, but it'll have to do. Async.StartWithContinuations(computation, (fun _ -> result.TrySetResult("Ok") |> ignore), (fun _ -> result.TrySetResult("Exception") |> ignore), @@ -173,7 +173,7 @@ type AsyncType() = let s = "Hello tasks!" let a = async { return s } let t : Task = Async.StartAsTask a - waitForCompletion t + verifyTaskCompletion t Assert.True(t.IsCompleted) Assert.AreEqual(s, t.Result) @@ -231,7 +231,7 @@ type AsyncType() = innerTcs.SetResult () try - waitForCompletion tcs.Task + verifyTaskCompletion tcs.Task with :? AggregateException as a -> match a.InnerException with | :? TaskCanceledException -> () @@ -284,7 +284,7 @@ type AsyncType() = Async.CancelDefaultToken () let mutable exceptionThrown = false try - waitForCompletion t + verifyTaskCompletion t with e -> exceptionThrown <- true Assert.True(exceptionThrown) Assert.True(t.IsCanceled) @@ -318,7 +318,7 @@ type AsyncType() = let s = "Hello tasks!" let a = async { return s } let t : Task = Async.StartImmediateAsTask a - waitForCompletion t + verifyTaskCompletion t Assert.True(t.IsCompleted) Assert.AreEqual(s, t.Result) @@ -327,7 +327,7 @@ type AsyncType() = let s = "Hello tasks!" let a = async { return s } let t = Async.StartImmediateAsTask a - waitForCompletion t + verifyTaskCompletion t Assert.True(t.IsCompleted) Assert.AreEqual(s, t.Result) @@ -843,7 +843,7 @@ module AsyncTaskLikeAwaitTests = |> Async.StartAsTask Assert.False(t.IsCompleted, "Should not be done before TCS is set") tcs.SetResult 7 - t.Wait(TimeSpan.FromSeconds 5.0) |> ignore + verifyTaskCompletion t Assert.Equal(7, t.Result) [] @@ -866,8 +866,8 @@ module AsyncTaskLikeAwaitTests = asyncLocal.Value <- "completing-context" // if ExecutionContext is not propagated correctly to the continuation, it will see this tcs.SetResult()) - Assert.True(completion.Wait(TimeSpan.FromSeconds 5.), "Completion task hung?") - Assert.True(t.Wait(TimeSpan.FromSeconds 5.), "Awaited subject task hung?") + verifyTaskCompletion completion + verifyTaskCompletion t Assert.Equal("trace-id", t.Result) // Validate the chaining worked correctly Assert.Equal("root-context", asyncLocal.Value) // Root level context should be preserved @@ -907,7 +907,7 @@ module AsyncTaskLikeAwaitTests = |> asyncWaitImm Assert.Equal(42, result) -// Intentionally in same collection to mitigate potential flakiness due to concurrency re #20306 +// Intentionally in same collection to rule out potential flakiness due to concurrency re #20306 [] module AsyncStartTaskImmediateTaskLikeTests = @@ -949,7 +949,7 @@ module AsyncStartTaskImmediateTaskLikeTests = Assert.Equal(cts.Token, capturedCt) -// Intentionally in same collection to mitigate potential flakiness due to concurrency re #20306 +// Intentionally in same collection to rule out potential flakiness due to concurrency re #20306 [] module AsyncAwaitStackTraceTests = From 55e0fb47cb83b514ee9c3e0504190d54ddfd78ea Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 21 Aug 2026 13:52:02 +0100 Subject: [PATCH 03/12] chore(Build hang triage): Rule out more RSI suspects --- src/Compiler/Driver/fsc.fs | 4 ++-- src/Compiler/Facilities/DiagnosticsLogger.fs | 2 +- src/Compiler/Interactive/fsi.fs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Compiler/Driver/fsc.fs b/src/Compiler/Driver/fsc.fs index 2333b971192..c6d84aed809 100644 --- a/src/Compiler/Driver/fsc.fs +++ b/src/Compiler/Driver/fsc.fs @@ -599,7 +599,7 @@ let main1 // Import basic assemblies let tcGlobals, frameworkTcImports = TcImports.BuildFrameworkTcImports(foundationalTcConfigP, sysRes, otherRes) - |> Async.RunSynchronouslyImmediate + |> Async.RunSynchronously // TEMP: NOT Immediate re #20306 let ilSourceDocs = [ @@ -647,7 +647,7 @@ let main1 let tcImports = TcImports.BuildNonFrameworkTcImports(tcConfigP, frameworkTcImports, otherRes, knownUnresolved, dependencyProvider) - |> Async.RunSynchronouslyImmediate + |> Async.RunSynchronously // TEMP: NOT Immediate re #20306 // register tcImports to be disposed in future disposables.Register tcImports diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fs b/src/Compiler/Facilities/DiagnosticsLogger.fs index 77351b7dd89..202993c8867 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fs +++ b/src/Compiler/Facilities/DiagnosticsLogger.fs @@ -1019,7 +1019,7 @@ type StackGuard(name: string) = Thread.CurrentThread.Name <- $"F# Extra Compilation Thread for {name} (depth {depthWhenJump})" return f () } - |> Async.RunSynchronouslyImmediate + |> Async.RunSynchronously // TEMP: NOT Immediate re #20306 finally depth.Value <- depth.Value - 1 diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index 436d0a0c777..e18e2afcdee 100644 --- a/src/Compiler/Interactive/fsi.fs +++ b/src/Compiler/Interactive/fsi.fs @@ -4802,7 +4802,7 @@ type FsiEvaluationSession try let tcConfig = tcConfigP.Get(ctokStartup) - checker.FrameworkImportsCache.Get tcConfig |> Async.RunSynchronouslyImmediate + checker.FrameworkImportsCache.Get tcConfig |> Async.RunSynchronously // TEMP: NOT Immediate re #20306 with e -> stopProcessingRecovery e range0 failwithf "Error creating evaluation session: %A" e @@ -4816,7 +4816,7 @@ type FsiEvaluationSession unresolvedReferences, fsiOptions.DependencyProvider ) - |> Async.RunSynchronouslyImmediate + |> Async.RunSynchronously // TEMP: NOT Immediate re #20306 with e -> stopProcessingRecovery e range0 failwithf "Error creating evaluation session: %A" e From 9fc39e73b1cf64546f1296341529745a96cc33fe Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 21 Aug 2026 17:40:25 +0100 Subject: [PATCH 04/12] fix: formatting --- .../MultiProjectAnalysisTests.fs | 3 +-- .../FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs | 7 ------- .../FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs | 2 +- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/tests/FSharp.Compiler.Service.Tests/MultiProjectAnalysisTests.fs b/tests/FSharp.Compiler.Service.Tests/MultiProjectAnalysisTests.fs index 9588984e2c8..78feadbfe9c 100644 --- a/tests/FSharp.Compiler.Service.Tests/MultiProjectAnalysisTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/MultiProjectAnalysisTests.fs @@ -910,8 +910,7 @@ module GenerativeTypeProviderFallbackTest = begin let fileName = __SOURCE_DIRECTORY__ ++ @"../service/data/TestProject/TestProject.fs" let fileSource = FileSystem.OpenFileForReadShim(fileName).ReadAllText() - let fileParseResults, fileCheckAnswer = checker.ParseAndCheckFileInProject(fileName, 0, SourceText.ofString fileSource, optionsTestProject) |> Async. - RunSynchronouslyImmediate + let fileParseResults, fileCheckAnswer = checker.ParseAndCheckFileInProject(fileName, 0, SourceText.ofString fileSource, optionsTestProject) |> Async.RunSynchronouslyImmediate let fileCheckResults = match fileCheckAnswer with diff --git a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs index 8d77d122fb2..b9d224c3d61 100644 --- a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs @@ -574,7 +574,6 @@ let ``Test file explicit parse symbols`` () = let wholeProjectResults = checker.ParseAndCheckProject(Project1.options) |> Async.RunSynchronouslyImmediate let parseResults1 = checker.ParseFile(Project1.fileName1, Project1.fileSource1, Project1.parsingOptions) |> Async.RunSynchronouslyImmediate - let parseResults2 = checker.ParseFile(Project1.fileName2, Project1.fileSource2, Project1.parsingOptions) |> Async.RunSynchronouslyImmediate let checkResults1 = @@ -621,7 +620,6 @@ let ``Test file explicit parse all symbols`` () = let wholeProjectResults = checker.ParseAndCheckProject(Project1.options) |> Async.RunSynchronouslyImmediate let parseResults1 = checker.ParseFile(Project1.fileName1, Project1.fileSource1, Project1.parsingOptions) |> Async.RunSynchronouslyImmediate - let parseResults2 = checker.ParseFile(Project1.fileName2, Project1.fileSource2, Project1.parsingOptions) |> Async.RunSynchronouslyImmediate let checkResults1 = @@ -4884,7 +4882,6 @@ let ``Test project37 typeof and arrays in attribute constructor arguments`` () = let wholeProjectResults = checker.ParseAndCheckProject(Project37.options) |> Async.RunSynchronouslyImmediate - let allSymbolsUses = wholeProjectResults.GetAllUsesOfAllSymbols() for su in allSymbolsUses do match su.Symbol with @@ -4939,7 +4936,6 @@ let ``Test project37 DeclaringEntity`` () = let wholeProjectResults = checker.ParseAndCheckProject(Project37.options) |> Async.RunSynchronouslyImmediate - let allSymbolsUses = wholeProjectResults.GetAllUsesOfAllSymbols() for sym in allSymbolsUses do match sym.Symbol with @@ -5028,7 +5024,6 @@ let ``Test project38 abstract slot information`` () = let wholeProjectResults = checker.ParseAndCheckProject(Project38.options) |> Async.RunSynchronouslyImmediate - let printAbstractSignature (s: FSharpAbstractSignature) = let printType (t: FSharpType) = hash t |> ignore // smoke test to check hash code doesn't loop @@ -5358,7 +5353,6 @@ let ``Test project42 to ensure cached checked results are invalidated`` () = FileSystem.OpenFileForWriteShim(Project42.fileName1).Write("""module File1""") try let checkedFile2Again = checker.ParseAndCheckFileInProject(Project42.fileName2, text2.GetHashCode(), text2, Project42.options) |> Async.RunSynchronouslyImmediate - match checkedFile2Again with | _, FSharpCheckFileAnswer.Succeeded(checkedFile2AgainResults) -> Assert.NotEmpty(checkedFile2AgainResults.Diagnostics) // this should contain errors as File1 does not contain the function `test()` @@ -5900,7 +5894,6 @@ let checkContentAsScript content = let scriptFullPath = Path.Combine(tempDir, scriptName) let sourceText = SourceText.ofString content let projectOptions, _ = checker.GetProjectOptionsFromScript(scriptFullPath, sourceText, useSdkRefs = true, assumeDotNetFramework = false) |> Async.RunSynchronouslyImmediate - let parseOptions, _ = checker.GetParsingOptionsFromProjectOptions projectOptions let parseResults = checker.ParseFile(scriptFullPath, sourceText, parseOptions) |> Async.RunSynchronouslyImmediate let checkResults = checker.CheckFileInProject(parseResults, scriptFullPath, 0, sourceText, projectOptions) |> Async.RunSynchronouslyImmediate diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs index 3bfb4b780a3..15f484bcb44 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs @@ -47,7 +47,7 @@ module Helpers = let completed = t.Wait(TimeSpan.FromSeconds 30.0) Assert.True(completed, "Task did not finish after waiting for 30 seconds.") - (* TEMP disabled Immediate re 20306 + (* TEMP disabled Immediate re #20306 let asyncWait immediate (a: Async<'T>): 'T = if immediate then Async.RunSynchronouslyImmediate a else Async.RunSynchronously a From ef40c5f53f96aa0049a83fd86cf728cc813a464b Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 21 Aug 2026 18:33:03 +0100 Subject: [PATCH 05/12] doc: Clarify test collection --- tests/FSharp.Test.Utilities/XunitSetup.fs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/FSharp.Test.Utilities/XunitSetup.fs b/tests/FSharp.Test.Utilities/XunitSetup.fs index 87ebb5436da..0c725cb2f16 100644 --- a/tests/FSharp.Test.Utilities/XunitSetup.fs +++ b/tests/FSharp.Test.Utilities/XunitSetup.fs @@ -17,7 +17,14 @@ type FSharpTestAssemblyFixture() = log $"Server GC enabled: {Runtime.GCSettings.IsServerGC}" logConfig initialConfig -/// Exclude from parallelization. Execute test cases in sequence and do not run any other collections at the same time. +module XUnitSetup = + + [); CaptureConsole; CaptureTrace>] + do () + +/// Modules/Types included in this Collection (via `[`)): +/// 1. do not run concurrently with other tests or modules in the collection (typical behavior) +/// 2. run entirely isolated from all other tests in a given test run (including ones not included in a Collection) due to `DisableParallelization = true` /// see https://github.com/xunit/xunit/issues/1999#issuecomment-522635397 [] type NotThreadSafeResourceCollection() = class end @@ -26,8 +33,3 @@ type NotThreadSafeResourceCollection() = class end /// In case Xunit 3 enables internal parallelization of test collections. [] type RunTestCasesInSequenceAttribute() = inherit Attribute() - -module XUnitSetup = - - [); CaptureConsole; CaptureTrace>] - do () \ No newline at end of file From 6abcd1b6149d4b3a01c79b1ffaee12891467d929 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 21 Aug 2026 18:33:30 +0100 Subject: [PATCH 06/12] cleanup: Remove XUNIT_EXTRAS straggler reference --- tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj index 4ffdca6d934..8bfa9ceef96 100644 --- a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj +++ b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj @@ -9,9 +9,6 @@ true $(OtherFlags) --realsig- true - - - true From 973bc153670a1bfedf940c4150cdcf3bcabc65fd Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 21 Aug 2026 18:58:47 +0100 Subject: [PATCH 07/12] refactor: Replace RunTestCasesInSequence with comments --- ...eturnTypeDirectedPartialActivePatternTests.fs | 4 ++-- .../SequenceExpressionTests.fs | 4 ++-- .../ErrorList/ScriptDiagnosticsTests.fs | 1 - tests/FSharp.Compiler.Service.Tests/FsiTests.fs | 1 - .../ModuleReaderCancellationTests.fs | 3 +-- .../ProjectAnalysisTests.fs | 16 ++++++++-------- .../ScriptOptionsTests.fs | 2 -- tests/FSharp.Test.Utilities/XunitSetup.fs | 5 ----- .../CodeGen/EmittedIL/DeterministicTests.fs | 2 -- 9 files changed, 13 insertions(+), 25 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/BooleanReturningAndReturnTypeDirectedPartialActivePatternTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/BooleanReturningAndReturnTypeDirectedPartialActivePatternTests.fs index 9a0a51a6eb2..9e52273188d 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/BooleanReturningAndReturnTypeDirectedPartialActivePatternTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/BooleanReturningAndReturnTypeDirectedPartialActivePatternTests.fs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -// Because of shared fsi session. -[] +// NOTE tests within this module utilize long-lived local state (caching a fsi session) +// and hence rely on the assumption that individual Xunit tests within a given module are never run concurrently module Language.BooleanReturningAndReturnTypeDirectedPartialActivePatternTests open Xunit diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressions/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressions/SequenceExpressionTests.fs index a9dcfe2fde4..5629de51c13 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressions/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressions/SequenceExpressionTests.fs @@ -7,8 +7,8 @@ open Xunit open FSharp.Test.Compiler open FSharp.Test.ScriptHelpers -// Run sequentially because of shared fsiSession. -[] +// NOTE tests within this module utilize long-lived local state (caching a fsi session) +// and hence rely on the assumption that individual Xunit tests within a given module are never run concurrently module SequenceExpression = let fsiSession = getSessionForEval [||] LangVersion.Preview diff --git a/tests/FSharp.Compiler.Service.Tests/ErrorList/ScriptDiagnosticsTests.fs b/tests/FSharp.Compiler.Service.Tests/ErrorList/ScriptDiagnosticsTests.fs index 6d69a3f2ca2..feedf7f853a 100644 --- a/tests/FSharp.Compiler.Service.Tests/ErrorList/ScriptDiagnosticsTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ErrorList/ScriptDiagnosticsTests.fs @@ -1,4 +1,3 @@ -[] module FSharp.Compiler.Service.Tests.ScriptDiagnosticsTests open System diff --git a/tests/FSharp.Compiler.Service.Tests/FsiTests.fs b/tests/FSharp.Compiler.Service.Tests/FsiTests.fs index a93b5d42570..897bd2cc887 100644 --- a/tests/FSharp.Compiler.Service.Tests/FsiTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/FsiTests.fs @@ -12,7 +12,6 @@ module MyModule = let test(x: int) = () // Running in parallel is unstable with occasional System.IO.FileLoadException: Could not load file or assembly 'FSI-ASSEMBLY... -[] module FsiTests = let createFsiSession (useOneDynamicAssembly: bool) = diff --git a/tests/FSharp.Compiler.Service.Tests/ModuleReaderCancellationTests.fs b/tests/FSharp.Compiler.Service.Tests/ModuleReaderCancellationTests.fs index fc3641811a5..7deb1974b02 100644 --- a/tests/FSharp.Compiler.Service.Tests/ModuleReaderCancellationTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ModuleReaderCancellationTests.fs @@ -1,5 +1,4 @@ -// Sequential execution because of shared mutable state. -[] +// NOTE These tests utilize mutable long-lived state/caching and hence rely on the assumption that individual Xunit tests within a given module are never run concurrently module FSharp.Compiler.Service.Tests.ModuleReaderCancellationTests open System diff --git a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs index b9d224c3d61..3230e1bf5f6 100644 --- a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs @@ -3719,7 +3719,7 @@ let _ = MyType().DoNothing() { checker.GetProjectOptionsFromCommandLineArgs(projFileName, args) with SourceFiles = fileNames }) // Uses TestTP (built locally) — no NuGet needed, deterministic. -[] +[] let ``Test Project25 whole project errors`` () = let wholeProjectResults = Project25.checker.ParseAndCheckProject(Project25.options.Value) |> Async.RunSynchronouslyImmediate @@ -3728,7 +3728,7 @@ let ``Test Project25 whole project errors`` () = wholeProjectResults.Diagnostics.Length |> shouldEqual 0 -[] +[] let ``Test Project25 symbol uses of type-provided members`` () = let wholeProjectResults = Project25.checker.ParseAndCheckProject(Project25.options.Value) |> Async.RunSynchronouslyImmediate @@ -3787,7 +3787,7 @@ let ``Test Project25 symbol uses of type-provided members`` () = [| ("file1", ((5, 8), (5, 21))) // line 5: T().DoNothing ("file1", ((10, 8), (10, 26))) |] // line 10: MyType().DoNothing -[] +[] let ``GetDeclarationLocation on a provided-ctor without DefinitionLocationAttribute returns DeclFound (regression #5538)`` () = let wholeProjectResults = Project25.checker.ParseAndCheckProject(Project25.options.Value) @@ -3828,7 +3828,7 @@ let ``GetDeclarationLocation on a provided-ctor without DefinitionLocationAttrib | FindDeclResult.ExternalDecl _ -> failwith "expected DeclFound for provided-ctor `T()`, got ExternalDecl" -[] +[] let ``GetDeclarationLocation on a provided-ctor invoked through the original provided name returns DeclFound (regression #5538)`` () = let wholeProjectResults = Project25.checker.ParseAndCheckProject(Project25.options.Value) @@ -3865,7 +3865,7 @@ let ``GetDeclarationLocation on a provided-ctor invoked through the original pro | FindDeclResult.ExternalDecl _ -> failwith "expected DeclFound for provided-ctor `MyType()`, got ExternalDecl" -[] +[] let ``Test Project25 symbol uses of type-provided types`` () = let wholeProjectResults = Project25.checker.ParseAndCheckProject(Project25.options.Value) |> Async.RunSynchronouslyImmediate @@ -3888,7 +3888,7 @@ let ``Test Project25 symbol uses of type-provided types`` () = ("file1", ((5, 8), (5, 9))) // line 5: let _ = >T<() (T resolves to MyType) ("file1", ((10, 8), (10, 14))) |] // line 10: let _ = >MyType<() -[] +[] let ``Test Project25 symbol uses of fully-qualified records`` () = let wholeProjectResults = Project25.checker.ParseAndCheckProject(Project25.options.Value) |> Async.RunSynchronouslyImmediate @@ -5467,7 +5467,7 @@ let x = (1 = 3.0) let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } -[] +[] let ``Test diagnostics with line directives active`` () = let wholeProjectResults = checker.ParseAndCheckProject(ProjectLineDirectives.options) |> Async.RunSynchronouslyImmediate @@ -5485,7 +5485,7 @@ let ``Test diagnostics with line directives active`` () = let m = e.Range in m.StartLine, m.EndLine, m.FileName ] |> shouldEqual [10, 10, "Test.fsy"] -[] +[] let ``Test diagnostics with line directives ignored`` () = // If you pass hidden IDE flag --ignorelinedirectives, the diagnostics are reported w.r.t. the source diff --git a/tests/FSharp.Compiler.Service.Tests/ScriptOptionsTests.fs b/tests/FSharp.Compiler.Service.Tests/ScriptOptionsTests.fs index 0ac8e58e1fb..855446fe7ab 100644 --- a/tests/FSharp.Compiler.Service.Tests/ScriptOptionsTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ScriptOptionsTests.fs @@ -1,5 +1,3 @@ -// Because of script closure cache. -[] module FSharp.Compiler.Service.Tests.ScriptOptionsTests open Xunit diff --git a/tests/FSharp.Test.Utilities/XunitSetup.fs b/tests/FSharp.Test.Utilities/XunitSetup.fs index 0c725cb2f16..a1385bc1174 100644 --- a/tests/FSharp.Test.Utilities/XunitSetup.fs +++ b/tests/FSharp.Test.Utilities/XunitSetup.fs @@ -28,8 +28,3 @@ module XUnitSetup = /// see https://github.com/xunit/xunit/issues/1999#issuecomment-522635397 [] type NotThreadSafeResourceCollection() = class end - -/// Mark test cases as not safe to run in parallel with other test cases of the same test collection. -/// In case Xunit 3 enables internal parallelization of test collections. -[] -type RunTestCasesInSequenceAttribute() = inherit Attribute() diff --git a/tests/fsharp/Compiler/CodeGen/EmittedIL/DeterministicTests.fs b/tests/fsharp/Compiler/CodeGen/EmittedIL/DeterministicTests.fs index d370e21be95..104646b7ef9 100644 --- a/tests/fsharp/Compiler/CodeGen/EmittedIL/DeterministicTests.fs +++ b/tests/fsharp/Compiler/CodeGen/EmittedIL/DeterministicTests.fs @@ -8,8 +8,6 @@ open FSharp.Test open FSharp.Test.Compiler open Xunit - -[] module DeterministicTests = let commonOptions = ["--refonly";"--deterministic";"--nooptimizationdata"] From 0fb4bd5e5ad22cd5346faff48451e3e25b0aafbb Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 21 Aug 2026 20:12:05 +0100 Subject: [PATCH 08/12] refactor(RunTestCasesInSequenceAttribute): replace with DisableParallelization shims --- ...AndReturnTypeDirectedPartialActivePatternTests.fs | 4 ++-- .../SequenceExpressions/SequenceExpressionTests.fs | 4 ++-- .../ErrorList/ScriptDiagnosticsTests.fs | 2 ++ tests/FSharp.Compiler.Service.Tests/FsiTests.fs | 1 + .../ModuleReaderCancellationTests.fs | 3 ++- .../ProjectAnalysisTests.fs | 10 +++++----- .../ScriptOptionsTests.fs | 2 ++ tests/FSharp.Test.Utilities/XunitSetup.fs | 12 ++++++++++++ .../Compiler/CodeGen/EmittedIL/DeterministicTests.fs | 2 ++ 9 files changed, 30 insertions(+), 10 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/BooleanReturningAndReturnTypeDirectedPartialActivePatternTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/BooleanReturningAndReturnTypeDirectedPartialActivePatternTests.fs index 9e52273188d..60c5c919bc8 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/BooleanReturningAndReturnTypeDirectedPartialActivePatternTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/BooleanReturningAndReturnTypeDirectedPartialActivePatternTests.fs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -// NOTE tests within this module utilize long-lived local state (caching a fsi session) -// and hence rely on the assumption that individual Xunit tests within a given module are never run concurrently +// Leverage caching/prevent concurrent mutation via long-lived fsiSession in module state +[] module Language.BooleanReturningAndReturnTypeDirectedPartialActivePatternTests open Xunit diff --git a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressions/SequenceExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressions/SequenceExpressionTests.fs index 5629de51c13..993a7238f55 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressions/SequenceExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/SequenceExpressions/SequenceExpressionTests.fs @@ -7,8 +7,8 @@ open Xunit open FSharp.Test.Compiler open FSharp.Test.ScriptHelpers -// NOTE tests within this module utilize long-lived local state (caching a fsi session) -// and hence rely on the assumption that individual Xunit tests within a given module are never run concurrently +// Leverage caching/prevent concurrent mutation via long-lived fsiSession in module state +[] module SequenceExpression = let fsiSession = getSessionForEval [||] LangVersion.Preview diff --git a/tests/FSharp.Compiler.Service.Tests/ErrorList/ScriptDiagnosticsTests.fs b/tests/FSharp.Compiler.Service.Tests/ErrorList/ScriptDiagnosticsTests.fs index feedf7f853a..c126f8483f4 100644 --- a/tests/FSharp.Compiler.Service.Tests/ErrorList/ScriptDiagnosticsTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ErrorList/ScriptDiagnosticsTests.fs @@ -1,3 +1,5 @@ +// TODO @T-Gro document reasoning for inhibiting parallelization +[] module FSharp.Compiler.Service.Tests.ScriptDiagnosticsTests open System diff --git a/tests/FSharp.Compiler.Service.Tests/FsiTests.fs b/tests/FSharp.Compiler.Service.Tests/FsiTests.fs index 897bd2cc887..f5121a2d115 100644 --- a/tests/FSharp.Compiler.Service.Tests/FsiTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/FsiTests.fs @@ -12,6 +12,7 @@ module MyModule = let test(x: int) = () // Running in parallel is unstable with occasional System.IO.FileLoadException: Could not load file or assembly 'FSI-ASSEMBLY... +[] module FsiTests = let createFsiSession (useOneDynamicAssembly: bool) = diff --git a/tests/FSharp.Compiler.Service.Tests/ModuleReaderCancellationTests.fs b/tests/FSharp.Compiler.Service.Tests/ModuleReaderCancellationTests.fs index 7deb1974b02..8140b5717aa 100644 --- a/tests/FSharp.Compiler.Service.Tests/ModuleReaderCancellationTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ModuleReaderCancellationTests.fs @@ -1,4 +1,5 @@ -// NOTE These tests utilize mutable long-lived state/caching and hence rely on the assumption that individual Xunit tests within a given module are never run concurrently +// Tests utilize caching in module state (checker) and `mutable` state +[] module FSharp.Compiler.Service.Tests.ModuleReaderCancellationTests open System diff --git a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs index 3230e1bf5f6..7b43878eeb2 100644 --- a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs @@ -3719,7 +3719,7 @@ let _ = MyType().DoNothing() { checker.GetProjectOptionsFromCommandLineArgs(projFileName, args) with SourceFiles = fileNames }) // Uses TestTP (built locally) — no NuGet needed, deterministic. -[] +[] // Inhibit shared usage of Project25.checker module state let ``Test Project25 whole project errors`` () = let wholeProjectResults = Project25.checker.ParseAndCheckProject(Project25.options.Value) |> Async.RunSynchronouslyImmediate @@ -3728,7 +3728,7 @@ let ``Test Project25 whole project errors`` () = wholeProjectResults.Diagnostics.Length |> shouldEqual 0 -[] +[] // Inhibit shared usage of Project25.checker module state let ``Test Project25 symbol uses of type-provided members`` () = let wholeProjectResults = Project25.checker.ParseAndCheckProject(Project25.options.Value) |> Async.RunSynchronouslyImmediate @@ -3787,7 +3787,7 @@ let ``Test Project25 symbol uses of type-provided members`` () = [| ("file1", ((5, 8), (5, 21))) // line 5: T().DoNothing ("file1", ((10, 8), (10, 26))) |] // line 10: MyType().DoNothing -[] +[] // Inhibit shared usage of Project25.checker module state let ``GetDeclarationLocation on a provided-ctor without DefinitionLocationAttribute returns DeclFound (regression #5538)`` () = let wholeProjectResults = Project25.checker.ParseAndCheckProject(Project25.options.Value) @@ -5467,7 +5467,7 @@ let x = (1 = 3.0) let args = mkProjectCommandLineArgs (dllName, []) let options = { checker.GetProjectOptionsFromCommandLineArgs (projFileName, args) with SourceFiles = fileNames } -[] +[] // Avoid concurrent use of checker let ``Test diagnostics with line directives active`` () = let wholeProjectResults = checker.ParseAndCheckProject(ProjectLineDirectives.options) |> Async.RunSynchronouslyImmediate @@ -5485,7 +5485,7 @@ let ``Test diagnostics with line directives active`` () = let m = e.Range in m.StartLine, m.EndLine, m.FileName ] |> shouldEqual [10, 10, "Test.fsy"] -[] +[] // Avoid concurrent use of checker let ``Test diagnostics with line directives ignored`` () = // If you pass hidden IDE flag --ignorelinedirectives, the diagnostics are reported w.r.t. the source diff --git a/tests/FSharp.Compiler.Service.Tests/ScriptOptionsTests.fs b/tests/FSharp.Compiler.Service.Tests/ScriptOptionsTests.fs index 855446fe7ab..fd2bb7cd311 100644 --- a/tests/FSharp.Compiler.Service.Tests/ScriptOptionsTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ScriptOptionsTests.fs @@ -1,3 +1,5 @@ +// Avoid parallelism to leverage script closure cache. +[] module FSharp.Compiler.Service.Tests.ScriptOptionsTests open Xunit diff --git a/tests/FSharp.Test.Utilities/XunitSetup.fs b/tests/FSharp.Test.Utilities/XunitSetup.fs index a1385bc1174..56ba6ab9df3 100644 --- a/tests/FSharp.Test.Utilities/XunitSetup.fs +++ b/tests/FSharp.Test.Utilities/XunitSetup.fs @@ -28,3 +28,15 @@ module XUnitSetup = /// see https://github.com/xunit/xunit/issues/1999#issuecomment-522635397 [] type NotThreadSafeResourceCollection() = class end + +namespace Xunit + +// Shim to be deleted when xunit dependency updates to >= 4 +[] +type TestClassAttribute(DisableParallelization: bool) = + inherit System.Attribute() + +// Shim to be removed and replaced with direct usage of FactAttribute from xunit >= 4 when xunit dependency updates to >= 4 +[] +type Fact4Attribute(DisableParallelization: bool) = + inherit FactAttribute() diff --git a/tests/fsharp/Compiler/CodeGen/EmittedIL/DeterministicTests.fs b/tests/fsharp/Compiler/CodeGen/EmittedIL/DeterministicTests.fs index 104646b7ef9..fe50b0bfc5e 100644 --- a/tests/fsharp/Compiler/CodeGen/EmittedIL/DeterministicTests.fs +++ b/tests/fsharp/Compiler/CodeGen/EmittedIL/DeterministicTests.fs @@ -8,6 +8,8 @@ open FSharp.Test open FSharp.Test.Compiler open Xunit +// A single temp file (name in module state) undergoes diverse manipulations across various tests +[] module DeterministicTests = let commonOptions = ["--refonly";"--deterministic";"--nooptimizationdata"] From bab7105ef0bed80e3f332b37657fa032c1b930d9 Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 21 Aug 2026 20:35:35 +0100 Subject: [PATCH 09/12] fix: Remove warning build break --- tests/FSharp.Test.Utilities/XunitSetup.fs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/FSharp.Test.Utilities/XunitSetup.fs b/tests/FSharp.Test.Utilities/XunitSetup.fs index 56ba6ab9df3..7e5a3b1443b 100644 --- a/tests/FSharp.Test.Utilities/XunitSetup.fs +++ b/tests/FSharp.Test.Utilities/XunitSetup.fs @@ -31,6 +31,8 @@ type NotThreadSafeResourceCollection() = class end namespace Xunit +#nowarn "1182" // the DisableParallelization properties are unused as these are shims waiting for us to move to xunit3 >= 4 + // Shim to be deleted when xunit dependency updates to >= 4 [] type TestClassAttribute(DisableParallelization: bool) = From b1aff551b08b239269325c2fce3ceda9f8ec5cef Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 21 Aug 2026 22:22:11 +0100 Subject: [PATCH 10/12] Roll back compiler temp RunSynchronously use --- src/Compiler/Driver/fsc.fs | 4 ++-- src/Compiler/Facilities/DiagnosticsLogger.fs | 2 +- src/Compiler/Interactive/fsi.fs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Compiler/Driver/fsc.fs b/src/Compiler/Driver/fsc.fs index c6d84aed809..2333b971192 100644 --- a/src/Compiler/Driver/fsc.fs +++ b/src/Compiler/Driver/fsc.fs @@ -599,7 +599,7 @@ let main1 // Import basic assemblies let tcGlobals, frameworkTcImports = TcImports.BuildFrameworkTcImports(foundationalTcConfigP, sysRes, otherRes) - |> Async.RunSynchronously // TEMP: NOT Immediate re #20306 + |> Async.RunSynchronouslyImmediate let ilSourceDocs = [ @@ -647,7 +647,7 @@ let main1 let tcImports = TcImports.BuildNonFrameworkTcImports(tcConfigP, frameworkTcImports, otherRes, knownUnresolved, dependencyProvider) - |> Async.RunSynchronously // TEMP: NOT Immediate re #20306 + |> Async.RunSynchronouslyImmediate // register tcImports to be disposed in future disposables.Register tcImports diff --git a/src/Compiler/Facilities/DiagnosticsLogger.fs b/src/Compiler/Facilities/DiagnosticsLogger.fs index 202993c8867..77351b7dd89 100644 --- a/src/Compiler/Facilities/DiagnosticsLogger.fs +++ b/src/Compiler/Facilities/DiagnosticsLogger.fs @@ -1019,7 +1019,7 @@ type StackGuard(name: string) = Thread.CurrentThread.Name <- $"F# Extra Compilation Thread for {name} (depth {depthWhenJump})" return f () } - |> Async.RunSynchronously // TEMP: NOT Immediate re #20306 + |> Async.RunSynchronouslyImmediate finally depth.Value <- depth.Value - 1 diff --git a/src/Compiler/Interactive/fsi.fs b/src/Compiler/Interactive/fsi.fs index e18e2afcdee..436d0a0c777 100644 --- a/src/Compiler/Interactive/fsi.fs +++ b/src/Compiler/Interactive/fsi.fs @@ -4802,7 +4802,7 @@ type FsiEvaluationSession try let tcConfig = tcConfigP.Get(ctokStartup) - checker.FrameworkImportsCache.Get tcConfig |> Async.RunSynchronously // TEMP: NOT Immediate re #20306 + checker.FrameworkImportsCache.Get tcConfig |> Async.RunSynchronouslyImmediate with e -> stopProcessingRecovery e range0 failwithf "Error creating evaluation session: %A" e @@ -4816,7 +4816,7 @@ type FsiEvaluationSession unresolvedReferences, fsiOptions.DependencyProvider ) - |> Async.RunSynchronously // TEMP: NOT Immediate re #20306 + |> Async.RunSynchronouslyImmediate with e -> stopProcessingRecovery e range0 failwithf "Error creating evaluation session: %A" e From 0548a8b4e66daa01e620628a855def189b597eba Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Fri, 21 Aug 2026 22:36:26 +0100 Subject: [PATCH 11/12] straggler RunTestCasesInSequence -> DisableParallization migrations --- tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs index 7b43878eeb2..0076df3203b 100644 --- a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs @@ -3828,7 +3828,7 @@ let ``GetDeclarationLocation on a provided-ctor without DefinitionLocationAttrib | FindDeclResult.ExternalDecl _ -> failwith "expected DeclFound for provided-ctor `T()`, got ExternalDecl" -[] +[] // Inhibit shared usage of Project25.checker module state let ``GetDeclarationLocation on a provided-ctor invoked through the original provided name returns DeclFound (regression #5538)`` () = let wholeProjectResults = Project25.checker.ParseAndCheckProject(Project25.options.Value) @@ -3865,7 +3865,7 @@ let ``GetDeclarationLocation on a provided-ctor invoked through the original pro | FindDeclResult.ExternalDecl _ -> failwith "expected DeclFound for provided-ctor `MyType()`, got ExternalDecl" -[] +[] // Inhibit shared usage of Project25.checker module state let ``Test Project25 symbol uses of type-provided types`` () = let wholeProjectResults = Project25.checker.ParseAndCheckProject(Project25.options.Value) |> Async.RunSynchronouslyImmediate @@ -3888,7 +3888,7 @@ let ``Test Project25 symbol uses of type-provided types`` () = ("file1", ((5, 8), (5, 9))) // line 5: let _ = >T<() (T resolves to MyType) ("file1", ((10, 8), (10, 14))) |] // line 10: let _ = >MyType<() -[] +[] // Inhibit shared usage of Project25.checker module state let ``Test Project25 symbol uses of fully-qualified records`` () = let wholeProjectResults = Project25.checker.ParseAndCheckProject(Project25.options.Value) |> Async.RunSynchronouslyImmediate From e841befc93b76df69e2fb8d7f1986e518ac35c7c Mon Sep 17 00:00:00 2001 From: Ruben Bartelink Date: Sun, 23 Aug 2026 22:58:27 +0100 Subject: [PATCH 12/12] chore(Tests): Reinstate RunSynchronouslyImmediate usage --- .../Microsoft.FSharp.Control/AsyncModuleFunctions.fs | 6 ++---- .../FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs | 9 +++------ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModuleFunctions.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModuleFunctions.fs index beded1df0c6..365e5c053f2 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModuleFunctions.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModuleFunctions.fs @@ -22,10 +22,8 @@ let cancelWithToken (tcs: TaskCompletionSource<'T>) = ct #endif -// TEMP disabled Immediate re #20306 // let asyncWait (a: Async<'T>): 'T = Async.RunSynchronouslyImmediate a -// TEMP disabled Immediate re #20306 // let asyncWaitWithCt (ct: CancellationToken) (a: Async<'T>): 'T = Async.RunSynchronouslyImmediate(a, cancellationToken = ct) -let asyncWait (a: Async<'T>): 'T = Async.RunSynchronously a -let asyncWaitWithCt (ct: CancellationToken) (a: Async<'T>): 'T = Async.RunSynchronously(a, cancellationToken = ct) +let asyncWait (a: Async<'T>): 'T = Async.RunSynchronouslyImmediate a +let asyncWaitWithCt (ct: CancellationToken) (a: Async<'T>): 'T = Async.RunSynchronouslyImmediate(a, cancellationToken = ct) [] let ``Async.result wraps value`` () = diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs index 15f484bcb44..9083a23418b 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncType.fs @@ -47,16 +47,13 @@ module Helpers = let completed = t.Wait(TimeSpan.FromSeconds 30.0) Assert.True(completed, "Task did not finish after waiting for 30 seconds.") - (* TEMP disabled Immediate re #20306 let asyncWait immediate (a: Async<'T>): 'T = if immediate then Async.RunSynchronouslyImmediate a else Async.RunSynchronously a let asyncWaitWithCt immediate (ct: CancellationToken) (a: Async<'T>): 'T = if immediate then Async.RunSynchronouslyImmediate(a, cancellationToken = ct) else Async.RunSynchronously(a, cancellationToken = ct) - *) - let asyncWait (_immediate: bool) (a: Async<'T>): 'T = Async.RunSynchronously a - let asyncWaitWithCt (_immediate: bool) (ct: CancellationToken) (a: Async<'T>): 'T = Async.RunSynchronously(a, cancellationToken = ct) + let asyncWaitImm a = asyncWait true a let asyncWaitWithCtImm ct a = asyncWaitWithCt true ct a @@ -907,7 +904,7 @@ module AsyncTaskLikeAwaitTests = |> asyncWaitImm Assert.Equal(42, result) -// Intentionally in same collection to rule out potential flakiness due to concurrency re #20306 +// Intentionally in same collection to help rule out potential flakiness due to concurrency re #20306 [] module AsyncStartTaskImmediateTaskLikeTests = @@ -949,7 +946,7 @@ module AsyncStartTaskImmediateTaskLikeTests = Assert.Equal(cts.Token, capturedCt) -// Intentionally in same collection to rule out potential flakiness due to concurrency re #20306 +// Intentionally in same collection to help rule out potential flakiness due to concurrency re #20306 [] module AsyncAwaitStackTraceTests =