FSharp.Control.AsyncSeq icon indicating copy to clipboard operation
FSharp.Control.AsyncSeq copied to clipboard

iterAsyncParallel and iterAsyncParallelThrottled may fail to cancel

Open thednaz opened this issue 5 years ago • 0 comments

Description

iterAsyncParallel and iterAsyncParallelThrottled may fail to cancel; this can be observed when exceptions are thrown from another Async in Async.Parallel.

Repro steps

Execute the following code snippet

let r = Random()
 
let handle x = async {
    do! Async.Sleep (r.Next(200))
    printfn "%A" x
}

let fakeAsync = async {    
    do! Async.Sleep 500
    return "hello"
}

let makeAsyncSeqBatch () =
    let rec loop() = asyncSeq {            
        let! batch =  fakeAsync |> Async.Catch
        match batch with
        | Choice1Of2 batch ->
          if (Seq.isEmpty batch) then
            do! Async.Sleep 500
            yield! loop()
          else
            yield batch
            yield! loop() 
        | Choice2Of2 err ->
             printfn "Problem getting batch: %A" err
    }
    
    loop()

let x = makeAsyncSeqBatch () |> AsyncSeq.concatSeq |> AsyncSeq.iterAsyncParallel handle
let exAsync = async {
    do! Async.Sleep 2000
    failwith "error"
}

[x; exAsync] |> Async.Parallel |> Async.Ignore |> Async.RunSynchronously

When exAsync throws, Async.Parallel will attempt to cancel the iteration of the AsyncSeq before it returns. iterAsyncParallel and iterAsyncParalleThrottled will sometimes not return and continue running forever. Changing to iterAsync always stops the iteration of the AsyncSeq reliably.

Known workarounds

Don't use iterAsyncParallel or iterAsyncParalleThrottled, use iterAsync instead.

Related information

  • Operating system Windows
  • Branch 2.0.21
  • .NET Runtime, CoreCLR or Mono Version .NET Core 3.1

thednaz avatar Nov 09 '20 19:11 thednaz