The value must satisfy the GetAwaiter pattern: it must have a GetAwaiter() method
+ ///
+ /// For the common case where you are running a Task within an Asynchronous Computation,
+ /// see StartTaskImmediate, which surfaces the ambient CancellationToken
+ /// so that it can be passed to the Task being started.
+ /// The value must satisfy the GetAwaiter pattern: it must have a GetAwaiter() method
/// returning an awaiter implementing
/// with IsCompleted and GetResult() members.
/// Exceptions thrown by GetResult() are propagated directly.
@@ -1330,6 +1426,53 @@ namespace Microsoft.FSharp.Control
and ^Awaiter: (member get_IsCompleted: unit -> bool)
and ^Awaiter: (member GetResult: unit -> 'T)
+ /// Creates an asynchronous computation that passes the ambient Async.CancellationToken to
+ /// createTask, and then awaits the resulting task-like value.
+ ///
+ /// A function that accepts a CancellationToken and returns a task-like value
+ /// satisfying the GetAwaiter pattern.
+ ///
+ /// The value returned by createTask must satisfy the GetAwaiter pattern: it must have a
+ /// GetAwaiter() method returning an awaiter implementing
+ ///
+ /// with IsCompleted and GetResult() members.
+ ///
+ /// This overload uses statically resolved type parameters (SRTP) so it can accept factories returning
+ /// any task-like type, including YieldAwaitable (from Task.Yield()) and
+ /// ConfiguredTaskAwaitable (from task.ConfigureAwait(false)).
+ /// The specific overloads for , ,
+ /// and
+ /// are preferred when the factory return type is known.
+ ///
+ /// Starting Async Computations
+ ///
+ ///
+ /// // Straightforward: factory returns Task<string>, which is handled by
+ /// // the specific Task<'T> overload of StartTaskImmediate (not this one).
+ /// let fetchPlain (url: string) =
+ /// Async.StartTaskImmediate(fun ct ->
+ /// httpClient.GetStringAsync(url, ct)) // returns Task<string>
+ ///
+ /// // Adding ConfigureAwait(false) to the mix yields a ConfiguredTaskAwaitable<string>,
+ /// // which has no specific overload — this SRTP overload handles it.
+ /// let fetchConfigured (url: string) =
+ /// Async.StartTaskImmediate(fun ct ->
+ /// httpClient.GetStringAsync(url, ct).ConfigureAwait(false))
+ ///
+ /// async {
+ /// let! html = fetchConfigured "https://example.com"
+ /// printfn $"Downloaded {html.Length} chars"
+ /// } |> Async.RunSynchronouslyImmediate
+ ///
+ ///
+ []
+ static member inline StartTaskImmediate< ^TaskLike, ^Awaiter, 'T> :
+ createTask: (CancellationToken -> ^TaskLike) -> Async<'T>
+ when ^TaskLike: (member GetAwaiter: unit -> ^Awaiter)
+ and ^Awaiter :> ICriticalNotifyCompletion
+ and ^Awaiter: (member get_IsCompleted: unit -> bool)
+ and ^Awaiter: (member GetResult: unit -> 'T)
+
/// The F# compiler emits references to this type to implement F# async expressions.
///
/// Async Internals
diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl
index 1f6241a57e3..da9227f2a63 100644
--- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl
+++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl
@@ -633,6 +633,8 @@ Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn T
Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.FSharpAsync`1[T] MakeAsync[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.AsyncActivation`1[T],Microsoft.FSharp.Control.AsyncReturn])
Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.Await.Static$W[TTaskLike,TAwaiter,T](Microsoft.FSharp.Core.FSharpFunc`2[TTaskLike,TAwaiter], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,T], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,System.Boolean], TTaskLike)
Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.Await.Static[TTaskLike,TAwaiter,T](TTaskLike)
+Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.StartTaskImmediate.Static$W[TTaskLike,TAwaiter,T](Microsoft.FSharp.Core.FSharpFunc`2[TTaskLike,TAwaiter], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,T], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,System.Boolean], Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,TTaskLike])
+Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.StartTaskImmediate.Static[TTaskLike,TAwaiter,T](Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,TTaskLike])
Microsoft.FSharp.Control.BackgroundTaskBuilder: System.Threading.Tasks.Task`1[T] RunDynamic[T](Microsoft.FSharp.Core.CompilerServices.ResumableCode`2[Microsoft.FSharp.Control.TaskStateMachineData`1[T],T])
Microsoft.FSharp.Control.BackgroundTaskBuilder: System.Threading.Tasks.Task`1[T] Run[T](Microsoft.FSharp.Core.CompilerServices.ResumableCode`2[Microsoft.FSharp.Control.TaskStateMachineData`1[T],T])
Microsoft.FSharp.Control.CommonExtensions: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] AsyncWrite(System.IO.Stream, Byte[], Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Int32])
@@ -657,6 +659,7 @@ Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Mic
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Ignore[T](Microsoft.FSharp.Control.FSharpAsync`1[T])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Sleep(Int32)
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Sleep(System.TimeSpan)
+Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] StartTaskImmediate(Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,System.Threading.Tasks.Task])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] SwitchToContext(System.Threading.SynchronizationContext)
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] SwitchToNewThread()
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] SwitchToThreadPool()
@@ -677,6 +680,7 @@ Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T]
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] FromBeginEnd[TArg1,T](TArg1, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`3[TArg1,System.AsyncCallback,System.Object],System.IAsyncResult], Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,T], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] FromBeginEnd[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.AsyncCallback,System.Object],System.IAsyncResult], Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,T], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] FromContinuations[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`3[Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit],Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,Microsoft.FSharp.Core.Unit],Microsoft.FSharp.Core.FSharpFunc`2[System.OperationCanceledException,Microsoft.FSharp.Core.Unit]],Microsoft.FSharp.Core.Unit])
+Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] StartTaskImmediate[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,System.Threading.Tasks.Task`1[T]])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] TryCancelled[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpFunc`2[System.OperationCanceledException,Microsoft.FSharp.Core.Unit])
Microsoft.FSharp.Control.FSharpAsync: System.Threading.CancellationToken DefaultCancellationToken
Microsoft.FSharp.Control.FSharpAsync: System.Threading.CancellationToken get_DefaultCancellationToken()
diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl
index 0b025a942de..b0d3ebc9665 100644
--- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl
+++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl
@@ -633,6 +633,8 @@ Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn T
Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.FSharpAsync`1[T] MakeAsync[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.AsyncActivation`1[T],Microsoft.FSharp.Control.AsyncReturn])
Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.Await.Static$W[TTaskLike,TAwaiter,T](Microsoft.FSharp.Core.FSharpFunc`2[TTaskLike,TAwaiter], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,T], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,System.Boolean], TTaskLike)
Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.Await.Static[TTaskLike,TAwaiter,T](TTaskLike)
+Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.StartTaskImmediate.Static$W[TTaskLike,TAwaiter,T](Microsoft.FSharp.Core.FSharpFunc`2[TTaskLike,TAwaiter], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,T], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,System.Boolean], Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,TTaskLike])
+Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.StartTaskImmediate.Static[TTaskLike,TAwaiter,T](Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,TTaskLike])
Microsoft.FSharp.Control.BackgroundTaskBuilder: System.Threading.Tasks.Task`1[T] RunDynamic[T](Microsoft.FSharp.Core.CompilerServices.ResumableCode`2[Microsoft.FSharp.Control.TaskStateMachineData`1[T],T])
Microsoft.FSharp.Control.BackgroundTaskBuilder: System.Threading.Tasks.Task`1[T] Run[T](Microsoft.FSharp.Core.CompilerServices.ResumableCode`2[Microsoft.FSharp.Control.TaskStateMachineData`1[T],T])
Microsoft.FSharp.Control.CommonExtensions: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] AsyncWrite(System.IO.Stream, Byte[], Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Int32])
@@ -657,6 +659,7 @@ Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Mic
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Ignore[T](Microsoft.FSharp.Control.FSharpAsync`1[T])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Sleep(Int32)
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Sleep(System.TimeSpan)
+Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] StartTaskImmediate(Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,System.Threading.Tasks.Task])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] SwitchToContext(System.Threading.SynchronizationContext)
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] SwitchToNewThread()
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] SwitchToThreadPool()
@@ -677,6 +680,7 @@ Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T]
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] FromBeginEnd[TArg1,T](TArg1, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`3[TArg1,System.AsyncCallback,System.Object],System.IAsyncResult], Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,T], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] FromBeginEnd[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.AsyncCallback,System.Object],System.IAsyncResult], Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,T], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] FromContinuations[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`3[Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit],Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,Microsoft.FSharp.Core.Unit],Microsoft.FSharp.Core.FSharpFunc`2[System.OperationCanceledException,Microsoft.FSharp.Core.Unit]],Microsoft.FSharp.Core.Unit])
+Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] StartTaskImmediate[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,System.Threading.Tasks.Task`1[T]])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] TryCancelled[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpFunc`2[System.OperationCanceledException,Microsoft.FSharp.Core.Unit])
Microsoft.FSharp.Control.FSharpAsync: System.Threading.CancellationToken DefaultCancellationToken
Microsoft.FSharp.Control.FSharpAsync: System.Threading.CancellationToken get_DefaultCancellationToken()
diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl
index 9496d1dfe2b..174a09bf2c0 100644
--- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl
+++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl
@@ -635,6 +635,8 @@ Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn T
Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.FSharpAsync`1[T] MakeAsync[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.AsyncActivation`1[T],Microsoft.FSharp.Control.AsyncReturn])
Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.Await.Static$W[TTaskLike,TAwaiter,T](Microsoft.FSharp.Core.FSharpFunc`2[TTaskLike,TAwaiter], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,T], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,System.Boolean], TTaskLike)
Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.Await.Static[TTaskLike,TAwaiter,T](TTaskLike)
+Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.StartTaskImmediate.Static$W[TTaskLike,TAwaiter,T](Microsoft.FSharp.Core.FSharpFunc`2[TTaskLike,TAwaiter], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,T], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,System.Boolean], Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,TTaskLike])
+Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.StartTaskImmediate.Static[TTaskLike,TAwaiter,T](Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,TTaskLike])
Microsoft.FSharp.Control.BackgroundTaskBuilder: System.Threading.Tasks.Task`1[T] RunDynamic[T](Microsoft.FSharp.Core.CompilerServices.ResumableCode`2[Microsoft.FSharp.Control.TaskStateMachineData`1[T],T])
Microsoft.FSharp.Control.BackgroundTaskBuilder: System.Threading.Tasks.Task`1[T] Run[T](Microsoft.FSharp.Core.CompilerServices.ResumableCode`2[Microsoft.FSharp.Control.TaskStateMachineData`1[T],T])
Microsoft.FSharp.Control.CommonExtensions: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] AsyncWrite(System.IO.Stream, Byte[], Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Int32])
@@ -660,6 +662,8 @@ Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Mic
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Ignore[T](Microsoft.FSharp.Control.FSharpAsync`1[T])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Sleep(Int32)
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Sleep(System.TimeSpan)
+Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] StartTaskImmediate(Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,System.Threading.Tasks.Task])
+Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] StartTaskImmediate(Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,System.Threading.Tasks.ValueTask])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] SwitchToContext(System.Threading.SynchronizationContext)
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] SwitchToNewThread()
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] SwitchToThreadPool()
@@ -681,6 +685,8 @@ Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T]
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] FromBeginEnd[TArg1,T](TArg1, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`3[TArg1,System.AsyncCallback,System.Object],System.IAsyncResult], Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,T], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] FromBeginEnd[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`2[System.AsyncCallback,System.Object],System.IAsyncResult], Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,T], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] FromContinuations[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`3[Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.Unit],Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,Microsoft.FSharp.Core.Unit],Microsoft.FSharp.Core.FSharpFunc`2[System.OperationCanceledException,Microsoft.FSharp.Core.Unit]],Microsoft.FSharp.Core.Unit])
+Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] StartTaskImmediate[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,System.Threading.Tasks.Task`1[T]])
+Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] StartTaskImmediate[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,System.Threading.Tasks.ValueTask`1[T]])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] TryCancelled[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpFunc`2[System.OperationCanceledException,Microsoft.FSharp.Core.Unit])
Microsoft.FSharp.Control.FSharpAsync: System.Threading.CancellationToken DefaultCancellationToken
Microsoft.FSharp.Control.FSharpAsync: System.Threading.CancellationToken get_DefaultCancellationToken()
diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl
index b1a377ea33b..ffe81d5ba6b 100644
--- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl
+++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl
@@ -635,6 +635,8 @@ Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn T
Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.FSharpAsync`1[T] MakeAsync[T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Control.AsyncActivation`1[T],Microsoft.FSharp.Control.AsyncReturn])
Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.Await.Static$W[TTaskLike,TAwaiter,T](Microsoft.FSharp.Core.FSharpFunc`2[TTaskLike,TAwaiter], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,T], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,System.Boolean], TTaskLike)
Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.Await.Static[TTaskLike,TAwaiter,T](TTaskLike)
+Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.StartTaskImmediate.Static$W[TTaskLike,TAwaiter,T](Microsoft.FSharp.Core.FSharpFunc`2[TTaskLike,TAwaiter], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,T], Microsoft.FSharp.Core.FSharpFunc`2[TAwaiter,System.Boolean], Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,TTaskLike])
+Microsoft.FSharp.Control.AsyncTaskLikeExtensions: Microsoft.FSharp.Control.FSharpAsync`1[T] Async.StartTaskImmediate.Static[TTaskLike,TAwaiter,T](Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,TTaskLike])
Microsoft.FSharp.Control.BackgroundTaskBuilder: System.Threading.Tasks.Task`1[T] RunDynamic[T](Microsoft.FSharp.Core.CompilerServices.ResumableCode`2[Microsoft.FSharp.Control.TaskStateMachineData`1[T],T])
Microsoft.FSharp.Control.BackgroundTaskBuilder: System.Threading.Tasks.Task`1[T] Run[T](Microsoft.FSharp.Core.CompilerServices.ResumableCode`2[Microsoft.FSharp.Control.TaskStateMachineData`1[T],T])
Microsoft.FSharp.Control.CommonExtensions: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] AsyncWrite(System.IO.Stream, Byte[], Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Int32])
@@ -660,6 +662,8 @@ Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Mic
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Ignore[T](Microsoft.FSharp.Control.FSharpAsync`1[T])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Sleep(Int32)
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Sleep(System.TimeSpan)
+Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] StartTaskImmediate(Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,System.Threading.Tasks.Task])
+Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] StartTaskImmediate(Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,System.Threading.Tasks.ValueTask])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] SwitchToContext(System.Threading.SynchronizationContext)
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] SwitchToNewThread()
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] SwitchToThreadPool()
@@ -676,6 +680,8 @@ Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T]
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] Await[T](System.Threading.Tasks.Task`1[T])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] Await[T](System.Threading.Tasks.ValueTask`1[T])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] AwaitTask[T](System.Threading.Tasks.Task`1[T])
+Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] StartTaskImmediate[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,System.Threading.Tasks.Task`1[T]])
+Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] StartTaskImmediate[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Threading.CancellationToken,System.Threading.Tasks.ValueTask`1[T]])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] FromBeginEnd[TArg1,TArg2,TArg3,T](TArg1, TArg2, TArg3, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`5[TArg1,TArg2,TArg3,System.AsyncCallback,System.Object],System.IAsyncResult], Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,T], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] FromBeginEnd[TArg1,TArg2,T](TArg1, TArg2, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`4[TArg1,TArg2,System.AsyncCallback,System.Object],System.IAsyncResult], Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,T], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]])
Microsoft.FSharp.Control.FSharpAsync: Microsoft.FSharp.Control.FSharpAsync`1[T] FromBeginEnd[TArg1,T](TArg1, Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`3[TArg1,System.AsyncCallback,System.Object],System.IAsyncResult], Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,T], Microsoft.FSharp.Core.FSharpOption`1[Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]])
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 2ce61e65968..e77154860d1 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
@@ -11,7 +11,7 @@ open Xunit
open System.Threading
open System.Threading.Tasks
-// Cancels default token.
+// Test affects global state via Async.CancelDefaultToken
[]
module AsyncType =
@@ -40,6 +40,7 @@ module AsyncType =
async { return () } |> expect Success
+// Multiple tests affect global state via Async.CancelDefaultToken
[]
type AsyncType() =
@@ -54,20 +55,21 @@ type AsyncType() =
let result = t.Wait(TimeSpan.FromSeconds(30.0))
Assert.True(result, "Task did not finish after waiting for 30 seconds.")
- []
- member _.AsyncRunSynchronouslyReusesThreadPoolThread() =
+ []
+ member _.AsyncRunSynchronouslyReusesThreadPoolThread(immediate) =
+ let run a = if immediate then Async.RunSynchronouslyImmediate(a) else Async.RunSynchronously(a)
let action _ =
async {
return
async { return Thread.CurrentThread.ManagedThreadId }
- |> Async.RunSynchronously
+ |> run
}
// This test needs approximately 1000 ThreadPool threads
// if Async.RunSynchronously doesn't reuse them.
let usedThreads =
Seq.init 1000 action
|> Async.Parallel
- |> Async.RunSynchronously
+ |> run
|> Set.ofArray
printfn $"RunSynchronously used {usedThreads.Count} threads. Environment.ProcessorCount is {Environment.ProcessorCount}."
// Some arbitrary large number but in practice it should not use more threads than there are CPU cores.
@@ -82,7 +84,7 @@ type AsyncType() =
match sleepType with
| "int32" -> Async.Sleep(10000000)
| "timespan" -> Async.Sleep(10000000.0 |> TimeSpan.FromMilliseconds)
- | unknown -> raise (NotImplementedException(unknown))
+ | unknown -> failwith $"Unknown {unknown}"
let mutable result = ""
use cts = new CancellationTokenSource()
Async.StartWithContinuations(computation,
@@ -91,7 +93,7 @@ type AsyncType() =
(fun _ -> result <- "Cancel"),
cts.Token)
cts.Cancel()
- Async.Sleep(1000) |> Async.RunSynchronously
+ Async.Sleep(1000) |> Async.RunSynchronouslyImmediate
Assert.AreEqual("Cancel", result)
)
@@ -104,7 +106,7 @@ type AsyncType() =
match sleepType with
| "int32" -> Async.Sleep(10)
| "timespan" -> Async.Sleep(10.0 |> TimeSpan.FromMilliseconds)
- | unknown -> raise (NotImplementedException(unknown))
+ | unknown -> failwith $"Unknown {unknown}"
for i in 1..100 do
let mutable result = ""
use completedEvent = new ManualResetEvent(false)
@@ -129,16 +131,16 @@ type AsyncType() =
do! match sleepType with
| "int32" -> Async.Sleep(-100)
| "timespan" -> Async.Sleep(-100.0 |> TimeSpan.FromMilliseconds)
- | unknown -> raise (NotImplementedException(unknown))
+ | unknown -> failwith $"Unknown {unknown}"
failwith "Expected ArgumentOutOfRangeException"
with
| :? ArgumentOutOfRangeException -> ()
- } |> Async.RunSynchronously
+ } |> Async.RunSynchronouslyImmediate
[]
member _.AsyncSleepInfinitely() =
ignoreSynchCtx (fun () ->
- let computation = Async.Sleep(System.Threading.Timeout.Infinite)
+ 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.
Async.StartWithContinuations(computation,
@@ -146,7 +148,7 @@ type AsyncType() =
(fun _ -> result.TrySetResult("Exception") |> ignore),
(fun _ -> result.TrySetResult("Cancel") |> ignore),
cts.Token)
- let result = result.Task |> Async.AwaitTask |> Async.RunSynchronously
+ let result = result.Task |> Async.Await |> Async.RunSynchronouslyImmediate
Assert.AreEqual("Cancel", result)
)
@@ -156,7 +158,7 @@ type AsyncType() =
let a = async { return s }
let t : Task = Async.StartAsTask a
waitForCompletion t
- Assert.True (t.IsCompleted)
+ Assert.True(t.IsCompleted)
Assert.AreEqual(s, t.Result)
[]
@@ -166,7 +168,7 @@ type AsyncType() =
let doSpinloop () = while spinloop do ()
let a = async {
asyncStarted.Set()
- cts.CancelAfter (100)
+ cts.CancelAfter(100)
doSpinloop()
}
@@ -179,7 +181,7 @@ type AsyncType() =
// Should not finish, we don't eagerly mark the task done just because it's been signaled to cancel.
try
let result = t.Wait(1000)
- Assert.False (result)
+ Assert.False(result)
with :? AggregateException -> Assert.Fail "Task should not finish, yet"
spinloop <- false
@@ -198,8 +200,8 @@ type AsyncType() =
[]
member _.``AwaitTask ignores Async cancellation`` () =
let cts = new CancellationTokenSource()
- let tcs = new TaskCompletionSource()
- let innerTcs = new TaskCompletionSource()
+ let tcs = TaskCompletionSource()
+ let innerTcs = TaskCompletionSource()
let a = innerTcs.Task |> Async.AwaitTask
Async.StartWithContinuations(a, tcs.SetResult, tcs.SetException, ignore >> tcs.SetCanceled, cts.Token)
@@ -207,7 +209,7 @@ type AsyncType() =
cts.CancelAfter(100)
try
let result = tcs.Task.Wait(300)
- Assert.False (result)
+ Assert.False(result)
with :? AggregateException -> Assert.Fail "Should not finish, yet"
innerTcs.SetResult ()
@@ -232,12 +234,12 @@ type AsyncType() =
let cancelled =
try
- Async.RunSynchronously(a, cancellationToken = cts.Token) |> ignore
+ Async.RunSynchronouslyImmediate(a, cancellationToken = cts.Token) |> ignore
false
with :? OperationCanceledException as o -> true
| _ -> false
- Assert.True (cancelled, "Task is not cancelled")
+ Assert.True(cancelled, "Task is not cancelled")
[]
member _.ExceptionPropagatesToTask () =
@@ -251,7 +253,7 @@ type AsyncType() =
t.Wait()
with
e -> exceptionThrown <- true
- Assert.True (t.IsFaulted)
+ Assert.True(t.IsFaulted)
Assert.True(exceptionThrown)
[]
@@ -268,7 +270,7 @@ type AsyncType() =
try
waitForCompletion t
with e -> exceptionThrown <- true
- Assert.True (exceptionThrown)
+ Assert.True(exceptionThrown)
Assert.True(t.IsCanceled)
[]
@@ -291,7 +293,7 @@ type AsyncType() =
try
t.Wait()
with e -> exceptionThrown <- true
- Assert.True (exceptionThrown)
+ Assert.True(exceptionThrown)
Assert.True(t.IsCanceled)
Assert.True(cancelled)
@@ -301,7 +303,7 @@ type AsyncType() =
let a = async { return s }
let t : Task = Async.StartImmediateAsTask a
waitForCompletion t
- Assert.True (t.IsCompleted)
+ Assert.True(t.IsCompleted)
Assert.AreEqual(s, t.Result)
[]
@@ -310,7 +312,7 @@ type AsyncType() =
let a = async { return s }
let t = Async.StartImmediateAsTask a
waitForCompletion t
- Assert.True (t.IsCompleted)
+ Assert.True(t.IsCompleted)
Assert.AreEqual(s, t.Result)
@@ -325,7 +327,7 @@ type AsyncType() =
t.Wait()
with
e -> exceptionThrown <- true
- Assert.True (t.IsFaulted)
+ Assert.True(t.IsFaulted)
Assert.True(exceptionThrown)
[]
@@ -340,7 +342,7 @@ type AsyncType() =
try
t.Wait()
with e -> exceptionThrown <- true
- Assert.True (exceptionThrown)
+ Assert.True(exceptionThrown)
Assert.True(t.IsCanceled)
[]
@@ -363,7 +365,7 @@ type AsyncType() =
try
t.Wait()
with e -> exceptionThrown <- true
- Assert.True (exceptionThrown)
+ Assert.True(exceptionThrown)
Assert.True(t.IsCanceled)
Assert.True(cancelled)
@@ -375,8 +377,7 @@ type AsyncType() =
let! s1 = t |> if newAwait then Async.Await else Async.AwaitTask
return s = s1
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
[]
member _.AwaitTaskCancellation(newAwait: bool) =
@@ -388,8 +389,7 @@ type AsyncType() =
return false
with :? OperationCanceledException -> return true
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
[]
member _.AwaitCompletedTask() =
@@ -399,8 +399,7 @@ type AsyncType() =
let threadIdAfter = Thread.CurrentThread.ManagedThreadId
return threadIdBefore = threadIdAfter
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
[]
member _.AwaitTaskCancellationUntyped(newAwait: bool) =
@@ -412,8 +411,7 @@ type AsyncType() =
return false
with :? OperationCanceledException -> return true
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
[]
member _.TaskAsyncValueException(newAwait: bool) =
@@ -423,8 +421,7 @@ type AsyncType() =
return false
with e -> return true
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
[]
member _.TaskAsyncValueCancellation(newAwait: bool) =
@@ -457,7 +454,7 @@ type AsyncType() =
do! t |> if newAwait then Async.Await else Async.AwaitTask
return true
}
- let ok = Async.RunSynchronously a
+ let ok = Async.RunSynchronouslyImmediate a
Assert.True(hasBeenCalled && ok)
[]
@@ -469,8 +466,7 @@ type AsyncType() =
return false
with e -> return true
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
[]
member _.NonGenericTaskAsyncValueCancellation(newAwait: bool) =
@@ -499,7 +495,7 @@ type AsyncType() =
let cts = new CancellationTokenSource()
let token = cts.Token
let mutable hasThrown = false
- token.Register(fun () -> ewh.Set() |> ignore) |> ignore
+ token.Register(fun () -> ewh.Set()) |> ignore
let a = async {
try
while true do token.ThrowIfCancellationRequested()
@@ -519,7 +515,7 @@ type AsyncType() =
return! loop(x+1)
}
- try Async.RunSynchronously (loop 0)
+ try Async.RunSynchronously(loop 0)
hasThrown <- false
with Failure "finish" ->
hasThrown <- true
@@ -561,8 +557,7 @@ type AsyncType() =
return false
with :? AggregateException as ae -> return ae.InnerExceptions.Count = 2
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
[]
member _.``Await and AwaitTask(Task) valid AggregateException is surfaced``(newAwait) =
@@ -574,8 +569,7 @@ type AsyncType() =
return false
with :? AggregateException as ae -> return ae.InnerExceptions.Count = 2
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
(* Async.Await behavioral differences
@@ -591,8 +585,7 @@ type AsyncType() =
return false
with :? AggregateException -> return true
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
// ... whereas Async.Await(Task) surfaces the inner exception directly.
[]
@@ -604,8 +597,7 @@ type AsyncType() =
return false
with :? ArgumentException as ae -> return ae.Message = "original"
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
// Async.AwaitTask(Task<'T>) surfaces the wrapping AggregateException ...
[]
@@ -617,8 +609,7 @@ type AsyncType() =
return false
with :? AggregateException -> return true
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
// ... whereas Async.Await(Task<'T>) surfaces the inner exception directly.
[]
@@ -630,28 +621,87 @@ type AsyncType() =
return false
with :? ArgumentException as ae -> return ae.Message = "original"
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
(* Await(Task/Task<'T>) overloads happy path *)
[]
member _.``Await(Task<'T>) happy path``() =
let a = async {
- let! v = Async.Await(System.Threading.Tasks.Task.FromResult(42))
+ let! v = Async.Await(Task.FromResult(42))
return v = 42
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
[]
member _.``Await(Task) happy path``() =
let a = async {
- do! Async.Await(System.Threading.Tasks.Task.CompletedTask)
+ do! Async.Await(Task.CompletedTask)
return true
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
+
+ (* StartTaskImmediate(Task/Task<'T>) *)
+
+ []
+ member _.``StartTaskImmediate(Task<'T>) flows result``() =
+ let a = async {
+ let! v = Async.StartTaskImmediate(fun _ct -> Task.result 42)
+ return v = 42
+ }
+ Assert.True(Async.RunSynchronouslyImmediate a)
+
+ []
+ member _.``StartTaskImmediate(Task) happy path``() =
+ let mutable called = false
+ let a = async {
+ do! Async.StartTaskImmediate(fun _ct -> task { called <- true })
+ }
+ Async.RunSynchronouslyImmediate a
+ Assert.True called
+
+ []
+ member _.``StartTaskImmediate flows CancellationToken``() =
+ let cts = new CancellationTokenSource()
+ let mutable capturedCt = CancellationToken.None
+ let a = async {
+ do! Async.StartTaskImmediate(fun ct -> task { capturedCt <- ct })
+ }
+ Async.RunSynchronouslyImmediate(a, cancellationToken = cts.Token)
+ Assert.Equal(cts.Token, capturedCt)
+
+ []
+ member _.``StartTaskImmediate(Task<'T>) exception unwraps``() =
+ let tcs = TaskCompletionSource()
+ tcs.SetException(ArgumentException "original")
+ let a = async {
+ try let! _ = Async.StartTaskImmediate(fun _ct -> tcs.Task)
+ return false
+ with :? ArgumentException as ae -> return ae.Message = "original"
+ }
+ Assert.True(Async.RunSynchronouslyImmediate a)
+
+ []
+ member _.``StartTaskImmediate(Task) exception unwraps``() =
+ let tcs = TaskCompletionSource()
+ tcs.SetException(ArgumentException "original")
+ let a = async {
+ try do! Async.StartTaskImmediate(fun _ct -> tcs.Task :> Task)
+ return false
+ with :? ArgumentException as ae -> return ae.Message = "original"
+ }
+ Assert.True(Async.RunSynchronouslyImmediate a)
+
+ []
+ member _.``StartTaskImmediate(Task<'T>) cancellation raises TaskCanceledException``() =
+ let tcs = TaskCompletionSource()
+ tcs.SetCanceled()
+ let a = async {
+ try let! _ = Async.StartTaskImmediate(fun _ct -> tcs.Task)
+ return false
+ with :? TaskCanceledException -> return true
+ }
+ Assert.True(Async.RunSynchronouslyImmediate a)
#if !NETFRAMEWORK
(* Await(ValueTask and ValueTask<'T>) overloads coverage of mainline behaviors *)
@@ -662,8 +712,7 @@ type AsyncType() =
do! Async.Await(ValueTask())
return true
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True (Async.RunSynchronouslyImmediate a)
[]
member _.``Await(ValueTask<'T>) happy path``() =
@@ -671,8 +720,7 @@ type AsyncType() =
let! v = Async.Await(ValueTask(42))
return v = 42
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True (Async.RunSynchronouslyImmediate a)
[]
member _.``Await(ValueTask) exception unwraps``() =
@@ -684,8 +732,7 @@ type AsyncType() =
return false
with :? ArgumentException as ae -> return ae.Message = "original"
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
[]
member _.``Await(ValueTask<'T>) exception unwraps``() =
@@ -696,11 +743,51 @@ type AsyncType() =
return false
with :? ArgumentException as ae -> return ae.Message = "original"
}
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
+
+ (* StartTaskImmediate(ValueTask/ValueTask<'T>) *)
+
+ []
+ member _.``StartTaskImmediate(ValueTask<'T>) flows result``() =
+ let a = async {
+ let! v = Async.StartTaskImmediate(fun _ct -> ValueTask(42))
+ return v = 42
+ }
+ Assert.True(Async.RunSynchronouslyImmediate a)
+
+ []
+ member _.``StartTaskImmediate(ValueTask) happy path``() =
+ let a = async {
+ do! Async.StartTaskImmediate(fun _ct -> ValueTask())
+ return true
+ }
+ Assert.True(Async.RunSynchronouslyImmediate a)
+
+ []
+ member _.``StartTaskImmediate(ValueTask<'T>) exception unwraps``() =
+ let tcs = TaskCompletionSource()
+ tcs.SetException(ArgumentException "original")
+ let a = async {
+ try
+ let! _ = Async.StartTaskImmediate(fun _ct -> ValueTask(tcs.Task))
+ return false
+ with :? ArgumentException as ae -> return ae.Message = "original"
+ }
+ Assert.True(Async.RunSynchronouslyImmediate a)
+
+ []
+ member _.``StartTaskImmediate(ValueTask) exception unwraps``() =
+ let tcs = TaskCompletionSource()
+ tcs.SetException(ArgumentException "original")
+ let a = async {
+ try
+ do! Async.StartTaskImmediate(fun _ct -> ValueTask(tcs.Task :> Task))
+ return false
+ with :? ArgumentException as ae -> return ae.Message = "original"
+ }
+ Assert.True(Async.RunSynchronouslyImmediate a)
#endif
-[]
module AsyncTaskLikeAwaitTests =
// Minimal custom task-like type wrapping Task<'T>
@@ -713,20 +800,20 @@ module AsyncTaskLikeAwaitTests =
[]
let ``Await(task-like) happy path with result``() =
- let result =
+ let a =
async {
- let! v = Async.Await(MyTask(Task.FromResult 99))
+ let! v = Async.Await(MyTask(Task.FromResult 42))
return v
}
- |> Async.RunSynchronously
- Assert.Equal(99, result)
+ Assert.Equal(42, Async.RunSynchronouslyImmediate a)
+
[]
let ``Await(task-like) happy path unit``() =
async {
do! Async.Await(MyUnitTask(Task.CompletedTask))
}
- |> Async.RunSynchronously
+ |> Async.RunSynchronouslyImmediate
[]
let ``Await(task-like) deferred completion``() =
@@ -778,8 +865,7 @@ module AsyncTaskLikeAwaitTests =
return e.Message = "boom"
}
tcs.SetException(InvalidOperationException "boom")
- let ok = Async.RunSynchronously a
- Assert.True ok
+ Assert.True(Async.RunSynchronouslyImmediate a)
[]
let ``Await(YieldAwaitable) yields and resumes``() =
@@ -790,7 +876,7 @@ module AsyncTaskLikeAwaitTests =
do! Async.Await(Task.Yield())
after <- true
}
- |> Async.RunSynchronously
+ |> Async.RunSynchronouslyImmediate
Assert.True(before && after)
[]
@@ -801,10 +887,51 @@ module AsyncTaskLikeAwaitTests =
let! v = Async.Await(Task.FromResult(42).ConfigureAwait(false))
return v
}
- |> Async.RunSynchronously
+ |> Async.RunSynchronouslyImmediate
Assert.Equal(42, result)
-[]
+module AsyncStartTaskImmediateTaskLikeTests =
+
+ []
+ let ``StartTaskImmediate(YieldAwaitable factory) yields and resumes``() =
+ // Task.Yield() returns a struct YieldAwaitable — exercises the struct-awaiter SRTP path.
+ let mutable before, after = false, false
+
+ async {
+ before <- true
+ do! Async.StartTaskImmediate(fun _ -> Task.Yield())
+ after <- true
+ }
+ |> Async.RunSynchronouslyImmediate
+
+ Assert.True(before && after)
+
+ []
+ let ``StartTaskImmediate(ConfiguredTaskAwaitable factory) returns result``() =
+ // ConfigureAwait(false) returns a ConfiguredTaskAwaitable — a common real-world task-like.
+ let result =
+ async {
+ return! Async.StartTaskImmediate(fun _ -> Task.FromResult(42).ConfigureAwait(false))
+ }
+ |> Async.RunSynchronouslyImmediate
+
+ Assert.Equal(42, result)
+
+ []
+ let ``StartTaskImmediate flows CancellationToken``() =
+ // The factory receives the ambient cancellation token from the enclosing async.
+ let mutable capturedCt = CancellationToken.None
+ use cts = new CancellationTokenSource()
+
+ let a = async {
+ do! Async.StartTaskImmediate(fun ct ->
+ capturedCt <- ct
+ Task.CompletedTask.ConfigureAwait(false))
+ }
+ Async.RunSynchronouslyImmediate(a, cancellationToken = cts.Token)
+
+ Assert.Equal(cts.Token, capturedCt)
+
module AsyncAwaitStackTraceTests =
open System.Runtime.CompilerServices
@@ -829,9 +956,9 @@ module AsyncAwaitStackTraceTests =
// Run via StartImmediateAsTask + .Wait() and return the inner exception.
// Using StartImmediateAsTask (not RunSynchronously) ensures that the async-layer
// exception machinery goes through TaskCompletionSource.SetException, which preserves
- // the stack trace rather than rethrowing synchronously and potentially truncating it.
+ // the stack trace rather than rethrowing synchronously and/or potentially truncating it.
let runAndCaptureException (computation: Async) : exn =
- // TODO swap in usage of Async.RunSynchronouslyImmediate
+ // TODO swap in usage of Async.RunSynchronouslyImmediate and add characterization of the stack trace behavior there as well
let t = Async.StartImmediateAsTask computation
let ae = Assert.Throws(fun () -> t.Wait())
ae.InnerException
@@ -888,5 +1015,6 @@ module AsyncAwaitStackTraceTests =
let ``Await task-like via SRTP overload: all three levels visible in stack trace`` () =
let e = runAndCaptureException (async { do! Async.Await(TaskWrapper(level2Task())) })
- // 4 instead of 3 as current impl has an outer "at FSharp.Core.UnitTests.Control.AsyncAwaitStackTraceTests.e@836-9.Invoke(Tuple`3 tupledArg)
+ // 4 instead of 3 as current impl has an outer
+ // at FSharp.Core.UnitTests.Control.AsyncAwaitStackTraceTests.e@836-9.Invoke(Tuple`3 tupledArg)
checkTrace 4 e
\ No newline at end of file