How do you catch CancellationToken.Register callback exceptions?
c#, cancellationtokensource, task-parallel-library
Solution
EDIT: So I re-read the doc at msdn, and it says "Any exception the delegate generates will be propagated out of this method call."
I'm not sure what it means by "propagated out of this method call", because even if I move the .Register() call into the try block the exception is still not caught.
What this means is that the caller of your cancellation callback (the code inside .NET Runtime) won't make an attempt to catch any exceptions you may throw there, so they will be propagated outside your callback, on whatever stack frame and synchronization context the callback was invoked on. This may crash the application, so you really should handle all non-fatal exceptions inside your callback. Think of it as of an event handler. After all, there may be multiple callbacks registered with `ct.Register()`, and each might throw. Which exception should have been propagated then?
So, such exception will not be captured and propagated into the "client" side of the token (i.e., to the code which calls `CancellationToken.ThrowIfCancellationRequested`).
Here's an alternative approach to throw `TimeoutException`, if you need to differentiate between user cancellation (e.g., a "Stop" button) and a timeout:
public async Task<int> Read( byte[] buffer, int? size=null,
CancellationToken userToken)
{
size = size ?? buffer.Length;
using( var cts = CancellationTokenSource.CreateLinkedTokenSource(userToken))
{
cts.CancelAfter( 1000 );
try
{
var t = stream.ReadAsync( buffer, 0, size.Value, cts.Token );
try
{
await t;
}
catch (OperationCanceledException ex)
{
if (ex.CancellationToken == cts.Token)
throw new TimeoutException("read timeout", ex);
throw;
}
return t.Result;
}
catch( Exception ex )
{
Debug.WriteLine( "exception" );
return 0;
}
}
}
Problem
I am using async I/O to communicate with an HID device, and I would like to throw a catchable exception when there is a timeout. I've got the following read method: ``` public async Task<int> Read( byte[] buffer, int? size=null ) { size = size ?? buffer.Length; using( var cts = new CancellationTokenSource() ) { cts.CancelAfter( 1000 ); cts.Token.Register( () => { throw new TimeoutException( "read timeout" ); }, true ); try { var t = stream.ReadAsync( buffer, 0, size.Value, cts.Token ); await t; return t.Result; } catch( Exception ex ) { Debug.WriteLine( "exception" ); return 0; } } } ``` The exception thrown from the Token's callback is not caught by any try/catch blocks and I'm not sure why. I assumed it would be thrown at the await, but it is not. Is there a way to catch this exception (or make it catchable by the caller of Read())? EDIT: So I re-read the doc at msdn, and it says "Any exception the delegate generates will be propagated out of this method call." I'm not sure what it means by "propagated out of this method call", because even if I move the .Register() call into the try block the exception is still not caught.