Skip to content

Commit 553e8d4

Browse files
gustywallymathieu
authored andcommitted
Avoid F# built-in task CEs
1 parent 3ac4476 commit 553e8d4

2 files changed

Lines changed: 87 additions & 38 deletions

File tree

src/FSharpPlus/Extensions/Task.fs

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,15 @@ module Task =
6565
raiseIfNull "source" source
6666
#endif
6767

68-
backgroundTask {
69-
let! r = source
70-
return mapper r
71-
}
68+
if source.IsCompleted then
69+
match source with
70+
| Succeeded r -> try result (mapper r) with e -> Task.FromException<_> e
71+
| Faulted exn -> FromExceptions exn
72+
| Canceled -> canceled
73+
else
74+
let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
75+
source |> continueTask tcs (fun r -> try tcs.SetResult (mapper r) with e -> tcs.SetException e)
76+
tcs.Task
7277

7378
/// <summary>Creates a task workflow from two workflows 'task1' and 'task2', mapping its results with 'mapper'.</summary>
7479
/// <remarks>Workflows are run in sequence.</remarks>
@@ -333,19 +338,13 @@ module Task =
333338
raiseIfNull "source" source
334339
#endif
335340

336-
backgroundTask {
337-
let! inner = source
338-
return! inner
339-
}
341+
source.Unwrap()
340342

