Example #1
0
        /// <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);
                }
            }
        }
Example #2
0
        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);
                }
            }
        }