F# handling Task cancellation

650 views Asked by At

I am struggling to understand why some code is never executed.

Consider this extension method:

type WebSocketListener with
  member x.AsyncAcceptWebSocket = async {
    try
        let! client = Async.AwaitTask <| x.AcceptWebSocketAsync Async.DefaultCancellationToken
        if(not (isNull client)) then
            return Some client
        else
            return None
    with
        | :? System.Threading.Tasks.TaskCanceledException -> 
        | :? AggregateException ->
            return None
  }

I know that AcceptSocketAsync throws a TaskCanceledException when the cancellation token is canceled. I have checked in a C# application. The idea is to return None.

However, that never happens. If I put a breakpoint in the last return None or even in the if expression it never stops there when the cancellation token has been cancelled. And I know it is awaiting in the Async.AwaitTask because if before cancelling, other client connects, it works and it stops in the breakpoints.

I am a little bit lost, why is the exception lost?

1

There are 1 answers

2
desco On BEST ANSWER

Cancellation uses a special path in F# asyncs - Async.AwaitTask will re-route execution of cancelled task to the cancellation continuation. If you want different behavior - you can always do this by manually:

type WebSocketListener with
  member x.AsyncAcceptWebSocket = async {
    let! ct = Async.CancellationToken
    return! Async.FromContinuations(fun (s, e, c) ->
        x.AcceptWebSocketAsync(ct).ContinueWith(fun (t: System.Threading.Tasks.Task<_>) -> 
            if t.IsFaulted then e t.Exception
            elif t.IsCanceled then s None // take success path in case of cancellation
            else 
            match t.Result with
            | null -> s None
            | x -> s (Some x)
        )
        |> ignore
    )
  }