diff --git a/src/fsharp/FSharp.Core/async.fs b/src/fsharp/FSharp.Core/async.fs index 6578233e762..ccfb8dd75db 100644 --- a/src/fsharp/FSharp.Core/async.fs +++ b/src/fsharp/FSharp.Core/async.fs @@ -1066,8 +1066,8 @@ namespace Microsoft.FSharp.Control [] let RunSynchronously cancellationToken (computation: Async<'T>) timeout = // Reuse the current ThreadPool thread if possible. - match Thread.CurrentThread.IsThreadPoolThread, timeout with - | true, None -> RunImmediate cancellationToken computation + match SynchronizationContext.Current, Thread.CurrentThread.IsThreadPoolThread, timeout with + | null, true, None -> RunImmediate cancellationToken computation | _ -> QueueAsyncAndWaitForResultSynchronously cancellationToken computation timeout [] diff --git a/src/fsharp/FSharp.Core/async.fsi b/src/fsharp/FSharp.Core/async.fsi index d127f6f6380..aeccba2ea0f 100644 --- a/src/fsharp/FSharp.Core/async.fsi +++ b/src/fsharp/FSharp.Core/async.fsi @@ -54,14 +54,19 @@ namespace Microsoft.FSharp.Control /// /// If no cancellation token is provided then the default cancellation token is used. /// + /// The computation is started on the current thread if is null, + /// has + /// of true, and no timeout is specified. Otherwise the computation is started by queueing a new work item in the thread pool, + /// and the current thread is blocked awaiting the completion of the computation. + /// /// The timeout parameter is given in milliseconds. A value of -1 is equivalent to - /// System.Threading.Timeout.Infinite. + /// . + /// /// /// The computation to run. /// The amount of time in milliseconds to wait for the result of the /// computation before raising a . If no value is provided - /// for timeout then a default of -1 is used to correspond to . - /// If a cancellable cancellationToken is provided, timeout parameter will be ignored + /// for timeout then a default of -1 is used to correspond to . /// The cancellation token to be associated with the computation. /// If one is not supplied, the default cancellation token is used. /// diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs index 1410106250f..89b74783e9f 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs @@ -409,8 +409,8 @@ type AsyncModule() = [] member _.``RunSynchronously.NoThreadJumpsAndTimeout.DifferentSyncContexts``() = let run syncContext = - let old = System.Threading.SynchronizationContext.Current - System.Threading.SynchronizationContext.SetSynchronizationContext(syncContext) + let old = SynchronizationContext.Current + SynchronizationContext.SetSynchronizationContext(syncContext) let longRunningTask = async { sleep(5000) } let mutable failed = false try @@ -418,11 +418,31 @@ type AsyncModule() = failed <- true with :? System.TimeoutException -> () - System.Threading.SynchronizationContext.SetSynchronizationContext(old) + SynchronizationContext.SetSynchronizationContext(old) if failed then Assert.Fail("TimeoutException expected") run null run (System.Threading.SynchronizationContext()) + [] + // See https://github.com/dotnet/fsharp/issues/12637#issuecomment-1020199383 + member _.``RunSynchronously.ThreadJump.IfSyncCtxtNonNull``() = + async { + do! Async.SwitchToThreadPool() + let old = SynchronizationContext.Current + SynchronizationContext.SetSynchronizationContext(SynchronizationContext()) + Assert.NotNull(SynchronizationContext.Current) + Assert.True(Thread.CurrentThread.IsThreadPoolThread) + let computation = + async { + let ctxt = SynchronizationContext.Current + Assert.Null(ctxt) + Assert.True(Thread.CurrentThread.IsThreadPoolThread) + } + Async.RunSynchronously(computation) + SynchronizationContext.SetSynchronizationContext(old) + } + |> Async.RunSynchronously + [] member _.``RaceBetweenCancellationAndError.AwaitWaitHandle``() = let disposedEvent = new System.Threading.ManualResetEvent(false)