Skip to content

Commit 1d1eb4c

Browse files
authored
Fix Async.Parallel stack overflow on cancellation of ~2000 uncompleted (#13186)
* Fix Async.Parallel stack overflow on cancellation of ~2000 uncompleted computations * Update AsyncModule.fs * Update AsyncModule.fs
1 parent 36f01e3 commit 1d1eb4c

File tree

2 files changed

+18
-6
lines changed

2 files changed

+18
-6
lines changed

src/FSharp.Core/async.fs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,21 +1470,21 @@ type Async =
14701470
if innerCTS.Token.IsCancellationRequested then
14711471
let cexn = OperationCanceledException (innerCTS.Token)
14721472
recordFailure (Choice2Of2 cexn) |> unfake
1473-
worker trampolineHolder |> unfake
1473+
worker trampolineHolder
14741474
else
14751475
let taskCtxt =
14761476
AsyncActivation.Create
14771477
innerCTS.Token
14781478
trampolineHolder
1479-
(fun res -> recordSuccess j res |> unfake; worker trampolineHolder)
1480-
(fun edi -> recordFailure (Choice1Of2 edi) |> unfake; worker trampolineHolder)
1481-
(fun cexn -> recordFailure (Choice2Of2 cexn) |> unfake; worker trampolineHolder)
1479+
(fun res -> recordSuccess j res |> unfake; worker trampolineHolder |> fake)
1480+
(fun edi -> recordFailure (Choice1Of2 edi) |> unfake; worker trampolineHolder |> fake)
1481+
(fun cexn -> recordFailure (Choice2Of2 cexn) |> unfake; worker trampolineHolder |> fake)
14821482
computations.[j].Invoke taskCtxt |> unfake
1483-
fake()
1483+
14841484
for x = 1 to maxDegreeOfParallelism do
14851485
let trampolineHolder = TrampolineHolder()
14861486
trampolineHolder.QueueWorkItemWithTrampoline (fun () ->
1487-
worker trampolineHolder)
1487+
worker trampolineHolder |> fake)
14881488
|> unfake
14891489

14901490
fake()))

tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModule.fs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,3 +764,15 @@ type AsyncModule() =
764764
lock gate <| fun () -> printfn "Unhandled exception: %s" exn.Message
765765
lock gate <| fun () -> printfn "Semaphore count available: %i" semaphore.CurrentCount
766766
Assert.AreEqual(acquiredCount, releaseCount)
767+
768+
[<Fact>]
769+
member _.``Async.Parallel blows stack when cancelling many`` () =
770+
let gen (i : int) = async {
771+
if i <> 0 then do! Async.Sleep i
772+
else return failwith "OK"}
773+
let count = 3600
774+
let comps = Seq.init count gen
775+
let result = Async.Parallel(comps, 16) |> Async.Catch |> Async.RunSynchronously
776+
match result with
777+
| Choice2Of2 e -> Assert.AreEqual("OK", e.Message)
778+
| x -> failwithf "unexpected %A" x

0 commit comments

Comments
 (0)