@@ -15,10 +15,11 @@ module Task =
1515
1616 /// Active pattern to match the state of a completed Task
1717 let inline internal (| Succeeded | Canceled | Faulted |) ( t : Task < 'a >) =
18- if t.IsFaulted then Faulted ( Unchecked.nonNull ( t.Exception))
19- elif t.IsCanceled then Canceled
20- elif t.IsCompleted then Succeeded t.Result
21- else invalidOp " Internal error: The task is not yet completed."
18+ match t.Status with
19+ | TaskStatus.RanToCompletion -> Succeeded t.Result
20+ | TaskStatus.Faulted -> Faulted ( Unchecked.nonNull t.Exception)
21+ | TaskStatus.Canceled -> Canceled
22+ | _ -> invalidOp ( sprintf " Internal error: The task is not yet in a final state. State = TaskStatus.%A " t.Status)
2223
2324 let inline internal continueTask ( tcs : TaskCompletionSource < 'Result >) ( k : 't -> unit ) ( x : Task < 't >) =
2425 let f = function
@@ -27,32 +28,56 @@ module Task =
2728 | Canceled -> tcs.SetCanceled ()
2829 x.ConfigureAwait( false ) .GetAwaiter() .UnsafeOnCompleted ( fun () -> f x)
2930
31+ #if NET5_ 0_ OR_ GREATER
32+ let [<Literal>] private tcsOptions = TaskCreationOptions.RunContinuationsAsynchronously
33+ #else
34+ let private tcsOptions = ()
35+ #endif
3036
3137 /// <summary >Creates a Task that's completed successfully with the specified value.</summary >
3238 /// <param name =" value " ></param >
3339 /// <returns >A Task that is completed successfully with the specified value.</returns >
3440 let result ( value : 'T ) : Task < 'T > = Task.FromResult value
35-
41+
42+
43+ /// <summary >Creates a Task that's completed unsuccessfully with the specified exception.</summary >
44+ /// <param name =" exn " >The exception to be raised.</param >
45+ /// <returns >A Task that is completed unsuccessfully with the specified exception.</returns >
46+ let raise < 'T > ( exn : exn ) : Task < 'T > =
47+ #if NET5_ 0_ OR_ GREATER
48+ Task.FromException< 'T> exn
49+ #else
50+ let tcs = TaskCompletionSource< 'T> tcsOptions
51+ tcs.SetException exn
52+ tcs.Task
53+ #endif
54+
3655 /// <summary >Creates a Task that's completed unsuccessfully with the specified exceptions.</summary >
37- /// <param name =" exn " >The AggregateException to be raised.</param >
56+ /// <param name =" aex " >The AggregateException to be raised.</param >
3857 /// <returns >A Task that is completed unsuccessfully with the specified exceptions.</returns >
3958 /// <remarks >
4059 /// Prefer this function to handle AggregateExceptions over Task.FromException as it handles them correctly.
4160 /// </remarks >
42- let internal FromExceptions < 'T > ( aex : AggregateException ) : Task < 'T > =
61+ let inline internal FromExceptions < 'T > ( aex : AggregateException ) : Task < 'T > =
62+ #if NET5_ 0_ OR_ GREATER
4363 match aex with
4464 | agg when agg.InnerExceptions.Count = 1 -> Task.FromException< 'T> agg.InnerExceptions[ 0 ]
45- | agg ->
46- let tcs = TaskCompletionSource< 'T> ()
47- tcs.SetException agg.InnerExceptions
65+ | _ ->
66+ #endif
67+ let tcs = TaskCompletionSource< 'T> tcsOptions
68+ tcs.SetException aex.InnerExceptions
4869 tcs.Task
4970
50- let private cancellationTokenSingleton = CancellationToken true
51-
5271 /// <summary >Creates a Task that's canceled.</summary >
5372 /// <returns >A Task that's canceled.</returns >
54- let canceled < 'T > : Task < 'T > = Task.FromCanceled< 'T> cancellationTokenSingleton
55-
73+ let canceled < 'T > : Task < 'T > =
74+ #if NET5_ 0_ OR_ GREATER
75+ Task.FromCanceled< 'T> ( CancellationToken true )
76+ #else
77+ let tcs = TaskCompletionSource< 'T> tcsOptions
78+ tcs.SetCanceled ()
79+ tcs.Task
80+ #endif
5681
5782 /// <summary >Creates a task workflow from 'source' workflow, mapping its result with 'mapper'.</summary >
5883 /// <param name =" mapper " >The mapping function.</param >
@@ -67,11 +92,11 @@ module Task =
6792
6893 if source.IsCompleted then
6994 match source with
70- | Succeeded r -> try result ( mapper r) with e -> Task.FromException <_> e
95+ | Succeeded r -> try result ( mapper r) with e -> raise e
7196 | Faulted exn -> FromExceptions exn
7297 | Canceled -> canceled
7398 else
74- let tcs = TaskCompletionSource< 'U> TaskCreationOptions.RunContinuationsAsynchronously
99+ let tcs = TaskCompletionSource< 'U> tcsOptions
75100 source |> continueTask tcs ( fun r -> try tcs.SetResult ( mapper r) with e -> tcs.SetException e)
76101 tcs.Task
77102
@@ -91,13 +116,13 @@ module Task =
91116
92117 if task1.IsCompleted && task2.IsCompleted then
93118 match task1, task2 with
94- | Succeeded r1, Succeeded r2 -> try result ( mapper r1 r2) with e -> Task.FromException <_> e
119+ | Succeeded r1, Succeeded r2 -> try result ( mapper r1 r2) with e -> raise e
95120 | Succeeded _ , Faulted exn -> FromExceptions exn
96121 | Succeeded _ , Canceled -> canceled
97122 | Faulted exn , _ -> FromExceptions exn
98123 | Canceled , _ -> canceled
99124 else
100- let tcs = TaskCompletionSource< 'U> ()
125+ let tcs = TaskCompletionSource< 'U> tcsOptions
101126
102127 match task1.Status, task2.Status with
103128 | TaskStatus.Canceled, _ -> tcs.SetCanceled ()
@@ -128,15 +153,15 @@ module Task =
128153
129154 if task1.IsCompleted && task2.IsCompleted && task3.IsCompleted then
130155 match task1, task2, task3 with
131- | Succeeded r1, Succeeded r2, Succeeded r3 -> try result ( mapper r1 r2 r3) with e -> Task.FromException <_> e
156+ | Succeeded r1, Succeeded r2, Succeeded r3 -> try result ( mapper r1 r2 r3) with e -> raise e
132157 | Faulted exn , _ , _ -> FromExceptions exn
133158 | Canceled , _ , _ -> canceled
134159 | _ , Faulted exn , _ -> FromExceptions exn
135160 | _ , Canceled , _ -> canceled
136161 | _ , _ , Faulted exn -> FromExceptions exn
137162 | _ , _ , Canceled -> canceled
138163 else
139- let tcs = TaskCompletionSource< 'U> ()
164+ let tcs = TaskCompletionSource< 'U> tcsOptions
140165 match task1.Status, task2.Status, task3.Status with
141166 | TaskStatus.Canceled, _ , _ -> tcs.SetCanceled ()
142167 | TaskStatus.Faulted , _ , _ -> tcs.SetException ( Unchecked.nonNull task1.Exception) .InnerExceptions
@@ -168,9 +193,9 @@ module Task =
168193 #endif
169194
170195 if task1.Status = TaskStatus.RanToCompletion && task2.Status = TaskStatus.RanToCompletion then
171- try result ( mapper task1.Result task2.Result) with e -> Task.FromException < 'U > e
196+ try result ( mapper task1.Result task2.Result) with e -> raise e
172197 else
173- let tcs = TaskCompletionSource<_> ()
198+ let tcs = TaskCompletionSource<_> tcsOptions
174199 let r1 = ref Unchecked.defaultof<_>
175200 let r2 = ref Unchecked.defaultof<_>
176201 let mutable cancelled = false
@@ -222,9 +247,9 @@ module Task =
222247
223248 if task1.Status = TaskStatus.RanToCompletion && task2.Status = TaskStatus.RanToCompletion && task3.Status = TaskStatus.RanToCompletion then
224249 try result ( mapper task1.Result task2.Result task3.Result)
225- with e -> Task.FromException < 'U > e
250+ with e -> raise e
226251 else
227- let tcs = TaskCompletionSource<_> ()
252+ let tcs = TaskCompletionSource<_> tcsOptions
228253 let r1 = ref Unchecked.defaultof<_>
229254 let r2 = ref Unchecked.defaultof<_>
230255 let r3 = ref Unchecked.defaultof<_>
@@ -273,13 +298,13 @@ module Task =
273298
274299 if f.IsCompleted && x.IsCompleted then
275300 match f, x with
276- | Succeeded r1, Succeeded r2 -> try result ( r1 r2) with e -> Task.FromException <_> e
301+ | Succeeded r1, Succeeded r2 -> try result ( r1 r2) with e -> raise e
277302 | Succeeded _ , Faulted exn -> FromExceptions exn
278303 | Succeeded _ , Canceled -> canceled
279304 | Faulted exn , _ -> FromExceptions exn
280305 | Canceled , _ -> canceled
281306 else
282- let tcs = TaskCompletionSource< 'U> ()
307+ let tcs = TaskCompletionSource< 'U> tcsOptions
283308 match f.Status, x.Status with
284309 | TaskStatus.Canceled, _ -> tcs.SetCanceled ()
285310 | TaskStatus.Faulted, _ -> tcs.SetException ( Unchecked.nonNull f.Exception) .InnerExceptions
@@ -307,7 +332,7 @@ module Task =
307332 | Faulted exn , _ -> FromExceptions exn
308333 | Canceled , _ -> canceled
309334 else
310- let tcs = TaskCompletionSource< 'T1 * 'T2> ()
335+ let tcs = TaskCompletionSource< 'T1 * 'T2> tcsOptions
311336 match task1.Status, task2.Status with
312337 | TaskStatus.Canceled, _ -> tcs.SetCanceled ()
313338 | TaskStatus.Faulted, _ -> tcs.SetException ( Unchecked.nonNull task1.Exception) .InnerExceptions
@@ -357,11 +382,12 @@ module Task =
357382 raiseIfNull " source" source
358383 #endif
359384
360- if source.IsCompletedSuccessfully then result ()
361- elif source.IsFaulted then FromExceptions ( Unchecked.nonNull source.Exception)
362- elif source.IsCanceled then canceled
363- else
364- let tcs = TaskCompletionSource< unit> ()
385+ match source.Status with
386+ | TaskStatus.RanToCompletion -> result ()
387+ | TaskStatus.Faulted -> FromExceptions ( Unchecked.nonNull source.Exception)
388+ | TaskStatus.Canceled -> canceled
389+ | _ ->
390+ let tcs = TaskCompletionSource< unit> tcsOptions
365391 let k ( t : Task ) : unit =
366392 if t.IsCanceled then tcs.SetCanceled ()
367393 elif t.IsFaulted then tcs.SetException ( Unchecked.nonNull source.Exception) .InnerExceptions
@@ -373,7 +399,7 @@ module Task =
373399 let rec tryWith ( body : unit -> Task < 'T >) ( compensation : exn -> Task < 'T >) : Task < 'T > =
374400 let runCompensation exn =
375401 try compensation exn
376- with e -> Task.FromException < 'T > e
402+ with e -> raise e
377403 let unwrapException ( agg : AggregateException ) =
378404 if agg.InnerExceptions.Count = 1 then agg.InnerExceptions.[ 0 ]
379405 else agg :> Exception
@@ -397,12 +423,12 @@ module Task =
397423 try
398424 compensation ()
399425 reraise ()
400- with e -> Task.FromException < 'T > e
426+ with e -> raise e
401427 if task.IsCompleted then
402428 try
403429 compensation ()
404430 task
405- with e -> Task.FromException < 'T > e
431+ with e -> raise e
406432 else
407433 task.ContinueWith( fun ( x : Task < 'T >) -> tryFinally ( fun () -> x) compensation) .Unwrap ()
408434
@@ -447,11 +473,46 @@ module Task =
447473 raiseIfNull " source" source
448474 #endif
449475 orElseWith ( fun _ -> fallbackTask) source
476+
477+ /// <summary >Attempts to recover from a potentially failed task by mapping the exception to a successful result.</summary >
478+ /// <param name =" mapper " >Mapping function from exception to result.</param >
479+ /// <param name =" source " >The source task.</param >
480+ /// <returns >A successful resulting task.</returns >
481+ /// <remarks >The result is always a successful task, unless the mapping function itself throws an exception.</remarks >
482+ let inline recover ( [<InlineIfLambda>] mapper : exn -> 'T ) ( source : Task < 'T >) : Task < 'T > =
483+ let source = nullArgCheck ( nameof source) source
450484
451- /// <summary >Creates a Task that's completed unsuccessfully with the specified exception.</summary >
452- /// <param name =" exn " >The exception to be raised.</param >
453- /// <returns >A Task that is completed unsuccessfully with the specified exception.</returns >
454- let raise < 'T > ( exn : exn ) : Task < 'T > = Task.FromException< 'T> exn
485+ tryWith ( fun () -> source) ( mapper >> result)
486+
487+ /// <summary >Maps the exception of a faulted task to another exception.</summary >
488+ /// <param name =" mapper " >Mapping function from exception to exception.</param >
489+ /// <param name =" source " >The source task.</param >
490+ /// <returns >The resulting task.</returns >
491+ let inline mapError ( [<InlineIfLambda>] mapper : exn -> exn ) ( source : Task < 'T >) : Task < 'T > =
492+ let source = nullArgCheck ( nameof source) source
493+
494+ if source.IsCompleted then
495+ match source with
496+ | Faulted exn -> FromExceptions ( AggregateException ( mapper exn))
497+ | _ -> source
498+ else
499+ let tcs = TaskCompletionSource< 'T> tcsOptions
500+ let k = function
501+ | Succeeded r -> tcs.SetResult r
502+ | Faulted aex -> tcs.SetException ( AggregateException ( mapper aex)) .InnerExceptions
503+ | Canceled -> tcs.SetCanceled ()
504+ source.ConfigureAwait( false ) .GetAwaiter() .UnsafeOnCompleted ( fun () -> k source)
505+ tcs.Task
506+
507+ /// Creates a Task from a Result value.
508+ /// If the Result is Ok, the Task will complete successfully with the value.
509+ /// If the Result is Error, the Task will complete unsuccessfully with the exception.
510+ /// <param name =" source " >The source Result.</param >
511+ /// <returns >The resulting Task.</returns >
512+ let ofResult ( source : Result < 'T , exn >) : Task < 'T > =
513+ match source with
514+ | Ok x -> result x
515+ | Error exn -> raise exn
455516
456517
457518/// Workaround to fix signatures without breaking binary compatibility.
0 commit comments