Skip to content

Commit 4f98a9c

Browse files
committed
Avoid F# built-in task CEs
1 parent 9544fff commit 4f98a9c

2 files changed

Lines changed: 80 additions & 38 deletions

File tree

src/FSharpPlus/Extensions/Task.fs

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,15 @@ module Task =
6060
let map (mapper: 'T -> 'U) (source: Task<'T>) : Task<'U> =
6161
let source = nullArgCheck (nameof source) source
6262

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

6873
/// <summary>Creates a task workflow from two workflows 'task1' and 'task2', mapping its results with 'mapper'.</summary>
6974
/// <remarks>Workflows are run in sequence.</remarks>
@@ -293,19 +298,13 @@ module Task =
293298
let join (source: Task<Task<'T>>) : Task<'T> =
294299
let source = nullArgCheck (nameof source) source
295300

296-
backgroundTask {
297-
let! inner = source
298-
return! inner
299-
}
301+
source.Unwrap()
300302

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

305-
backgroundTask {
306-
let! r = source
307-
return! f r
308-
}
307+
source |> Unchecked.nonNull |> map f |> join
309308

310309
/// <summary>Creates a task that ignores the result of the source task.</summary>
311310
/// <param name="source">The source Task.</param>
@@ -327,14 +326,37 @@ module Task =
327326
tcs.Task
328327

329328
[<ObsoleteAttribute("Swap parameters")>]
330-
let tryWith (body: unit -> Task<'T>) (compensation: exn -> Task<'T>) : Task<'T> = backgroundTask {
331-
try return! body ()
332-
with e -> return! compensation e }
329+
let rec tryWith (body: unit -> Task<'T>) (compensation: exn -> Task<'T>) : Task<'T> =
330+
let unwrapException (agg: AggregateException) =
331+
if agg.InnerExceptions.Count = 1 then agg.InnerExceptions.[0]
332+
else agg :> Exception
333+
try Ok (body ()) with e -> Error e
334+
|> function
335+
| Ok task ->
336+
if task.IsCompleted then
337+
match task with
338+
| Succeeded _ -> task
339+
| Faulted aex -> compensation (unwrapException aex)
340+
| Canceled -> canceled
341+
else
342+
task.ContinueWith((fun (x: Task<'T>) -> tryWith (fun () -> x) compensation)).Unwrap ()
343+
| Error (:? AggregateException as exn) -> compensation (unwrapException exn)
344+
| Error exn -> compensation exn
333345

334346
[<ObsoleteAttribute("Swap parameters")>]
335-
let tryFinally (body: unit -> Task<'T>) (compensation : unit -> unit) : Task<'T> = backgroundTask {
336-
try return! body ()
337-
finally compensation () }
347+
let tryFinally (body: unit -> Task<'T>) (compensation : unit -> unit) : Task<'T> =
348+
let mutable ran = false
349+
let compensation () =
350+
if not ran then
351+
ran <- true
352+
compensation ()
353+
try
354+
let task = body ()
355+
if task.IsCompleted then compensation (); task
356+
else task.ContinueWith(fun (_: Task<'T>) -> compensation (); task).Unwrap ()
357+
with _ ->
358+
compensation ()
359+
reraise ()
338360

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

src/FSharpPlus/Extensions/ValueTask.fs

Lines changed: 40 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,38 @@ 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 unwrapException (agg: AggregateException) =
305+
if agg.InnerExceptions.Count = 1 then agg.InnerExceptions.[0]
306+
else agg :> Exception
307+
try Ok (body ()) with e -> Error e
308+
|> function
309+
| Ok task ->
310+
if task.IsCompleted then
311+
match task with
312+
| Succeeded _ -> task
313+
| Faulted aex -> compensation (unwrapException aex)
314+
| Canceled -> canceled
315+
else
316+
task.AsTask().ContinueWith((fun (x: Task<'T>) -> Task.tryWith (compensation >> fun x -> x.AsTask()) (fun () -> x))).Unwrap () |> ValueTask<'T>
317+
| Error (:? AggregateException as exn) -> compensation (unwrapException exn)
318+
| Error exn -> compensation exn
319+
309320

310321
/// Used to de-sugar try .. finally .. blocks in Computation Expressions.
311322
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>
323+
let mutable ran = false
324+
let compensation () =
325+
if not ran then
326+
ran <- true
327+
compensation ()
328+
try
329+
let task = body().AsTask ()
330+
if task.IsCompleted then compensation (); task
331+
else task.ContinueWith(fun (_: Task<'T>) -> compensation (); task).Unwrap ()
332+
with _ ->
333+
compensation ()
334+
reraise ()
335+
|> ValueTask<'T>
316336

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

0 commit comments

Comments
 (0)