File: FrameworkFork\System.ServiceModel\Internals\System\Runtime\AsyncResult.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
 
namespace System.Runtime
{
    // AsyncResult starts acquired; Complete releases.
    [Fx.Tag.SynchronizationPrimitive(Fx.Tag.BlocksUsing.ManualResetEvent, SupportsAsync = true, ReleaseMethod = "Complete")]
    internal abstract class AsyncResult : IAsyncResult
    {
        private static AsyncCallback s_asyncCompletionWrapperCallback;
        private AsyncCallback _callback;
        private bool _completedSynchronously;
        private bool _endCalled;
        private Exception _exception;
        private bool _isCompleted;
        private AsyncCompletion _nextAsyncCompletion;
        private object _state;
        private Action _beforePrepareAsyncCompletionAction;
        private Func<IAsyncResult, bool> _checkSyncValidationFunc;
        [Fx.Tag.SynchronizationObject]
 
        private ManualResetEvent _manualResetEvent;
        [Fx.Tag.SynchronizationObject(Blocking = false)]
 
        private object _thisLock;
 
 
        protected AsyncResult(AsyncCallback callback, object state)
        {
            _callback = callback;
            _state = state;
            _thisLock = new object();
        }
 
        public object AsyncState
        {
            get
            {
                return _state;
            }
        }
 
        public WaitHandle AsyncWaitHandle
        {
            get
            {
                if (_manualResetEvent != null)
                {
                    return _manualResetEvent;
                }
 
                lock (ThisLock)
                {
                    if (_manualResetEvent == null)
                    {
                        _manualResetEvent = new ManualResetEvent(_isCompleted);
                    }
                }
 
                return _manualResetEvent;
            }
        }
 
        public bool CompletedSynchronously
        {
            get
            {
                return _completedSynchronously;
            }
        }
 
        public bool HasCallback
        {
            get
            {
                return _callback != null;
            }
        }
 
        public bool IsCompleted
        {
            get
            {
                return _isCompleted;
            }
        }
 
        // used in conjunction with PrepareAsyncCompletion to allow for finally blocks
        protected Action<AsyncResult, Exception> OnCompleting { get; set; }
 
        private object ThisLock
        {
            get
            {
                return _thisLock;
            }
        }
 
        // subclasses like TraceAsyncResult can use this to wrap the callback functionality in a scope
        protected Action<AsyncCallback, IAsyncResult> VirtualCallback
        {
            get;
            set;
        }
 
        protected void Complete(bool completedSynchronously)
        {
            if (_isCompleted)
            {
                throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.AsyncResultCompletedTwice(GetType())));
            }
 
 
 
            _completedSynchronously = completedSynchronously;
            if (OnCompleting != null)
            {
                // Allow exception replacement, like a catch/throw pattern.
                try
                {
                    OnCompleting(this, _exception);
                }
                catch (Exception exception)
                {
                    if (Fx.IsFatal(exception))
                    {
                        throw;
                    }
                    _exception = exception;
                }
            }
 
            if (completedSynchronously)
            {
                // If we completedSynchronously, then there's no chance that the manualResetEvent was created so
                // we don't need to worry about a race condition.
                Fx.Assert(_manualResetEvent == null, "No ManualResetEvent should be created for a synchronous AsyncResult.");
                _isCompleted = true;
            }
            else
            {
                lock (ThisLock)
                {
                    _isCompleted = true;
                    if (_manualResetEvent != null)
                    {
                        _manualResetEvent.Set();
                    }
                }
            }
 
