File: System\Runtime\OperationWithTimeoutAsyncResult.cs
Web Access
Project: src\src\System.ServiceModel.Federation\src\System.ServiceModel.Federation.csproj (System.ServiceModel.Federation)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.ServiceModel;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
 
namespace System.Runtime
{
    internal class OperationWithTimeoutAsyncResult : AsyncResult
    {
        private static readonly Action<object> s_scheduledCallback = new Action<object>(OnScheduled);
        private TimeoutHelper _timeoutHelper;
        private Action<TimeSpan> _operationWithTimeout;
 
        public OperationWithTimeoutAsyncResult(Action<TimeSpan> operationWithTimeout, TimeSpan timeout, AsyncCallback callback, object state)
            : base(callback, state)
        {
            _operationWithTimeout = operationWithTimeout;
            _timeoutHelper = new TimeoutHelper(timeout);
            _ = Task.Factory.StartNew(s_scheduledCallback, this, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
        }
 
        private static void OnScheduled(object state)
        {
            OperationWithTimeoutAsyncResult thisResult = (OperationWithTimeoutAsyncResult)state;
            Exception completionException = null;
            try
            {
                thisResult._operationWithTimeout(thisResult._timeoutHelper.RemainingTime());
            }
            catch (Exception e)
            {
                if (Fx.IsFatal(e))
                {
                    throw;
                }
 
                completionException = e;
            }
 
            thisResult.Complete(false, completionException);
        }
 
        public static void End(IAsyncResult result)
        {
            End<OperationWithTimeoutAsyncResult>(result);
        }
    }
 
    internal abstract class AsyncResult : IAsyncResult
    {
        private static AsyncCallback s_asyncCompletionWrapperCallback;
        private AsyncCallback _callback;
        private bool _endCalled;
        private Exception _exception;
        private AsyncCompletion _nextAsyncCompletion;
        private Action _beforePrepareAsyncCompletionAction;
        private Func<IAsyncResult, bool> _checkSyncValidationFunc;
        private ManualResetEvent _manualResetEvent;
        private object _thisLock;
 
 
        protected AsyncResult(AsyncCallback callback, object state)
        {
            _callback = callback;
            AsyncState = state;
            _thisLock = new object();
        }
 
        public object AsyncState { get; }
 
        public WaitHandle AsyncWaitHandle
        {
            get
            {
                if (_manualResetEvent != null)
                {
                    return _manualResetEvent;
                }
 
                lock (ThisLock)
                {
                    if (_manualResetEvent == null)
                    {
                        _manualResetEvent = new ManualResetEvent(IsCompleted);
                    }
                }
 
                return _manualResetEvent;
            }
        }
 
        public bool CompletedSynchronously { get; private set; }
 
        public bool HasCallback
        {
            get
            {
                return _callback != null;
            }
        }
 
        public bool IsCompleted { get; private set; }
 
        // 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 new InvalidOperationException(SR.Format(SR.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.
                Debug.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);
                    }
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
 
                    throw new CommunicationException(InternalSR.AsyncCallbackThrewException, e);
                }
            }
        }
 
        protected void Complete(bool completedSynchronously, Exception exception)
        {
            _exception = exception;
            Complete(completedSynchronously);
        }
 
        private static void AsyncCompletionWrapperCallback(IAsyncResult result)
        {
            if (result == null)
            {
                throw 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 = 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 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 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 new InvalidOperationException(message);
        }
 
        protected static TAsyncResult End<TAsyncResult>(IAsyncResult result)
            where TAsyncResult : AsyncResult
        {
            if (result == null)
            {
                throw new ArgumentNullException(nameof(result));
            }
 
            TAsyncResult asyncResult = result as TAsyncResult;
 
            if (asyncResult == null)
            {
                throw new ArgumentException(InternalSR.InvalidAsyncResult, nameof(result));
            }
 
            if (asyncResult._endCalled)
            {
                throw 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 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);
    }
}