@@ -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 $" 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,57 @@ module Task =
2728 | Canceled -> tcs.SetCanceled ()
2829 x.ConfigureAwait( false ) .GetAwaiter() .UnsafeOnCompleted ( fun () -> f x)
2930
31+ let [<Literal>] private tcsOptions =
32+ #if NET5_ 0_ OR_ GREATER
33+ TaskCreationOptions.RunContinuationsAsynchronously
34+ #else
35+ ()
36+ #endif
3037
3138 /// <summary >Creates a Task that's completed successfully with the specified value.</summary >
3239 /// <param name =" value " ></param >
3340 /// <returns >A Task that is completed successfully with the specified value.</returns >
3441 let result ( value : 'T ) : Task < 'T > = Task.FromResult value
35-
42+
43+
44+ /// <summary >Creates a Task that's completed unsuccessfully with the specified exception.</summary >
45+ /// <param name =" exn " >The exception to be raised.</param >
46+ /// <returns >A Task that is completed unsuccessfully with the specified exception.</returns >
47+ let raise < 'T > ( exn : exn ) : Task < 'T > =
48+ #if NET5_ 0_ OR_ GREATER
49+ Task.FromException< 'T> exn
50+ #else
51+ let tcs = TaskCompletionSource< 'T> tcsOptions
52+ tcs.SetException exn
53+ tcs.Task
54+ #endif
55+
3656 /// <summary >Creates a Task that's completed unsuccessfully with the specified exceptions.</summary >
37- /// <param name =" exn " >The AggregateException to be raised.</param >
57+ /// <param name =" aex " >The AggregateException to be raised.</param >
3858 /// <returns >A Task that is completed unsuccessfully with the specified exceptions.</returns >
3959 /// <remarks >
4060 /// Prefer this function to handle AggregateExceptions over Task.FromException as it handles them correctly.
4161 /// </remarks >
42- let internal FromExceptions < 'T > ( aex : AggregateException ) : Task < 'T > =
62+ let inline internal FromExceptions < 'T > ( aex : AggregateException ) : Task < 'T > =
63+ #if NET5_ 0_ OR_ GREATER
4364 match aex with
4465 | agg when agg.InnerExceptions.Count = 1 -> Task.FromException< 'T> agg.InnerExceptions[ 0 ]
45- | agg ->
46- let tcs = TaskCompletionSource< 'T> ()
47- tcs.SetException agg.InnerExceptions
66+ | _ ->
67+ #endif
68+ let tcs = TaskCompletionSource< 'T> tcsOptions
69+ tcs.SetException aex.InnerExceptions
4870 tcs.Task
4971
50- let private cancellationTokenSingleton = CancellationToken true
51-
5272 /// <summary >Creates a Task that's canceled.</summary >
5373 /// <returns >A Task that's canceled.</returns >
54- let canceled < 'T > : Task < 'T > = Task.FromCanceled< 'T> cancellationTokenSingleton
55-
74+ let canceled < 'T > : Task < 'T > =
75+ #if NET5_ 0_ OR_ GREATER
76+ Task.FromCanceled< 'T> ( CancellationToken true )
77+ #else
78+ let tcs = TaskCompletionSource< 'T> tcsOptions
79+ tcs.SetCanceled ()
80+ tcs.Task
81+ #endif
5682
5783 /// <summary >Creates a task workflow from 'source' workflow, mapping its result with 'mapper'.</summary >
5884 /// <param name =" mapper " >The mapping function.</param >
@@ -67,11 +93,11 @@ module Task =
6793
6894 if source.IsCompleted then
6995 match source with
70- | Succeeded r -> try result ( mapper r) with e -> Task.FromException <_> e
96+ | Succeeded r -> try result ( mapper r) with e -> raise e
7197 | Faulted exn -> FromExceptions exn
7298 | Canceled -> canceled
7399 else
74- let tcs = TaskCompletionSource< 'U> TaskCreationOptions.RunContinuationsAsynchronously
100+ let tcs = TaskCompletionSource< 'U> tcsOptions
75101 source |> continueTask tcs ( fun r -> try tcs.SetResult ( mapper r) with e -> tcs.SetException e)
76102 tcs.Task
77103
@@ -91,13 +117,13 @@ module Task =
91117
92118 if task1.IsCompleted && task2.IsCompleted then
93119 match task1, task2 with
94- | Succeeded r1, Succeeded r2 -> try result ( mapper r1 r2) with e -> Task.FromException <_> e
120+ | Succeeded r1, Succeeded r2 -> try result ( mapper r1 r2) with e -> raise e
95121 | Succeeded _ , Faulted exn -> FromExceptions exn
96122 | Succeeded _ , Canceled -> canceled
97123 | Faulted exn , _ -> FromExceptions exn
98124 | Canceled , _ -> canceled
99125 else
100- let tcs = TaskCompletionSource< 'U> ()
126+ let tcs = TaskCompletionSource< 'U> tcsOptions
101127
102128 match task1.Status, task2.Status with
103129 | TaskStatus.Canceled, _ -> tcs.SetCanceled ()
@@ -128,15 +154,15 @@ module Task =
128154
129155 if task1.IsCompleted && task2.IsCompleted && task3.IsCompleted then
130156 match task1, task2, task3 with
131- | Succeeded r1, Succeeded r2, Succeeded r3 -> try result ( mapper r1 r2 r3) with e -> Task.FromException <_> e
157+ | Succeeded r1, Succeeded r2, Succeeded r3 -> try result ( mapper r1 r2 r3) with e -> raise e
132158 | Faulted exn , _ , _ -> FromExceptions exn
133159 | Canceled , _ , _ -> canceled
134160 | _ , Faulted exn , _ -> FromExceptions exn
135161 | _ , Canceled , _ -> canceled
136162 | _ , _ , Faulted exn -> FromExceptions exn
137163 | _ , _ , Canceled -> canceled
138164 else
139- let tcs = TaskCompletionSource< 'U> ()
165+ let tcs = TaskCompletionSource< 'U> tcsOptions
140166 match task1.Status, task2.Status, task3.Status with
141167 | TaskStatus.Canceled, _ , _ -> tcs.SetCanceled ()
142168 | TaskStatus.Faulted , _ , _ -> tcs.SetException ( Unchecked.nonNull task1.Exception) .InnerExceptions
@@ -168,9 +194,9 @@ module Task =
168194 #endif
169195
170196 if task1.Status = TaskStatus.RanToCompletion && task2.Status = TaskStatus.RanToCompletion then
171- try result ( mapper task1.Result task2.Result) with e -> Task.FromException < 'U > e
197+ try result ( mapper task1.Result task2.Result) with e -> raise e
172198 else
173- let tcs = TaskCompletionSource<_> ()
199+ let tcs = TaskCompletionSource<_> tcsOptions
174200 let r1 = ref Unchecked.defaultof<_>
175201 let r2 = ref Unchecked.defaultof<_>
176202 let mutable cancelled = false
@@ -222,9 +248,9 @@ module Task =
222248
223249 if task1.Status = TaskStatus.RanToCompletion && task2.Status = TaskStatus.RanToCompletion && task3.Status = TaskStatus.RanToCompletion then
224250 try result ( mapper task1.Result task2.Result task3.Result)
225- with e -> Task.FromException < 'U > e
251+ with e -> raise e
226252 else
227- let tcs = TaskCompletionSource<_> ()
253+ let tcs = TaskCompletionSource<_> tcsOptions
228254 let r1 = ref Unchecked.defaultof<_>
229255 let r2 = ref Unchecked.defaultof<_>
230256 let r3 = ref Unchecked.defaultof<_>
@@ -273,13 +299,13 @@ module Task =
273299
274300 if f.IsCompleted && x.IsCompleted then
275301 match f, x with
276- | Succeeded r1, Succeeded r2 -> try result ( r1 r2) with e -> Task.FromException <_> e
302+ | Succeeded r1, Succeeded r2 -> try result ( r1 r2) with e -> raise e
277303 | Succeeded _ , Faulted exn -> FromExceptions exn
278304 | Succeeded _ , Canceled -> canceled
279305 | Faulted exn , _ -> FromExceptions exn
280306 | Canceled , _ -> canceled
281307 else
282- let tcs = TaskCompletionSource< 'U> ()
308+ let tcs = TaskCompletionSource< 'U> tcsOptions
283309 match f.Status, x.Status with
284310 | TaskStatus.Canceled, _ -> tcs.SetCanceled ()
285311 | TaskStatus.Faulted, _ -> tcs.SetException ( Unchecked.nonNull f.Exception) .InnerExceptions
@@ -307,7 +333,7 @@ module Task =
307333 | Faulted exn , _ -> FromExceptions exn
308334 | Canceled , _ -> canceled
309335 else
310- let tcs = TaskCompletionSource< 'T1 * 'T2> ()
336+ let tcs = TaskCompletionSource< 'T1 * 'T2> tcsOptions
311337 match task1.Status, task2.Status with
312338 | TaskStatus.Canceled, _ -> tcs.SetCanceled ()
313339 | TaskStatus.Faulted, _ -> tcs.SetException ( Unchecked.nonNull task1.Exception) .InnerExceptions
@@ -357,11 +383,12 @@ module Task =
357383 raiseIfNull " source" source
358384 #endif
359385
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> ()
386+ match source.Status with
387+ | TaskStatus.RanToCompletion -> result ()
388+ | TaskStatus.Faulted -> FromExceptions ( Unchecked.nonNull source.Exception)
389+ | TaskStatus.Canceled -> canceled
390+ | _ ->
391+ let tcs = TaskCompletionSource< unit> tcsOptions
365392 let k ( t : Task ) : unit =
366393 if t.IsCanceled then tcs.SetCanceled ()
367394 elif t.IsFaulted then tcs.SetException ( Unchecked.nonNull source.Exception) .InnerExceptions
@@ -373,7 +400,7 @@ module Task =
373400 let rec tryWith ( body : unit -> Task < 'T >) ( compensation : exn -> Task < 'T >) : Task < 'T > =
374401 let runCompensation exn =
375402 try compensation exn
376- with e -> Task.FromException < 'T > e
403+ with e -> raise e
377404 let unwrapException ( agg : AggregateException ) =
378405 if agg.InnerExceptions.Count = 1 then agg.InnerExceptions.[ 0 ]
379406 else agg :> Exception
@@ -397,12 +424,12 @@ module Task =
397424 try
398425 compensation ()
399426 reraise ()
400- with e -> Task.FromException < 'T > e
427+ with e -> raise e
401428 if task.IsCompleted then
402429 try
403430 compensation ()
404431 task
405- with e -> Task.FromException < 'T > e
432+ with e -> raise e
406433 else
407434 task.ContinueWith( fun ( x : Task < 'T >) -> tryFinally ( fun () -> x) compensation) .Unwrap ()
408435
@@ -447,11 +474,46 @@ module Task =
447474 raiseIfNull " source" source
448475 #endif
449476 orElseWith ( fun _ -> fallbackTask) source
477+
478+ /// <summary >Attempts to recover from a potentially failed task by mapping the exception to a successful result.</summary >
479+ /// <param name =" mapper " >Mapping function from exception to result.</param >
480+ /// <param name =" source " >The source task.</param >
481+ /// <returns >A successful resulting task.</returns >
482+ /// <remarks >The result is always a successful task, unless the mapping function itself throws an exception.</remarks >
483+ let inline recover ( [<InlineIfLambda>] mapper : exn -> 'T ) ( source : Task < 'T >) : Task < 'T > =
484+ let source = nullArgCheck ( nameof source) source
450485
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
486+ tryWith ( fun () -> source) ( mapper >> result)
487+
488+ /// <summary >Maps the exception of a faulted task to another exception.</summary >
489+ /// <param name =" mapper " >Mapping function from exception to exception.</param >
490+ /// <param name =" source " >The source task.</param >
491+ /// <returns >The resulting task.</returns >
492+ let inline mapError ( [<InlineIfLambda>] mapper : exn -> exn ) ( source : Task < 'T >) : Task < 'T > =
493+ let source = nullArgCheck ( nameof source) source
494+
495+ if source.IsCompleted then
496+ match source with
497+ | Faulted exn -> FromExceptions ( AggregateException ( mapper exn))
498+ | _ -> source
499+ else
500+ let tcs = TaskCompletionSource< 'T> tcsOptions
501+ let k = function
502+ | Succeeded r -> tcs.SetResult r
503+ | Faulted aex -> tcs.SetException ( AggregateException ( mapper aex)) .InnerExceptions
504+ | Canceled -> tcs.SetCanceled ()
505+ source.ConfigureAwait( false ) .GetAwaiter() .UnsafeOnCompleted ( fun () -> k source)
506+ tcs.Task
507+
508+ /// Creates a Task from a Result value.
509+ /// If the Result is Ok, the Task will complete successfully with the value.
510+ /// If the Result is Error, the Task will complete unsuccessfully with the exception.
511+ /// <param name =" source " >The source Result.</param >
512+ /// <returns >The resulting Task.</returns >
513+ let ofResult ( source : Result < 'T , exn >) : Task < 'T > =
514+ match source with
515+ | Ok x -> result x
516+ | Error exn -> raise exn
455517
456518
457519/// Workaround to fix signatures without breaking binary compatibility.
0 commit comments