Skip to content

Commit bc1c717

Browse files
committed
Reorganize code for multitargeting
1 parent 651fc07 commit bc1c717

File tree

2 files changed

+118
-78
lines changed

2 files changed

+118
-78
lines changed

src/FSharpPlus/Extensions/Task.fs

Lines changed: 64 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ module Task =
1414

1515
/// Active pattern to match the state of a completed Task
1616
let inline internal (|Succeeded|Canceled|Faulted|) (t: Task<'a>) =
17-
if t.IsCompletedSuccessfully then Succeeded t.Result
18-
elif t.IsFaulted then Faulted (Unchecked.nonNull t.Exception)
19-
elif t.IsCanceled then Canceled
20-
else invalidOp "Internal error: The task is not yet completed."
17+
match t.Status with
18+
| TaskStatus.RanToCompletion -> Succeeded t.Result
19+
| TaskStatus.Faulted -> Faulted (Unchecked.nonNull t.Exception)
20+
| TaskStatus.Canceled -> Canceled
21+
| _ -> invalidOp (sprintf "Internal error: The task is not yet in a final state. State = TaskStatus.%A" t.Status)
2122

2223
let inline internal continueTask (tcs: TaskCompletionSource<'Result>) (k: 't -> unit) (x: Task<'t>) =
2324
let f = function
@@ -26,32 +27,56 @@ module Task =
2627
| Canceled -> tcs.SetCanceled ()
2728
x.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> f x)
2829

30+
#if NET5_0_OR_GREATER
31+
let [<Literal>] private tcsOptions = TaskCreationOptions.RunContinuationsAsynchronously
32+
#else
33+
let private tcsOptions = ()
34+
#endif
2935

3036
/// <summary>Creates a Task that's completed successfully with the specified value.</summary>
3137
/// <param name="value"></param>
3238
/// <returns>A Task that is completed successfully with the specified value.</returns>
3339
let result (value: 'T) : Task<'T> = Task.FromResult value
34-
40+
41+
42+
/// <summary>Creates a Task that's completed unsuccessfully with the specified exception.</summary>
43+
/// <param name="exn">The exception to be raised.</param>
44+
/// <returns>A Task that is completed unsuccessfully with the specified exception.</returns>
45+
let raise<'T> (exn: exn) : Task<'T> =
46+
#if NET5_0_OR_GREATER
47+
Task.FromException<'T> exn
48+
#else
49+
let tcs = TaskCompletionSource<'T> tcsOptions
50+
tcs.SetException exn
51+
tcs.Task
52+
#endif
53+
3554
/// <summary>Creates a Task that's completed unsuccessfully with the specified exceptions.</summary>
36-
/// <param name="exn">The AggregateException to be raised.</param>
55+
/// <param name="aex">The AggregateException to be raised.</param>
3756
/// <returns>A Task that is completed unsuccessfully with the specified exceptions.</returns>
3857
/// <remarks>
3958
/// Prefer this function to handle AggregateExceptions over Task.FromException as it handles them correctly.
4059
/// </remarks>
4160
let inline internal FromExceptions<'T> (aex: AggregateException) : Task<'T> =
61+
#if NET5_0_OR_GREATER
4262
match aex with
4363
| agg when agg.InnerExceptions.Count = 1 -> Task.FromException<'T> agg.InnerExceptions[0]
44-
| agg ->
45-
let tcs = TaskCompletionSource<'T> ()
46-
tcs.SetException agg.InnerExceptions
64+
| _ ->
65+
#endif
66+
let tcs = TaskCompletionSource<'T> tcsOptions
67+
tcs.SetException aex.InnerExceptions
4768
tcs.Task
4869

49-
let private cancellationTokenSingleton = CancellationToken true
50-
5170
/// <summary>Creates a Task that's canceled.</summary>
5271
/// <returns>A Task that's canceled.</returns>
53-
let canceled<'T> : Task<'T> = Task.FromCanceled<'T> cancellationTokenSingleton
54-
72+
let canceled<'T> : Task<'T> =
73+
#if NET5_0_OR_GREATER
74+
Task.FromCanceled<'T> (CancellationToken true)
75+
#else
76+
let tcs = TaskCompletionSource<'T> tcsOptions
77+
tcs.SetCanceled ()
78+
tcs.Task
79+
#endif
5580

5681
/// <summary>Creates a task workflow from 'source' workflow, mapping its result with 'mapper'.</summary>
5782
/// <param name="mapper">The mapping function.</param>
@@ -62,11 +87,11 @@ module Task =
6287

6388
if source.IsCompleted then
6489
match source with
65-
| Succeeded r -> try result (mapper r) with e -> Task.FromException<_> e
90+
| Succeeded r -> try result (mapper r) with e -> raise e
6691
| Faulted exn -> FromExceptions exn
6792
| Canceled -> canceled
6893
else
69-
let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
94+
let tcs = TaskCompletionSource<'U> tcsOptions
7095
source |> continueTask tcs (fun r -> try tcs.SetResult (mapper r) with e -> tcs.SetException e)
7196
tcs.Task
7297

@@ -81,13 +106,13 @@ module Task =
81106

82107
if task1.IsCompleted && task2.IsCompleted then
83108
match task1, task2 with
84-
| Succeeded r1, Succeeded r2 -> try result (mapper r1 r2) with e -> Task.FromException<_> e
109+
| Succeeded r1, Succeeded r2 -> try result (mapper r1 r2) with e -> raise e
85110
| Succeeded _ , Faulted exn -> FromExceptions exn
86111
| Succeeded _ , Canceled -> canceled
87112
| Faulted exn , _ -> FromExceptions exn
88113
| Canceled , _ -> canceled
89114
else
90-
let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
115+
let tcs = TaskCompletionSource<'U> tcsOptions
91116

92117
match task1.Status, task2.Status with
93118
| TaskStatus.Canceled, _ -> tcs.SetCanceled ()
@@ -112,15 +137,15 @@ module Task =
112137

113138
if task1.IsCompleted && task2.IsCompleted && task3.IsCompleted then
114139
match task1, task2, task3 with
115-
| Succeeded r1, Succeeded r2, Succeeded r3 -> try result (mapper r1 r2 r3) with e -> Task.FromException<_> e
140+
| Succeeded r1, Succeeded r2, Succeeded r3 -> try result (mapper r1 r2 r3) with e -> raise e
116141
| Faulted exn , _ , _ -> FromExceptions exn
117142
| Canceled , _ , _ -> canceled
118143
| _ , Faulted exn , _ -> FromExceptions exn
119144
| _ , Canceled , _ -> canceled
120145
| _ , _ , Faulted exn -> FromExceptions exn
121146
| _ , _ , Canceled -> canceled
122147
else
123-
let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
148+
let tcs = TaskCompletionSource<'U> tcsOptions
124149
match task1.Status, task2.Status, task3.Status with
125150
| TaskStatus.Canceled, _ , _ -> tcs.SetCanceled ()
126151
| TaskStatus.Faulted , _ , _ -> tcs.SetException (Unchecked.nonNull task1.Exception).InnerExceptions
@@ -147,9 +172,9 @@ module Task =
147172
let task2 = nullArgCheck (nameof task2) task2
148173

149174
if task1.Status = TaskStatus.RanToCompletion && task2.Status = TaskStatus.RanToCompletion then
150-
try result (mapper task1.Result task2.Result) with e -> Task.FromException<'U> e
175+
try result (mapper task1.Result task2.Result) with e -> raise e
151176
else
152-
let tcs = TaskCompletionSource<_> ()
177+
let tcs = TaskCompletionSource<_> tcsOptions
153178
let r1 = ref Unchecked.defaultof<_>
154179
let r2 = ref Unchecked.defaultof<_>
155180
let mutable cancelled = false
@@ -195,9 +220,9 @@ module Task =
195220

196221
if task1.Status = TaskStatus.RanToCompletion && task2.Status = TaskStatus.RanToCompletion && task3.Status = TaskStatus.RanToCompletion then
197222
try result (mapper task1.Result task2.Result task3.Result)
198-
with e -> Task.FromException<'U> e
223+
with e -> raise e
199224
else
200-
let tcs = TaskCompletionSource<_> ()
225+
let tcs = TaskCompletionSource<_> tcsOptions
201226
let r1 = ref Unchecked.defaultof<_>
202227
let r2 = ref Unchecked.defaultof<_>
203228
let r3 = ref Unchecked.defaultof<_>
@@ -241,13 +266,13 @@ module Task =
241266

242267
if f.IsCompleted && x.IsCompleted then
243268
match f, x with
244-
| Succeeded r1, Succeeded r2 -> try result (r1 r2) with e -> Task.FromException<_> e
269+
| Succeeded r1, Succeeded r2 -> try result (r1 r2) with e -> raise e
245270
| Succeeded _ , Faulted exn -> FromExceptions exn
246271
| Succeeded _ , Canceled -> canceled
247272
| Faulted exn , _ -> FromExceptions exn
248273
| Canceled , _ -> canceled
249274
else
250-
let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
275+
let tcs = TaskCompletionSource<'U> tcsOptions
251276
match f.Status, x.Status with
252277
| TaskStatus.Canceled, _ -> tcs.SetCanceled ()
253278
| TaskStatus.Faulted, _ -> tcs.SetException (Unchecked.nonNull f.Exception).InnerExceptions
@@ -271,7 +296,7 @@ module Task =
271296
| Faulted exn , _ -> FromExceptions exn
272297
| Canceled , _ -> canceled
273298
else
274-
let tcs = TaskCompletionSource<'T1 * 'T2> ()
299+
let tcs = TaskCompletionSource<'T1 * 'T2> tcsOptions
275300
match task1.Status, task2.Status with
276301
| TaskStatus.Canceled, _ -> tcs.SetCanceled ()
277302
| TaskStatus.Faulted, _ -> tcs.SetException (Unchecked.nonNull task1.Exception).InnerExceptions
@@ -313,11 +338,12 @@ module Task =
313338
let ignore (source: Task) =
314339
let source = nullArgCheck (nameof source) source
315340

316-
if source.IsCompletedSuccessfully then result ()
317-
elif source.IsFaulted then FromExceptions (Unchecked.nonNull source.Exception)
318-
elif source.IsCanceled then canceled
319-
else
320-
let tcs = TaskCompletionSource<unit> ()
341+
match source.Status with
342+
| TaskStatus.RanToCompletion -> result ()
343+
| TaskStatus.Faulted -> FromExceptions (Unchecked.nonNull source.Exception)
344+
| TaskStatus.Canceled -> canceled
345+
| _ ->
346+
let tcs = TaskCompletionSource<unit> tcsOptions
321347
let k (t: Task) : unit =
322348
if t.IsCanceled then tcs.SetCanceled ()
323349
elif t.IsFaulted then tcs.SetException (Unchecked.nonNull source.Exception).InnerExceptions
@@ -329,7 +355,7 @@ module Task =
329355
let rec tryWith (body: unit -> Task<'T>) (compensation: exn -> Task<'T>) : Task<'T> =
330356
let runCompensation exn =
331357
try compensation exn
332-
with e -> Task.FromException<'T> e
358+
with e -> raise e
333359
let unwrapException (agg: AggregateException) =
334360
if agg.InnerExceptions.Count = 1 then agg.InnerExceptions.[0]
335361
else agg :> Exception
@@ -353,12 +379,12 @@ module Task =
353379
try
354380
compensation ()
355381
reraise ()
356-
with e -> Task.FromException<'T> e
382+
with e -> raise e
357383
if task.IsCompleted then
358384
try
359385
compensation ()
360386
task
361-
with e -> Task.FromException<'T> e
387+
with e -> raise e
362388
else
363389
task.ContinueWith(fun (x: Task<'T>) -> tryFinally (fun () -> x) compensation).Unwrap ()
364390

@@ -401,7 +427,7 @@ module Task =
401427
let inline recover ([<InlineIfLambda>]mapper: exn -> 'T) (source: Task<'T>) : Task<'T> =
402428
let source = nullArgCheck (nameof source) source
403429

404-
tryWith (fun () -> source) (mapper >> Task.FromResult)
430+
tryWith (fun () -> source) (mapper >> result)
405431

406432
/// <summary>Maps the exception of a faulted task to another exception.</summary>
407433
/// <param name="mapper">Mapping function from exception to exception.</param>
@@ -415,7 +441,7 @@ module Task =
415441
| Faulted exn -> FromExceptions (AggregateException (mapper exn))
416442
| _ -> source
417443
else
418-
let tcs = TaskCompletionSource<'T> TaskCreationOptions.RunContinuationsAsynchronously
444+
let tcs = TaskCompletionSource<'T> tcsOptions
419445
let k = function
420446
| Succeeded r -> tcs.SetResult r
421447
| Faulted aex -> tcs.SetException (AggregateException (mapper aex)).InnerExceptions
@@ -430,13 +456,8 @@ module Task =
430456
/// <returns>The resulting Task.</returns>
431457
let ofResult (source: Result<'T, exn>) : Task<'T> =
432458
match source with
433-
| Ok x -> Task.FromResult x
434-
| Error exn -> Task.FromException<'T> exn
435-
436-
/// <summary>Creates a Task that's completed unsuccessfully with the specified exception.</summary>
437-
/// <param name="exn">The exception to be raised.</param>
438-
/// <returns>A Task that is completed unsuccessfully with the specified exception.</returns>
439-
let raise<'T> (exn: exn) : Task<'T> = Task.FromException<'T> exn
459+
| Ok x -> result x
460+
| Error exn -> raise exn
440461

441462

442463
/// Workaround to fix signatures without breaking binary compatibility.

0 commit comments

Comments
 (0)