When Async.Parallel is handling cancellation (via the token or a computation throwing) with e.g. 2000 items outstanding, the current implementation as of FSharp.Core 6.0.4 is such that the non-tail-recursive impl can blow the stack.
Repro steps
let [<Fact>] ``Async.Parallel blows stack when cancelling many`` () =
let gen (i : int) = async {
if i <> 0 then do! Async.Sleep i
else return failwith (string i) }
let count = 1800
let comps = Seq.init count gen
let result = Async.Parallel(comps, 16) |> Async.Catch |> Async.RunSynchronously
test <@ match result with
| Choice2Of2 e -> int e.Message < count
| x -> failwithf "unexpected %A" x @>
Known workarounds
batch the work with Seq.ChunkBySize 1500, add an outer Async.Parallel call (distributing the degreeOfParallelism over the constituent calls, or using the same value but internally constraining it with Async.Throttle), Array.concat the results.
Related information
(1800 is the approx threshold with SDK 6.0.202 on MacOS x86 with Rider test runner)
Problem appears to be at
|
worker trampolineHolder |> unfake |
When
Async.Parallelis handling cancellation (via the token or a computation throwing) with e.g. 2000 items outstanding, the current implementation as of FSharp.Core 6.0.4 is such that the non-tail-recursive impl can blow the stack.Repro steps
Known workarounds
batch the work with
Seq.ChunkBySize 1500, add an outerAsync.Parallelcall (distributing thedegreeOfParallelismover the constituent calls, or using the same value but internally constraining it withAsync.Throttle),Array.concatthe results.Related information
(1800 is the approx threshold with SDK 6.0.202 on MacOS x86 with Rider test runner)
Problem appears to be at
fsharp/src/FSharp.Core/async.fs
Line 1473 in 0006b73