From 15d9da9121ac66687d2de66350f94a6cbb85c61a Mon Sep 17 00:00:00 2001
From: gusty <1261319+gusty@users.noreply.github.com>
Date: Fri, 9 Jan 2026 12:24:20 +0100
Subject: [PATCH 1/2] Avoid F# built-in task CEs
---
src/FSharpPlus/Extensions/Task.fs | 62 +++++++++++++++++--------
src/FSharpPlus/Extensions/ValueTask.fs | 63 ++++++++++++++++++--------
2 files changed, 87 insertions(+), 38 deletions(-)
diff --git a/src/FSharpPlus/Extensions/Task.fs b/src/FSharpPlus/Extensions/Task.fs
index 36406ba22..9b1a89fbc 100644
--- a/src/FSharpPlus/Extensions/Task.fs
+++ b/src/FSharpPlus/Extensions/Task.fs
@@ -60,10 +60,15 @@ module Task =
let map (mapper: 'T -> 'U) (source: Task<'T>) : Task<'U> =
let source = nullArgCheck (nameof source) source
- backgroundTask {
- let! r = source
- return mapper r
- }
+ if source.IsCompleted then
+ match source with
+ | Succeeded r -> try result (mapper r) with e -> Task.FromException<_> e
+ | Faulted exn -> FromExceptions exn
+ | Canceled -> canceled
+ else
+ let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
+ source |> continueTask tcs (fun r -> try tcs.SetResult (mapper r) with e -> tcs.SetException e)
+ tcs.Task
/// Creates a task workflow from two workflows 'task1' and 'task2', mapping its results with 'mapper'.
/// Workflows are run in sequence.
@@ -293,19 +298,13 @@ module Task =
let join (source: Task>) : Task<'T> =
let source = nullArgCheck (nameof source) source
- backgroundTask {
- let! inner = source
- return! inner
- }
+ source.Unwrap()
/// Creates a task workflow from 'source' workflow, mapping and flattening its result with 'f'.
let bind (f: 'T -> Task<'U>) (source: Task<'T>) : Task<'U> =
let source = nullArgCheck (nameof source) source
- backgroundTask {
- let! r = source
- return! f r
- }
+ source |> Unchecked.nonNull |> map f |> join
/// Creates a task that ignores the result of the source task.
/// The source Task.
@@ -327,14 +326,41 @@ module Task =
tcs.Task
[]
- let tryWith (body: unit -> Task<'T>) (compensation: exn -> Task<'T>) : Task<'T> = backgroundTask {
- try return! body ()
- with e -> return! compensation e }
+ let rec tryWith (body: unit -> Task<'T>) (compensation: exn -> Task<'T>) : Task<'T> =
+ let runCompensation exn =
+ try compensation exn
+ with e -> Task.FromException<'T> e
+ let unwrapException (agg: AggregateException) =
+ if agg.InnerExceptions.Count = 1 then agg.InnerExceptions.[0]
+ else agg :> Exception
+ try Ok (body ()) with e -> Error e
+ |> function
+ | Ok task ->
+ if task.IsCompleted then
+ match task with
+ | Succeeded _ -> task
+ | Faulted aex -> runCompensation (unwrapException aex)
+ | Canceled -> canceled
+ else
+ task.ContinueWith(fun (x: Task<'T>) -> tryWith (fun () -> x) compensation).Unwrap ()
+ | Error exn -> runCompensation exn
[]
- let tryFinally (body: unit -> Task<'T>) (compensation : unit -> unit) : Task<'T> = backgroundTask {
- try return! body ()
- finally compensation () }
+ let rec tryFinally (body: unit -> Task<'T>) (compensation : unit -> unit) : Task<'T> =
+ let task =
+ try body ()
+ with _ ->
+ try
+ compensation ()
+ reraise ()
+ with e -> Task.FromException<'T> e
+ if task.IsCompleted then
+ try
+ compensation ()
+ task
+ with e -> Task.FromException<'T> e
+ else
+ task.ContinueWith(fun (x: Task<'T>) -> tryFinally (fun () -> x) compensation).Unwrap ()
/// Used to de-sugar use .. blocks in Computation Expressions.
let using (disp: 'T when 'T :> IDisposable) (body: 'T -> Task<'U>) =
diff --git a/src/FSharpPlus/Extensions/ValueTask.fs b/src/FSharpPlus/Extensions/ValueTask.fs
index d985dae50..289223ea0 100644
--- a/src/FSharpPlus/Extensions/ValueTask.fs
+++ b/src/FSharpPlus/Extensions/ValueTask.fs
@@ -63,10 +63,15 @@ module ValueTask =
/// The source ValueTask workflow.
/// The resulting ValueTask workflow.
let map (mapper: 'T -> 'U) (source: ValueTask<'T>) : ValueTask<'U> =
- backgroundTask {
- let! r = source
- return mapper r
- } |> ValueTask<'U>
+ if source.IsCompleted then
+ match source with
+ | Succeeded r -> try result (mapper r) with e -> ValueTask.FromException<_> e
+ | Faulted exn -> FromExceptions exn
+ | Canceled -> canceled
+ else
+ let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
+ source |> continueTask tcs (fun r -> try tcs.SetResult (mapper r) with e -> tcs.SetException e)
+ tcs.Task |> ValueTask<'U>
/// Creates a ValueTask workflow from two workflows 'x' and 'y', mapping its results with 'f'.
@@ -271,17 +276,11 @@ module ValueTask =
/// Flattens two nested ValueTask into one.
let join (source: ValueTask>) : ValueTask<'T> =
- backgroundTask {
- let! inner = source
- return! inner
- } |> ValueTask<'T>
+ (source |> map (fun x -> x.AsTask())).AsTask().Unwrap () |> ValueTask<'T>
/// Creates a ValueTask workflow from 'source' workflow, mapping and flattening its result with 'f'.
let bind (f: 'T -> ValueTask<'U>) (source: ValueTask<'T>) : ValueTask<'U> =
- backgroundTask {
- let! r = source
- return! f r
- } |> ValueTask<'U>
+ source |> map f |> join
/// Creates a ValueTask that ignores the result of the source ValueTask.
/// The source ValueTask.
@@ -302,17 +301,41 @@ module ValueTask =
/// Used to de-sugar try .. with .. blocks in Computation Expressions.
let inline tryWith ([]compensation: exn -> ValueTask<'T>) ([]body: unit -> ValueTask<'T>) : ValueTask<'T> =
- backgroundTask {
- try return! body ()
- with e -> return! compensation e
- } |> ValueTask<'T>
+ let runCompensation exn =
+ try compensation exn
+ with e -> ValueTask.FromException<'T> e
+ let unwrapException (agg: AggregateException) =
+ if agg.InnerExceptions.Count = 1 then agg.InnerExceptions.[0]
+ else agg :> Exception
+ try Ok (body ()) with e -> Error e
+ |> function
+ | Ok task ->
+ if task.IsCompleted then
+ match task with
+ | Succeeded _ -> task
+ | Faulted aex -> runCompensation (unwrapException aex)
+ | Canceled -> canceled
+ else
+ task.AsTask().ContinueWith(fun (x: Task<'T>) -> Task.tryWith (compensation >> fun x -> x.AsTask()) (fun () -> x)).Unwrap () |> ValueTask<'T>
+ | Error exn -> runCompensation exn
+
/// Used to de-sugar try .. finally .. blocks in Computation Expressions.
let inline tryFinally ([]compensation : unit -> unit) ([]body: unit -> ValueTask<'T>) : ValueTask<'T> =
- backgroundTask {
- try return! body ()
- finally compensation ()
- } |> ValueTask<'T>
+ let task =
+ try body ()
+ with _ ->
+ try
+ compensation ()
+ reraise ()
+ with e -> ValueTask.FromException<'T> e
+ if task.IsCompleted then
+ try
+ compensation ()
+ task
+ with e -> ValueTask.FromException<'T> e
+ else
+ task.AsTask().ContinueWith(fun (x: Task<'T>) -> Task.tryFinally compensation (fun () -> x)).Unwrap () |> ValueTask<'T>
/// Used to de-sugar use .. blocks in Computation Expressions.
let inline using (disp: 'T when 'T :> IDisposable) ([]body: 'T -> ValueTask<'U>) =
From 6b43f5126700009aa49193fd730fa56b6eeaa972 Mon Sep 17 00:00:00 2001
From: gusty <1261319+gusty@users.noreply.github.com>
Date: Sat, 10 Jan 2026 09:23:54 +0100
Subject: [PATCH 2/2] Reorganize code for multitargeting
---
src/FSharpPlus/Extensions/Task.fs | 107 +++++++++++++++----------
src/FSharpPlus/Extensions/ValueTask.fs | 89 ++++++++++++--------
2 files changed, 118 insertions(+), 78 deletions(-)
diff --git a/src/FSharpPlus/Extensions/Task.fs b/src/FSharpPlus/Extensions/Task.fs
index 9b1a89fbc..ccf3143d8 100644
--- a/src/FSharpPlus/Extensions/Task.fs
+++ b/src/FSharpPlus/Extensions/Task.fs
@@ -14,10 +14,11 @@ module Task =
/// Active pattern to match the state of a completed Task
let inline internal (|Succeeded|Canceled|Faulted|) (t: Task<'a>) =
- if t.IsCompletedSuccessfully then Succeeded t.Result
- elif t.IsFaulted then Faulted (Unchecked.nonNull t.Exception)
- elif t.IsCanceled then Canceled
- else invalidOp "Internal error: The task is not yet completed."
+ match t.Status with
+ | TaskStatus.RanToCompletion -> Succeeded t.Result
+ | TaskStatus.Faulted -> Faulted (Unchecked.nonNull t.Exception)
+ | TaskStatus.Canceled -> Canceled
+ | _ -> invalidOp (sprintf "Internal error: The task is not yet in a final state. State = TaskStatus.%A" t.Status)
let inline internal continueTask (tcs: TaskCompletionSource<'Result>) (k: 't -> unit) (x: Task<'t>) =
let f = function
@@ -26,32 +27,56 @@ module Task =
| Canceled -> tcs.SetCanceled ()
x.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> f x)
+ #if NET5_0_OR_GREATER
+ let [] private tcsOptions = TaskCreationOptions.RunContinuationsAsynchronously
+ #else
+ let private tcsOptions = ()
+ #endif
/// Creates a Task that's completed successfully with the specified value.
///
/// A Task that is completed successfully with the specified value.
let result (value: 'T) : Task<'T> = Task.FromResult value
-
+
+
+ /// Creates a Task that's completed unsuccessfully with the specified exception.
+ /// The exception to be raised.
+ /// A Task that is completed unsuccessfully with the specified exception.
+ let raise<'T> (exn: exn) : Task<'T> =
+ #if NET5_0_OR_GREATER
+ Task.FromException<'T> exn
+ #else
+ let tcs = TaskCompletionSource<'T> tcsOptions
+ tcs.SetException exn
+ tcs.Task
+ #endif
+
/// Creates a Task that's completed unsuccessfully with the specified exceptions.
- /// The AggregateException to be raised.
+ /// The AggregateException to be raised.
/// A Task that is completed unsuccessfully with the specified exceptions.
///
/// Prefer this function to handle AggregateExceptions over Task.FromException as it handles them correctly.
///
let inline internal FromExceptions<'T> (aex: AggregateException) : Task<'T> =
+ #if NET5_0_OR_GREATER
match aex with
| agg when agg.InnerExceptions.Count = 1 -> Task.FromException<'T> agg.InnerExceptions[0]
- | agg ->
- let tcs = TaskCompletionSource<'T> ()
- tcs.SetException agg.InnerExceptions
+ | _ ->
+ #endif
+ let tcs = TaskCompletionSource<'T> tcsOptions
+ tcs.SetException aex.InnerExceptions
tcs.Task
- let private cancellationTokenSingleton = CancellationToken true
-
/// Creates a Task that's canceled.
/// A Task that's canceled.
- let canceled<'T> : Task<'T> = Task.FromCanceled<'T> cancellationTokenSingleton
-
+ let canceled<'T> : Task<'T> =
+ #if NET5_0_OR_GREATER
+ Task.FromCanceled<'T> (CancellationToken true)
+ #else
+ let tcs = TaskCompletionSource<'T> tcsOptions
+ tcs.SetCanceled ()
+ tcs.Task
+ #endif
/// Creates a task workflow from 'source' workflow, mapping its result with 'mapper'.
/// The mapping function.
@@ -62,11 +87,11 @@ module Task =
if source.IsCompleted then
match source with
- | Succeeded r -> try result (mapper r) with e -> Task.FromException<_> e
+ | Succeeded r -> try result (mapper r) with e -> raise e
| Faulted exn -> FromExceptions exn
| Canceled -> canceled
else
- let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
+ let tcs = TaskCompletionSource<'U> tcsOptions
source |> continueTask tcs (fun r -> try tcs.SetResult (mapper r) with e -> tcs.SetException e)
tcs.Task
@@ -81,13 +106,13 @@ module Task =
if task1.IsCompleted && task2.IsCompleted then
match task1, task2 with
- | Succeeded r1, Succeeded r2 -> try result (mapper r1 r2) with e -> Task.FromException<_> e
+ | Succeeded r1, Succeeded r2 -> try result (mapper r1 r2) with e -> raise e
| Succeeded _ , Faulted exn -> FromExceptions exn
| Succeeded _ , Canceled -> canceled
| Faulted exn , _ -> FromExceptions exn
| Canceled , _ -> canceled
else
- let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
+ let tcs = TaskCompletionSource<'U> tcsOptions
match task1.Status, task2.Status with
| TaskStatus.Canceled, _ -> tcs.SetCanceled ()
@@ -112,7 +137,7 @@ module Task =
if task1.IsCompleted && task2.IsCompleted && task3.IsCompleted then
match task1, task2, task3 with
- | Succeeded r1, Succeeded r2, Succeeded r3 -> try result (mapper r1 r2 r3) with e -> Task.FromException<_> e
+ | Succeeded r1, Succeeded r2, Succeeded r3 -> try result (mapper r1 r2 r3) with e -> raise e
| Faulted exn , _ , _ -> FromExceptions exn
| Canceled , _ , _ -> canceled
| _ , Faulted exn , _ -> FromExceptions exn
@@ -120,7 +145,7 @@ module Task =
| _ , _ , Faulted exn -> FromExceptions exn
| _ , _ , Canceled -> canceled
else
- let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
+ let tcs = TaskCompletionSource<'U> tcsOptions
match task1.Status, task2.Status, task3.Status with
| TaskStatus.Canceled, _ , _ -> tcs.SetCanceled ()
| TaskStatus.Faulted , _ , _ -> tcs.SetException (Unchecked.nonNull task1.Exception).InnerExceptions
@@ -147,9 +172,9 @@ module Task =
let task2 = nullArgCheck (nameof task2) task2
if task1.Status = TaskStatus.RanToCompletion && task2.Status = TaskStatus.RanToCompletion then
- try result (mapper task1.Result task2.Result) with e -> Task.FromException<'U> e
+ try result (mapper task1.Result task2.Result) with e -> raise e
else
- let tcs = TaskCompletionSource<_> ()
+ let tcs = TaskCompletionSource<_> tcsOptions
let r1 = ref Unchecked.defaultof<_>
let r2 = ref Unchecked.defaultof<_>
let mutable cancelled = false
@@ -195,9 +220,9 @@ module Task =
if task1.Status = TaskStatus.RanToCompletion && task2.Status = TaskStatus.RanToCompletion && task3.Status = TaskStatus.RanToCompletion then
try result (mapper task1.Result task2.Result task3.Result)
- with e -> Task.FromException<'U> e
+ with e -> raise e
else
- let tcs = TaskCompletionSource<_> ()
+ let tcs = TaskCompletionSource<_> tcsOptions
let r1 = ref Unchecked.defaultof<_>
let r2 = ref Unchecked.defaultof<_>
let r3 = ref Unchecked.defaultof<_>
@@ -241,13 +266,13 @@ module Task =
if f.IsCompleted && x.IsCompleted then
match f, x with
- | Succeeded r1, Succeeded r2 -> try result (r1 r2) with e -> Task.FromException<_> e
+ | Succeeded r1, Succeeded r2 -> try result (r1 r2) with e -> raise e
| Succeeded _ , Faulted exn -> FromExceptions exn
| Succeeded _ , Canceled -> canceled
| Faulted exn , _ -> FromExceptions exn
| Canceled , _ -> canceled
else
- let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
+ let tcs = TaskCompletionSource<'U> tcsOptions
match f.Status, x.Status with
| TaskStatus.Canceled, _ -> tcs.SetCanceled ()
| TaskStatus.Faulted, _ -> tcs.SetException (Unchecked.nonNull f.Exception).InnerExceptions
@@ -271,7 +296,7 @@ module Task =
| Faulted exn , _ -> FromExceptions exn
| Canceled , _ -> canceled
else
- let tcs = TaskCompletionSource<'T1 * 'T2> ()
+ let tcs = TaskCompletionSource<'T1 * 'T2> tcsOptions
match task1.Status, task2.Status with
| TaskStatus.Canceled, _ -> tcs.SetCanceled ()
| TaskStatus.Faulted, _ -> tcs.SetException (Unchecked.nonNull task1.Exception).InnerExceptions
@@ -313,11 +338,12 @@ module Task =
let ignore (source: Task) =
let source = nullArgCheck (nameof source) source
- if source.IsCompletedSuccessfully then result ()
- elif source.IsFaulted then FromExceptions (Unchecked.nonNull source.Exception)
- elif source.IsCanceled then canceled
- else
- let tcs = TaskCompletionSource ()
+ match source.Status with
+ | TaskStatus.RanToCompletion -> result ()
+ | TaskStatus.Faulted -> FromExceptions (Unchecked.nonNull source.Exception)
+ | TaskStatus.Canceled -> canceled
+ | _ ->
+ let tcs = TaskCompletionSource tcsOptions
let k (t: Task) : unit =
if t.IsCanceled then tcs.SetCanceled ()
elif t.IsFaulted then tcs.SetException (Unchecked.nonNull source.Exception).InnerExceptions
@@ -329,7 +355,7 @@ module Task =
let rec tryWith (body: unit -> Task<'T>) (compensation: exn -> Task<'T>) : Task<'T> =
let runCompensation exn =
try compensation exn
- with e -> Task.FromException<'T> e
+ with e -> raise e
let unwrapException (agg: AggregateException) =
if agg.InnerExceptions.Count = 1 then agg.InnerExceptions.[0]
else agg :> Exception
@@ -353,12 +379,12 @@ module Task =
try
compensation ()
reraise ()
- with e -> Task.FromException<'T> e
+ with e -> raise e
if task.IsCompleted then
try
compensation ()
task
- with e -> Task.FromException<'T> e
+ with e -> raise e
else
task.ContinueWith(fun (x: Task<'T>) -> tryFinally (fun () -> x) compensation).Unwrap ()
@@ -401,7 +427,7 @@ module Task =
let inline recover ([]mapper: exn -> 'T) (source: Task<'T>) : Task<'T> =
let source = nullArgCheck (nameof source) source
- tryWith (fun () -> source) (mapper >> Task.FromResult)
+ tryWith (fun () -> source) (mapper >> result)
/// Maps the exception of a faulted task to another exception.
/// Mapping function from exception to exception.
@@ -415,7 +441,7 @@ module Task =
| Faulted exn -> FromExceptions (AggregateException (mapper exn))
| _ -> source
else
- let tcs = TaskCompletionSource<'T> TaskCreationOptions.RunContinuationsAsynchronously
+ let tcs = TaskCompletionSource<'T> tcsOptions
let k = function
| Succeeded r -> tcs.SetResult r
| Faulted aex -> tcs.SetException (AggregateException (mapper aex)).InnerExceptions
@@ -430,13 +456,8 @@ module Task =
/// The resulting Task.
let ofResult (source: Result<'T, exn>) : Task<'T> =
match source with
- | Ok x -> Task.FromResult x
- | Error exn -> Task.FromException<'T> exn
-
- /// Creates a Task that's completed unsuccessfully with the specified exception.
- /// The exception to be raised.
- /// A Task that is completed unsuccessfully with the specified exception.
- let raise<'T> (exn: exn) : Task<'T> = Task.FromException<'T> exn
+ | Ok x -> result x
+ | Error exn -> raise exn
/// Workaround to fix signatures without breaking binary compatibility.
diff --git a/src/FSharpPlus/Extensions/ValueTask.fs b/src/FSharpPlus/Extensions/ValueTask.fs
index 289223ea0..b23117d1c 100644
--- a/src/FSharpPlus/Extensions/ValueTask.fs
+++ b/src/FSharpPlus/Extensions/ValueTask.fs
@@ -16,7 +16,7 @@ module ValueTask =
if t.IsCompletedSuccessfully then Succeeded t.Result
elif t.IsFaulted then Faulted (Unchecked.nonNull (t.AsTask().Exception))
elif t.IsCanceled then Canceled
- else invalidOp "Internal error: The task is not yet completed."
+ else invalidOp "Internal error: The task is not yet in a final state."
let inline internal continueTask (tcs: TaskCompletionSource<'Result>) (k: 't -> unit) (x: ValueTask<'t>) =
let f = function
@@ -25,6 +25,11 @@ module ValueTask =
| Canceled -> tcs.SetCanceled ()
x.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> f x)
+ #if NET5_0_OR_GREATER
+ let [] private tcsOptions = TaskCreationOptions.RunContinuationsAsynchronously
+ #else
+ let private tcsOptions = ()
+ #endif
/// Creates a ValueTask that's completed successfully with the specified value.
///
@@ -33,30 +38,49 @@ module ValueTask =
#if NET5_0_OR_GREATER
ValueTask.FromResult value
#else
- let tcs = TaskCompletionSource<'T> ()
+ let tcs = TaskCompletionSource<'T> tcsOptions
tcs.SetResult value
ValueTask<'T> tcs.Task
#endif
+
+ /// Creates a ValueTask that's completed unsuccessfully with the specified exception.
+ /// The exception to be raised.
+ /// A ValueTask that is completed unsuccessfully with the specified exception.
+ let raise<'T> (exn: exn) : ValueTask<'T> =
+ #if NET5_0_OR_GREATER
+ ValueTask.FromException<'T> exn
+ #else
+ let tcs = TaskCompletionSource<'T> tcsOptions
+ tcs.SetException exn
+ ValueTask<'T> tcs.Task
+ #endif
/// Creates a Task that's completed unsuccessfully with the specified exceptions.
- /// The AggregateException to be raised.
+ /// The AggregateException to be raised.
/// A Task that is completed unsuccessfully with the specified exceptions.
///
/// Prefer this function to handle AggregateExceptions over Task.FromException as it handles them correctly.
///
let inline internal FromExceptions<'T> (aex: AggregateException) : ValueTask<'T> =
+ #if NET5_0_OR_GREATER
match aex with
| agg when agg.InnerExceptions.Count = 1 -> ValueTask.FromException<'T> agg.InnerExceptions[0]
- | agg ->
- let tcs = TaskCompletionSource<'T> ()
- tcs.SetException agg.InnerExceptions
+ | _ ->
+ #endif
+ let tcs = TaskCompletionSource<'T> tcsOptions
+ tcs.SetException aex.InnerExceptions
ValueTask<'T> tcs.Task
- let private cancellationTokenSingleton = CancellationToken true
-
/// Creates a ValueTask that's canceled.
/// A ValueTask that's canceled.
- let canceled<'T> : ValueTask<'T> = ValueTask.FromCanceled<'T> cancellationTokenSingleton
+ let canceled<'T> : ValueTask<'T> =
+ #if NET5_0_OR_GREATER
+ ValueTask.FromCanceled<'T> (CancellationToken true)
+ #else
+ let tcs = TaskCompletionSource<'T> tcsOptions
+ tcs.SetCanceled ()
+ ValueTask<'T> tcs.Task
+ #endif
/// Creates a ValueTask workflow from 'source' workflow, mapping its result with 'mapper'.
/// The mapping function.
@@ -65,11 +89,11 @@ module ValueTask =
let map (mapper: 'T -> 'U) (source: ValueTask<'T>) : ValueTask<'U> =
if source.IsCompleted then
match source with
- | Succeeded r -> try result (mapper r) with e -> ValueTask.FromException<_> e
+ | Succeeded r -> try result (mapper r) with e -> raise e
| Faulted exn -> FromExceptions exn
| Canceled -> canceled
else
- let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
+ let tcs = TaskCompletionSource<'U> tcsOptions
source |> continueTask tcs (fun r -> try tcs.SetResult (mapper r) with e -> tcs.SetException e)
tcs.Task |> ValueTask<'U>
@@ -82,13 +106,13 @@ module ValueTask =
let lift2 (mapper: 'T1 -> 'T2 -> 'U) (task1: ValueTask<'T1>) (task2: ValueTask<'T2>) : ValueTask<'U> =
if task1.IsCompleted && task2.IsCompleted then
match task1, task2 with
- | Succeeded r1, Succeeded r2 -> try result (mapper r1 r2) with e -> ValueTask.FromException<_> e
+ | Succeeded r1, Succeeded r2 -> try result (mapper r1 r2) with e -> raise e
| Succeeded _ , Faulted exn -> FromExceptions exn
| Succeeded _ , Canceled -> canceled
| Faulted exn , _ -> FromExceptions exn
| Canceled , _ -> canceled
else
- let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
+ let tcs = TaskCompletionSource<'U> tcsOptions
if task1.IsCanceled then tcs.SetCanceled ()
elif task1.IsFaulted then tcs.SetException (Unchecked.nonNull (task1.AsTask().Exception)).InnerExceptions
elif task2.IsCanceled then tcs.SetCanceled ()
@@ -107,7 +131,7 @@ module ValueTask =
let lift3 (mapper: 'T1 -> 'T2 -> 'T3 -> 'U) (task1: ValueTask<'T1>) (task2: ValueTask<'T2>) (task3: ValueTask<'T3>) : ValueTask<'U> =
if task1.IsCompleted && task2.IsCompleted && task3.IsCompleted then
match task1, task2, task3 with
- | Succeeded r1, Succeeded r2, Succeeded r3 -> try result (mapper r1 r2 r3) with e -> ValueTask.FromException<_> e
+ | Succeeded r1, Succeeded r2, Succeeded r3 -> try result (mapper r1 r2 r3) with e -> raise e
| Faulted exn , _ , _ -> FromExceptions exn
| Canceled , _ , _ -> canceled
| _ , Faulted exn , _ -> FromExceptions exn
@@ -115,7 +139,7 @@ module ValueTask =
| _ , _ , Faulted exn -> FromExceptions exn
| _ , _ , Canceled -> canceled
else
- let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
+ let tcs = TaskCompletionSource<'U> tcsOptions
if task1.IsCanceled then tcs.SetCanceled ()
elif task1.IsFaulted then tcs.SetException (Unchecked.nonNull (task1.AsTask().Exception)).InnerExceptions
elif task2.IsCanceled then tcs.SetCanceled ()
@@ -139,9 +163,9 @@ module ValueTask =
let map2 mapper (task1: ValueTask<'T1>) (task2: ValueTask<'T2>) : ValueTask<'U> =
if task1.IsCompletedSuccessfully && task2.IsCompletedSuccessfully then
try result (mapper task1.Result task2.Result)
- with e -> ValueTask.FromException<'U> e
+ with e -> raise e
else
- let tcs = TaskCompletionSource<_> ()
+ let tcs = TaskCompletionSource<_> tcsOptions
let r1 = ref Unchecked.defaultof<_>
let r2 = ref Unchecked.defaultof<_>
let mutable cancelled = false
@@ -183,9 +207,9 @@ module ValueTask =
let map3 mapper (task1: ValueTask<'T1>) (task2: ValueTask<'T2>) (task3: ValueTask<'T3>) : ValueTask<'U> =
if task1.IsCompletedSuccessfully && task2.IsCompletedSuccessfully && task3.IsCompletedSuccessfully then
try result (mapper task1.Result task2.Result task3.Result)
- with e -> ValueTask.FromException<'U> e
+ with e -> raise e
else
- let tcs = TaskCompletionSource<_> ()
+ let tcs = TaskCompletionSource<_> tcsOptions
let r1 = ref Unchecked.defaultof<_>
let r2 = ref Unchecked.defaultof<_>
let r3 = ref Unchecked.defaultof<_>
@@ -226,13 +250,13 @@ module ValueTask =
let apply (f: ValueTask<'T -> 'U>) (x: ValueTask<'T>) : ValueTask<'U> =
if f.IsCompleted && x.IsCompleted then
match f, x with
- | Succeeded r1, Succeeded r2 -> try result (r1 r2) with e -> ValueTask.FromException<_> e
+ | Succeeded r1, Succeeded r2 -> try result (r1 r2) with e -> raise e
| Succeeded _ , Faulted exn -> FromExceptions exn
| Succeeded _ , Canceled -> canceled
| Faulted exn , _ -> FromExceptions exn
| Canceled , _ -> canceled
else
- let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
+ let tcs = TaskCompletionSource<'U> tcsOptions
if f.IsCanceled then tcs.SetCanceled ()
elif f.IsFaulted then tcs.SetException (Unchecked.nonNull (f.AsTask().Exception)).InnerExceptions
elif x.IsCanceled then tcs.SetCanceled ()
@@ -252,7 +276,7 @@ module ValueTask =
| Faulted exn , _ -> FromExceptions exn
| Canceled , _ -> canceled
else
- let tcs = TaskCompletionSource<'T1 * 'T2> ()
+ let tcs = TaskCompletionSource<'T1 * 'T2> tcsOptions
if task1.IsCanceled then tcs.SetCanceled ()
elif task1.IsFaulted then tcs.SetException (Unchecked.nonNull (task1.AsTask().Exception)).InnerExceptions
elif task2.IsCanceled then tcs.SetCanceled ()
@@ -291,7 +315,7 @@ module ValueTask =
elif source.IsFaulted then FromExceptions (Unchecked.nonNull (source.AsTask().Exception))
elif source.IsCanceled then canceled
else
- let tcs = TaskCompletionSource ()
+ let tcs = TaskCompletionSource tcsOptions
let k (t: ValueTask) : unit =
if t.IsCanceled then tcs.SetCanceled ()
elif t.IsFaulted then tcs.SetException (Unchecked.nonNull (source.AsTask().Exception)).InnerExceptions
@@ -303,7 +327,7 @@ module ValueTask =
let inline tryWith ([]compensation: exn -> ValueTask<'T>) ([]body: unit -> ValueTask<'T>) : ValueTask<'T> =
let runCompensation exn =
try compensation exn
- with e -> ValueTask.FromException<'T> e
+ with e -> raise e
let unwrapException (agg: AggregateException) =
if agg.InnerExceptions.Count = 1 then agg.InnerExceptions.[0]
else agg :> Exception
@@ -328,12 +352,12 @@ module ValueTask =
try
compensation ()
reraise ()
- with e -> ValueTask.FromException<'T> e
+ with e -> raise e
if task.IsCompleted then
try
compensation ()
task
- with e -> ValueTask.FromException<'T> e
+ with e -> raise e
else
task.AsTask().ContinueWith(fun (x: Task<'T>) -> Task.tryFinally compensation (fun () -> x)).Unwrap () |> ValueTask<'T>
@@ -367,7 +391,7 @@ module ValueTask =
/// A successful resulting task.
/// The result is always a successful task, unless the mapping function itself throws an exception.
let inline recover ([]mapper: exn -> 'T) (source: ValueTask<'T>) : ValueTask<'T> =
- tryWith (mapper >> ValueTask.FromResult) (fun () -> source)
+ tryWith (mapper >> result) (fun () -> source)
/// Maps the exception of a faulted task to another exception.
/// Mapping function from exception to exception.
@@ -379,7 +403,7 @@ module ValueTask =
| Faulted exn -> FromExceptions (AggregateException (mapper exn))
| _ -> source
else
- let tcs = TaskCompletionSource<'T> TaskCreationOptions.RunContinuationsAsynchronously
+ let tcs = TaskCompletionSource<'T> tcsOptions
let k = function
| Succeeded r -> tcs.SetResult r
| Faulted aex -> tcs.SetException (AggregateException (mapper aex)).InnerExceptions
@@ -394,12 +418,7 @@ module ValueTask =
/// The resulting Task.
let ofResult (source: Result<'T, exn>) : ValueTask<'T> =
match source with
- | Ok x -> ValueTask.FromResult x
- | Error exn -> ValueTask.FromException<'T> exn
-
- /// Creates a ValueTask that's completed unsuccessfully with the specified exception.
- /// The exception to be raised.
- /// A ValueTask that is completed unsuccessfully with the specified exception.
- let raise<'T> (exn: exn) : ValueTask<'T> = ValueTask.FromException<'T> exn
+ | Ok x -> result x
+ | Error exn -> raise exn
#endif