I personally prefer to wrap the undo logic in my own method.
For example, taking into account the extension method, for example:
public static async Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken) { var tcs = new TaskCompletionSource<bool>(); using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs)) { if (task != await Task.WhenAny(task, tcs.Task)) { throw new OperationCanceledException(cancellationToken); } } return task.Result; }
You can simplify your method to:
public async Task<int> Read( byte[] buffer, int? size=null ) { size = size ?? buffer.Length; using( var cts = new CancellationTokenSource() ) { cts.CancelAfter( 1000 ); try { return await stream.ReadAsync( buffer, 0, size.Value, cts.Token ).WithCancellation(cts.Token); } catch( OperationCanceledException cancel ) { Debug.WriteLine( "cancelled" ); return 0; } catch( Exception ex ) { Debug.WriteLine( "exception" ); return 0; } } }
In this case, since your only goal is to time out, you can make it even easier:
public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout) { if (task != await Task.WhenAny(task, Task.Delay(timeout))) { throw new TimeoutException(); } return task.Result; // Task is guaranteed completed (WhenAny), so this won't block }
Then your method could be:
public async Task<int> Read( byte[] buffer, int? size=null ) { size = size ?? buffer.Length; try { return await stream.ReadAsync( buffer, 0, size.Value, cts.Token ).TimeoutAfter(TimeSpan.FromSeconds(1)); } catch( TimeoutException timeout ) { Debug.WriteLine( "Timed out" ); return 0; } catch( Exception ex ) { Debug.WriteLine( "exception" ); return 0; } }
Reed copsey
source share