Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Async Web Api Controller - How to handle cancellations?

I'm running an Owin Self hosted web Api, with typical controller like so:

    [HttpGet]
    [Route("refresh/{secId}")]
    [ResponseType(typeof(int))]
    public async Task<IHttpActionResult> RefreshElement(int secId)
    {
        var count = await _db.Refresh(secId);

        if (count == 0)
            return NotFound();

        return Ok(count);
    }

Assuming _db.Refresh() is long running (few secs) and sometimes throws an exception.

The problem i've managed to reproduce is this:

  • Request hits the method, _db.Refresh gets fired off
  • Request is cancelled (socket closed)

The result of _db.Refresh is no longer awaited - because I see when it returns an exception, it shows up via the TPL unobserved exception handling when the task is GCd...

Maybe because of such interactions the .net team changed the unhandled exception policy not to tear down processes (4.5 onwards i think)... so what's a good pattern out of this problem? Specifically for self hosted WebApi using OWIN - because i still log unobserved exceptions as FATAL :)

I can make _db.Refresh() take in a cancellation token, but how/when do i set a cancellation token for disconnects / cancels in self host webapi?

like image 857
Vivek Avatar asked May 08 '15 20:05

Vivek


People also ask

What is cancellation token in Web API?

Cancellation is a way to signal to an async task that it should stop doing whatever it happens to be doing. In . NET, this is done using a CancellationToken. An instance of a cancellation token is passed to the async task and the async task monitors the token to see if a cancellation has been requested.

How do I cancel my cancellation token?

Without a reference to the source you cannot cancel a token. That doesn't mean that you need the CancellationTokenSource that first spawned the token. When given a CancellationToken , you can create a new instance of the token source, assign it's token to the provided token, and cancel it.

How does cancellation token works?

A CancellationToken enables cooperative cancellation between threads, thread pool work items, or Task objects. You create a cancellation token by instantiating a CancellationTokenSource object, which manages cancellation tokens retrieved from its CancellationTokenSource.

How do I stop async process?

You can cancel an asynchronous operation after a period of time by using the CancellationTokenSource. CancelAfter method if you don't want to wait for the operation to finish.


1 Answers

Perhaps this does not fully answer your question, but I believe it answers this last part:

I can make _db.Refresh() take in a cancellation token, but how/when do i set a cancellation token for disconnects / cancels in self host webapi?

If you add a CancellationToken parameter to your action method, the framework will supply one when it calls it.

[HttpGet]
[Route("refresh/{secId}")]
[ResponseType(typeof(int))]
public async Task<IHttpActionResult> RefreshElement(
    int secId, CancellationToken cancellationToken)
{
    var count = await _db.Refresh(secId, cancellationToken);

    if (count == 0)
        return NotFound();

    return Ok(count);
}

Note however that there is still a small window of opportunity in which the client could disconnect before the action method exits, in which case your TPL unobserved exception will still occur.

like image 153
Timothy Shields Avatar answered Sep 21 '22 21:09

Timothy Shields