341343
/// <summary>Creates a task workflow from 'source' workflow, mapping and flattening its result with 'f'.</summary>
342344
let bind (f: 'T -> Task<'U>) (source: Task<'T>) : Task<'U> =
343345
let source = nullArgCheck (nameof source) source
344346

345-
backgroundTask {
346-
let! r = source
347-
return! f r
348-
}
347+
source |> Unchecked.nonNull |> map f |> join
349348

350349
/// <summary>Creates a task that ignores the result of the source task.</summary>
351350
/// <param name="source">The source Task.</param>
@@ -371,14 +370,41 @@ module Task =
371370
tcs.Task
372371

373372
[<ObsoleteAttribute("Swap parameters")>]
374-
let tryWith (body: unit -> Task<'T>) (compensation: exn -> Task<'T>) : Task<'T> = backgroundTask {
375-
try return! body ()
376-
with e -> return! compensation e }
373+
let rec tryWith (body: unit -> Task<'T>) (compensation: exn -> Task<'T>) : Task<'T> =
374+
let runCompensation exn =
375+
try compensation exn
376+
with e -> Task.FromException<'T> e
377+
let unwrapException (agg: AggregateException) =
378+
if agg.InnerExceptions.Count = 1 then agg.InnerExceptions.[0]
379+
else agg :> Exception
380+
try Ok (body ()) with e -> Error e
381+
|> function
382+
| Ok task ->
383+
if task.IsCompleted then
384+
match task with
385+
| Succeeded _ -> task
386+
| Faulted aex -> runCompensation (unwrapException aex)
387+
| Canceled -> canceled
388+
else
389+
task.ContinueWith(fun (x: Task<'T>) -> tryWith (fun () -> x) compensation).Unwrap ()
390+
| Error exn -> runCompensation exn
377391

378392
[<ObsoleteAttribute("Swap parameters")>]
379-
let tryFinally (body: unit -> Task<'T>) (compensation : unit -> unit) : Task<'T> = backgroundTask {
380-
try return! body ()
381-
finally compensation () }
393+
let rec tryFinally (body: unit -> Task<'T>) (compensation : unit -> unit) : Task<'T> =
394+
let task =
395+
try body ()
396+
with _ ->
397+
try
398+
compensation ()
399+
reraise ()
400+
with e -> Task.FromException<'T> e
401+
if task.IsCompleted then
402+
try
403+
compensation ()
404+
task
405+
with e -> Task.FromException<'T> e
406+
else
407+
task.ContinueWith(fun (x: Task<'T>) -> tryFinally (fun () -> x) compensation).Unwrap ()
382408

383409
/// Used to de-sugar use .. blocks in Computation Expressions.
384410
let using (disp: 'T when 'T :> IDisposable) (body: 'T -> Task<'U>) =

src/FSharpPlus/Extensions/ValueTask.fs

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,15 @@ module ValueTask =
6363
/// <param name="source">The source ValueTask workflow.</param>
6464
/// <returns>The resulting ValueTask workflow.</returns>
6565
let map (mapper: 'T -> 'U) (source: ValueTask<'T>) : ValueTask<'U> =
66-
backgroundTask {
67-
let! r = source
68-
return mapper r
69-
} |> ValueTask<'U>
66+
if source.IsCompleted then
67+
match source with
68+
| Succeeded r -> try result (mapper r) with e -> ValueTask.FromException<_> e
69+
| Faulted exn -> FromExceptions exn
70+
| Canceled -> canceled
71+
else
72+
let tcs = TaskCompletionSource<'U> TaskCreationOptions.RunContinuationsAsynchronously
73+
source |> continueTask tcs (fun r -> try tcs.SetResult (mapper r) with e -> tcs.SetException e)
74+
tcs.Task |> ValueTask<'U>
7075

7176

7277
/// <summary>Creates a ValueTask workflow from two workflows 'x' and 'y', mapping its results with 'f'.</summary>
@@ -271,17 +276,11 @@ module ValueTask =
271276

272277
/// Flattens two nested ValueTask into one.
273278
let join (source: ValueTask<ValueTask<'T>>) : ValueTask<'T> =
274-
backgroundTask {
275-
let! inner = source
276-
return! inner
277-
} |> ValueTask<'T>
279+
(source |> map (fun x -> x.AsTask())).AsTask().Unwrap () |> ValueTask<'T>
278280

279281
/// <summary>Creates a ValueTask workflow from 'source' workflow, mapping and flattening its result with 'f'.</summary>
280282
let bind (f: 'T -> ValueTask<'U>) (source: ValueTask<'T>) : ValueTask<'U> =
281-
backgroundTask {
282-
let! r = source
283-
return! f r
284-
} |> ValueTask<'U>
283+
source |> map f |> join
285284

286285
/// <summary>Creates a ValueTask that ignores the result of the source ValueTask.</summary>
287286
/// <param name="source">The source ValueTask.</param>
@@ -302,17 +301,41 @@ module ValueTask =
302301

303302
/// Used to de-sugar try .. with .. blocks in Computation Expressions.
304303
let inline tryWith ([<InlineIfLambda>]compensation: exn -> ValueTask<'T>) ([<InlineIfLambda>]body: unit -> ValueTask<'T>) : ValueTask<'T> =
305-
backgroundTask {
306-
try return! body ()
307-
with e -> return! compensation e
308-
} |> ValueTask<'T>
304+
let runCompensation exn =
305+
try compensation exn
306+
with e -> ValueTask.FromException<'T> e
307+
let unwrapException (agg: AggregateException) =
308+
if agg.InnerExceptions.Count = 1 then agg.InnerExceptions.[0]
309+
else agg :> Exception
310+
try Ok (body ()) with e -> Error e
311+
|> function
312+
| Ok task ->
313+
if task.IsCompleted then
314+
match task with
315+
| Succeeded _ -> task
316+
| Faulted aex -> runCompensation (unwrapException aex)
317+
| Canceled -> canceled
318+
else
319+
task.AsTask().ContinueWith(fun (x: Task<'T>) -> Task.tryWith (compensation >> fun x -> x.AsTask()) (fun () -> x)).Unwrap () |> ValueTask<'T>
320+
| Error exn -> runCompensation exn
321+
309322

310323
/// Used to de-sugar try .. finally .. blocks in Computation Expressions.
311324
let inline tryFinally ([<InlineIfLambda>]compensation : unit -> unit) ([<InlineIfLambda>]body: unit -> ValueTask<'T>) : ValueTask<'T> =
312-
backgroundTask {
313-
try return! body ()
314-
finally compensation ()
315-
} |> ValueTask<'T>
325+
let task =
326+
try body ()
327+
with _ ->
328+
try
329+
compensation ()
330+
reraise ()
331+
with e -> ValueTask.FromException<'T> e
332+
if task.IsCompleted then
333+
try
334+
compensation ()
335+
task
336+
with e -> ValueTask.FromException<'T> e
337+
else
338+
task.AsTask().ContinueWith(fun (x: Task<'T>) -> Task.tryFinally compensation (fun () -> x)).Unwrap () |> ValueTask<'T>
316339

317340
/// Used to de-sugar use .. blocks in Computation Expressions.
318341
let inline using (disp: 'T when 'T :> IDisposable) ([<InlineIfLambda>]body: 'T -> ValueTask<'U>) =

0 commit comments

Comments
 (0)