            if (_callback != null)
            {
                try
                {
                    if (VirtualCallback != null)
                    {
                        VirtualCallback(_callback, this);
                    }
                    else
                    {
                        _callback(this);
                    }
                }
#pragma warning disable 1634
#pragma warning suppress 56500 // transferring exception to another thread
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
 
                    throw Fx.Exception.AsError(new CallbackException(InternalSR.AsyncCallbackThrewException, e));
                }
#pragma warning restore 1634
            }
        }
 
        protected void Complete(bool completedSynchronously, Exception exception)
        {
            _exception = exception;
            Complete(completedSynchronously);
        }
 
        private static void AsyncCompletionWrapperCallback(IAsyncResult result)
        {
            if (result == null)
            {
                throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.InvalidNullAsyncResult));
            }
            if (result.CompletedSynchronously)
            {
                return;
            }
 
            AsyncResult thisPtr = (AsyncResult)result.AsyncState;
            if (!thisPtr.OnContinueAsyncCompletion(result))
            {
                return;
            }
 
            AsyncCompletion callback = thisPtr.GetNextCompletion();
            if (callback == null)
            {
                ThrowInvalidAsyncResult(result);
            }
 
            bool completeSelf = false;
            Exception completionException = null;
            try
            {
                completeSelf = callback(result);
            }
            catch (Exception e)
            {
                if (Fx.IsFatal(e))
                {
                    throw;
                }
                completeSelf = true;
                completionException = e;
            }
 
            if (completeSelf)
            {
                thisPtr.Complete(false, completionException);
            }
        }
 
        // Note: this should be only derived by the TransactedAsyncResult
        protected virtual bool OnContinueAsyncCompletion(IAsyncResult result)
        {
            return true;
        }
 
        // Note: this should be used only by the TransactedAsyncResult
        protected void SetBeforePrepareAsyncCompletionAction(Action beforePrepareAsyncCompletionAction)
        {
            _beforePrepareAsyncCompletionAction = beforePrepareAsyncCompletionAction;
        }
 
        // Note: this should be used only by the TransactedAsyncResult
        protected void SetCheckSyncValidationFunc(Func<IAsyncResult, bool> checkSyncValidationFunc)
        {
            _checkSyncValidationFunc = checkSyncValidationFunc;
        }
 
        protected AsyncCallback PrepareAsyncCompletion(AsyncCompletion callback)
        {
            if (_beforePrepareAsyncCompletionAction != null)
            {
                _beforePrepareAsyncCompletionAction();
            }
 
            _nextAsyncCompletion = callback;
            if (AsyncResult.s_asyncCompletionWrapperCallback == null)
            {
                AsyncResult.s_asyncCompletionWrapperCallback = Fx.ThunkCallback(new AsyncCallback(AsyncCompletionWrapperCallback));
            }
            return AsyncResult.s_asyncCompletionWrapperCallback;
        }
 
        protected bool CheckSyncContinue(IAsyncResult result)
        {
            AsyncCompletion dummy;
            return TryContinueHelper(result, out dummy);
        }
 
        protected bool SyncContinue(IAsyncResult result)
        {
            AsyncCompletion callback;
            if (TryContinueHelper(result, out callback))
            {
                return callback(result);
            }
            else
            {
                return false;
            }
        }
 
        private bool TryContinueHelper(IAsyncResult result, out AsyncCompletion callback)
        {
            if (result == null)
            {
                throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.InvalidNullAsyncResult));
            }
 
            callback = null;
            if (_checkSyncValidationFunc != null)
            {
                if (!_checkSyncValidationFunc(result))
                {
                    return false;
                }
            }
            else if (!result.CompletedSynchronously)
            {
                return false;
            }
 
            callback = GetNextCompletion();
            if (callback == null)
            {
                ThrowInvalidAsyncResult("Only call Check/SyncContinue once per async operation (once per PrepareAsyncCompletion).");
            }
            return true;
        }
 
        private AsyncCompletion GetNextCompletion()
        {
            AsyncCompletion result = _nextAsyncCompletion;
            _nextAsyncCompletion = null;
            return result;
        }
 
        protected static void ThrowInvalidAsyncResult(IAsyncResult result)
        {
            throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.InvalidAsyncResultImplementation(result.GetType())));
        }
 
        protected static void ThrowInvalidAsyncResult(string debugText)
        {
            string message = InternalSR.InvalidAsyncResultImplementationGeneric;
            if (debugText != null)
            {
#if DEBUG
                message += " " + debugText;
#endif
            }
            throw Fx.Exception.AsError(new InvalidOperationException(message));
        }
 
        [Fx.Tag.Blocking(Conditional = "!asyncResult.isCompleted")]
        protected static TAsyncResult End<TAsyncResult>(IAsyncResult result)
            where TAsyncResult : AsyncResult
        {
            if (result == null)
            {
                throw Fx.Exception.ArgumentNull("result");
            }
 
            TAsyncResult asyncResult = result as TAsyncResult;
 
            if (asyncResult == null)
            {
                throw Fx.Exception.Argument("result", InternalSR.InvalidAsyncResult);
            }
 
            if (asyncResult._endCalled)
            {
                throw Fx.Exception.AsError(new InvalidOperationException(InternalSR.AsyncResultAlreadyEnded));
            }
 
 
            asyncResult._endCalled = true;
 
            if (!asyncResult._isCompleted)
            {
                asyncResult.AsyncWaitHandle.WaitOne();
            }
 
            if (asyncResult._manualResetEvent != null)
            {
                asyncResult._manualResetEvent.Dispose();
            }
 
            if (asyncResult._exception != null)
            {
                throw Fx.Exception.AsError(asyncResult._exception);
            }
 
            return asyncResult;
        }
 
        // can be utilized by subclasses to write core completion code for both the sync and async paths
        // in one location, signalling chainable synchronous completion with the boolean result,
        // and leveraging PrepareAsyncCompletion for conversion to an AsyncCallback.
        // NOTE: requires that "this" is passed in as the state object to the asynchronous sub-call being used with a completion routine.
        protected delegate bool AsyncCompletion(IAsyncResult result);
    }
}