/// <summary> /// DoAsyncOperationOverLegacyBad shows an async operation that does not properly clean up references to the canceallation token. /// </summary> public Task <string> DoAsyncOperationOverLegacyBad(CancellationToken cancellationToken) { // The following TaskCompletionSource hasn't been creating with the TaskCreationOptions.RunContinuationsAsynchronously // option. This means that the calling code will resume in the OnCompleted callback. This has a couple of consequences // 1. It will extend the lifetime of these objects since they will be on the stack when user code is resumed. // 2. If the calling code blocks, it could *steal* the thread from the LegacyAsyncOperation. var tcs = new TaskCompletionSource <string>(); var operation = new LegacyAsyncOperation(); if (cancellationToken.CanBeCanceled) { // CancellationToken.Register returns a CancellationTokenRegistration that needs to be disposed. // If this isn't disposed, it will stay around in the CancellationTokenSource until the // backing CancellationTokenSource is disposed. cancellationToken.Register(state => { ((LegacyAsyncOperation)state).Cancel(); }, operation); } // Not removing the event handler can result in a memory leak // this object is referenced by the callback which itself isn't cleaned up until // the token is disposed or the registration is disposed. operation.Completed += OnCompleted; operation.Start(); return(tcs.Task); void OnCompleted(string result, bool cancelled) { if (cancelled) { tcs.TrySetCanceled(cancellationToken); } else { tcs.TrySetResult(result); } } }
public Task <string> DoAsyncOperationOverLegacy(CancellationToken cancellationToken) { var tcs = new TaskCompletionSource <string>(TaskCreationOptions.RunContinuationsAsynchronously); var operation = new LegacyAsyncOperation(); var registration = default(CancellationTokenRegistration); if (cancellationToken.CanBeCanceled) { registration = cancellationToken.Register(state => { ((LegacyAsyncOperation)state).Cancel(); }, operation); } operation.Completed += OnCompleted; operation.Start(); return(tcs.Task); void OnCompleted(string result, bool cancelled) { registration.Dispose(); operation.Completed -= OnCompleted; if (cancelled) { tcs.TrySetCanceled(cancellationToken); } else { tcs.TrySetResult(result); } } }