Skip to content

Commit 28a8d5a

Browse files
committed
Fix ValueTask
1 parent 0926e4f commit 28a8d5a

File tree

1 file changed

+53
-22
lines changed

1 file changed

+53
-22
lines changed

src/FSharpPlus/Extensions/ValueTask.fs

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,29 +26,63 @@ module ValueTask =
2626
if x.IsCompleted then f x
2727
else x.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> f x)
2828

29-
let inline continueWith (x: ValueTask<'t>) f =
29+
let inline continueWith f (x: ValueTask<'t>) =
3030
if x.IsCompleted then f x
3131
else x.ConfigureAwait(false).GetAwaiter().UnsafeOnCompleted (fun () -> f x)
3232

33-
/// Creates a ValueTask from a value
33+
34+
/// <summary>Creates a ValueTask that's completed successfully with the specified value.</summary>
35+
/// <param name="value"></param>
36+
/// <returns>A ValueTask that is completed successfully with the specified value.</returns>
3437
let result (value: 'T) : ValueTask<'T> =
3538
#if NET5_0_OR_GREATER
3639
ValueTask.FromResult value
3740
#else
3841
let tcs = TaskCompletionSource<'T> ()
3942
tcs.SetResult value
40-
tcs.Task |> ValueTask<'T>
43+
ValueTask<'T> tcs.Task
4144
#endif
45+
46+
/// <summary>Creates a ValueTask that's completed unsuccessfully with the specified exception.</summary>
47+
/// <param name="exn">The exception to be raised.</param>
48+
/// <returns>A ValueTask that is completed unsuccessfully with the specified exception.</returns>
49+
/// <remarks>
50+
/// If the exception is not an AggregateException it is wrapped into one.
51+
/// Prefer this function over ValueTask.FromException as it handles AggregateExceptions correctly.
52+
/// </remarks>
53+
let raise<'T> (exn: exn) : ValueTask<'T> =
54+
match exn with
55+
| :? AggregateException as agg when agg.InnerExceptions.Count > 1 ->
56+
let tcs = TaskCompletionSource<'T> ()
57+
tcs.SetException agg.InnerExceptions
58+
ValueTask<'T> tcs.Task
59+
| :? AggregateException as agg -> ValueTask.FromException<'T> agg.InnerExceptions[0]
60+
| exn -> ValueTask.FromException<'T> exn
4261

43-
/// <summary>Creates a ValueTask workflow from 'source' another, mapping its result with 'f'.</summary>
44-
/// <param name="f">The mapping function.</param>
45-
/// <param name="source">ValueTask workflow.</param>
46-
let map (f: 'T -> 'U) (source: ValueTask<'T>) : ValueTask<'U> =
47-
let tcs = TaskCompletionSource<'U> ()
48-
continueTask tcs source (fun x ->
49-
try tcs.SetResult (f x)
50-
with e -> tcs.SetException e)
51-
tcs.Task |> ValueTask<'U>
62+
let private cancellationTokenSingleton = CancellationToken true
63+
64+
/// <summary>Creates a ValueTask that's canceled.</summary>
65+
/// <returns>A ValueTask that's canceled.</returns>
66+
let canceled<'T> : ValueTask<'T> = ValueTask.FromCanceled<'T> cancellationTokenSingleton
67+
68+
/// <summary>Creates a ValueTask workflow from 'source' workflow, mapping its result with 'mapper'.</summary>
69+
/// <param name="mapper">The mapping function.</param>
70+
/// <param name="source">The source ValueTask workflow.</param>
71+
/// <returns>The resulting ValueTask workflow.</returns>
72+
let map (mapper: 'T -> 'U) (source: ValueTask<'T>) : ValueTask<'U> =
73+
if source.IsCompleted then
74+
match source with
75+
| Succeeded r -> try result (mapper r) with e -> raise e
76+
| Faulted exn -> raise exn
77+
| Canceled -> canceled
78+
else
79+
let tcs = TaskCompletionSource<'U> ()
80+
let k = function
81+
| Succeeded r -> try tcs.SetResult (mapper r) with e -> tcs.SetException e
82+
| Faulted exn -> tcs.SetException exn.InnerExceptions
83+
| Canceled -> tcs.SetCanceled ()
84+
continueWith k source
85+
ValueTask<'U> tcs.Task
5286

5387

5488
/// <summary>Creates a ValueTask workflow from two workflows 'x' and 'y', mapping its results with 'f'.</summary>
@@ -121,9 +155,9 @@ module ValueTask =
121155
task1 |> k r1 0
122156
task2 |> k r2 1
123157
else
124-
continueWith task1 (k r1 0)
125-
continueWith task2 (k r2 1)
126-
tcs.Task |> ValueTask<'U>
158+
continueWith (k r1 0) task1
159+
continueWith (k r2 1) task2
160+
ValueTask<'U> tcs.Task
127161

128162
/// <summary>Creates a ValueTask workflow from three workflows, mapping its results with a specified function.</summary>
129163
/// <remarks>Similar to lift3 but although workflows are started in sequence they might end independently in different order
@@ -170,10 +204,10 @@ module ValueTask =
170204
task2 |> k r2 1
171205
task3 |> k r3 2
172206
else
173-
continueWith task1 (k r1 0)
174-
continueWith task2 (k r2 1)
175-
continueWith task3 (k r3 2)
176-
tcs.Task |> ValueTask<'U>
207+
continueWith (k r1 0) task1
208+
continueWith (k r2 1) task2
209+
continueWith (k r3 2) task3
210+
ValueTask<'U> tcs.Task
177211

178212
/// <summary>Creates a ValueTask workflow that is the result of applying the resulting function of a ValueTask workflow
179213
/// to the resulting value of another ValueTask workflow</summary>
@@ -313,7 +347,4 @@ module ValueTask =
313347
/// <returns>The option if the option is Some, else the alternate option.</returns>
314348
let orElse (fallbackValueTask: ValueTask<'T>) (source: ValueTask<'T>) : ValueTask<'T> = orElseWith (fun _ -> fallbackValueTask) source
315349

316-
/// Raises an exception in the ValueTask
317-
let raise<'TResult> (``exception``: exn) = ValueTask<'TResult> (Task.FromException<'TResult> ``exception``)
318-
319350
#endif

0 commit comments

Comments
 (0)