exception handling - Async.Catch doesnt work on OperationCanceledExceptions -
i use async.catch handle exceptions thrown async workflows:
work |> async.catch |> async.runsynchronously |> fun x -> match x | choice1of2 _ -> () // success | choice2of2 ex -> // failure, handle exception today noticed operationcanceledexceptions aren't handled async.catch. instead of getting choice async.catch exception keeps bubbling until hits me. expected following test red, it's green:
[<test>] let ``async.catch doesnt work on operationcancelledexceptions``() = use cancellationtokensource = new system.threading.cancellationtokensource(1000) let work = async { while true do! async.sleep 100 } (fun () -> work |> async.catch |> fun x -> async.runsynchronously (x, cancellationtoken=cancellationtokensource.token) |> ignore) |> should throw typeof<system.operationcanceledexception> evaluating exceptions async.catch + choices + matching , others using try/catch blocks doesn't seem right... following, way complicated. besides wonder use async.catch has, since have use try/catch block anyway...:
[<test>] let ``evaluating exceptions of async workflows``() = use cancellationtokensource = new system.threading.cancellationtokensource(1000) let work = async { while true do! async.sleep 100 } try work |> async.catch |> fun x -> async.runsynchronously (x, cancellationtoken=cancellationtokensource.token) |> fun x -> match x | choice1of2 result -> () // success, process result | choice2of2 ex -> () // failure, handle exception ex -> () // failure, handle exception here what's best way handle exceptions of async workflows? should dump async.catch , use try/catch blocks everywhere?
cancellation special kind of exception in asynchronous computations. when workflow cancelled, cancels child computations (the cancellation token shared). so, if handle cancellation ordinary exception, still cancel other parts of computation (and hard reason going on).
however, can write primitive starts workflow (and separates parent workflow) , handles cancellation in sub-workflow.
type async = static member startcatchcancellation(work, ?cancellationtoken) = async.fromcontinuations(fun (cont, econt, _) -> // when child cancelled, report operationcancelled // ordinary exception "error continuation" rather // using "cancellation continuation" let ccont e = econt e // start workflow using provided cancellation token async.startwithcontinuations( work, cont, econt, ccont, ?cancellationtoken=cancellationtoken) ) the usage similar async.catch, have pass cancellation token startcatchcancellation rather passing main runsynchronously (because workflow started separately):
let work = async { while true do! async.sleep 100 } let ct = new system.threading.cancellationtokensource(10000) async.startcatchcancellation(work, ct.token) |> async.catch |> async.runsynchronously |> printfn "%a"
Comments
Post a Comment