File: FrameworkFork\System.ServiceModel\System\ServiceModel\Channels\CommunicationObject.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.Diagnostics;
using System.Diagnostics.Contracts;
using System.Runtime;
using System.Threading.Tasks;
using System.ServiceModel.Diagnostics;
 
namespace System.ServiceModel.Channels
{
    public abstract class CommunicationObject : ICommunicationObject, IAsyncCommunicationObject
    {
        private bool _aborted;
        private bool _closeCalled;
        private object _mutex;
        private bool _onClosingCalled;
        private bool _onClosedCalled;
        private bool _onOpeningCalled;
        private bool _onOpenedCalled;
        private bool _raisedClosed;
        private bool _raisedClosing;
        private bool _raisedFaulted;
        private bool _traceOpenAndClose;
        private object _eventSender;
        private CommunicationState _state;
 
        protected CommunicationObject()
            : this(new object())
        {
        }
 
        protected CommunicationObject(object mutex)
        {
            _mutex = mutex;
            _eventSender = this;
            _state = CommunicationState.Created;
        }
 
        internal bool Aborted
        {
            get { return _aborted; }
        }
 
        internal object EventSender
        {
            get { return _eventSender; }
            set { _eventSender = value; }
        }
 
        protected bool IsDisposed
        {
            get { return _state == CommunicationState.Closed; }
        }
 
        public CommunicationState State
        {
            get { return _state; }
        }
 
        protected object ThisLock
        {
            get { return _mutex; }
        }
 
        protected abstract TimeSpan DefaultCloseTimeout { get; }
        protected abstract TimeSpan DefaultOpenTimeout { get; }
 
        internal TimeSpan InternalCloseTimeout
        {
            get { return this.DefaultCloseTimeout; }
        }
 
        internal TimeSpan InternalOpenTimeout
        {
            get { return this.DefaultOpenTimeout; }
        }
 
        public event EventHandler Closed;
        public event EventHandler Closing;
        public event EventHandler Faulted;
        public event EventHandler Opened;
        public event EventHandler Opening;
 
        public void Abort()
        {
            lock (ThisLock)
            {
                if (_aborted || _state == CommunicationState.Closed)
                    return;
                _aborted = true;
 
                _state = CommunicationState.Closing;
            }
 
 
 
            try
            {
                OnClosing();
                if (!_onClosingCalled)
                    throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this);
 
                OnAbort();
 
                OnClosed();
                if (!_onClosedCalled)
                    throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this);
            }
            finally
            {
            }
        }
 
        public IAsyncResult BeginClose(AsyncCallback callback, object state)
        {
            return this.BeginClose(this.DefaultCloseTimeout, callback, state);
        }
 
        public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return CloseAsyncInternal(timeout).ToApm(callback, state);
        }
 
        public IAsyncResult BeginOpen(AsyncCallback callback, object state)
        {
            return this.BeginOpen(this.DefaultOpenTimeout, callback, state);
        }
 
        public IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return OpenAsyncInternal(timeout).ToApm(callback, state);
        }
 
        public void Close()
        {
            this.Close(this.DefaultCloseTimeout);
        }
 
        public void Close(TimeSpan timeout)
        {
            CloseAsyncInternal(timeout).WaitForCompletion();
        }
 
        private async Task CloseAsyncInternal(TimeSpan timeout)
        {
            await TaskHelpers.EnsureDefaultTaskScheduler();
            await ((IAsyncCommunicationObject)this).CloseAsync(timeout);
        }
 
        async Task IAsyncCommunicationObject.CloseAsync(TimeSpan timeout)
        {
            if (timeout < TimeSpan.Zero)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new ArgumentOutOfRangeException("timeout", SRServiceModel.SFxTimeoutOutOfRange0));
 
 
            CommunicationState originalState;
            lock (ThisLock)
            {
                originalState = _state;
                if (originalState != CommunicationState.Closed)
                    _state = CommunicationState.Closing;
 
                _closeCalled = true;
            }
 
            switch (originalState)
            {
                case CommunicationState.Created:
                case CommunicationState.Opening:
                case CommunicationState.Faulted:
                    this.Abort();
                    if (originalState == CommunicationState.Faulted)
                    {
                        throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
                    }
                    break;
 
                case CommunicationState.Opened:
                    {
                        bool throwing = true;
                        try
                        {
                            TimeoutHelper actualTimeout = new TimeoutHelper(timeout);
 
                            OnClosing();
                            if (!_onClosingCalled)
                                throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosing"), Guid.Empty, this);
 
                            await OnCloseAsync(actualTimeout.RemainingTime());
 
                            OnClosed();
                            if (!_onClosedCalled)
                                throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnClosed"), Guid.Empty, this);
 
                            throwing = false;
                        }
                        finally
                        {
                            if (throwing)
                            {
                                Abort();
                            }
                        }
                        break;
                    }
 
                case CommunicationState.Closing:
                case CommunicationState.Closed:
                    break;
 
                default:
                    throw Fx.AssertAndThrow("CommunicationObject.BeginClose: Unknown CommunicationState");
            }
        }
 
        private Exception CreateNotOpenException()
        {
            return new InvalidOperationException(string.Format(SRServiceModel.CommunicationObjectCannotBeUsed, this.GetCommunicationObjectType().ToString(), _state.ToString()));
        }
 
        private Exception CreateImmutableException()
        {
            return new InvalidOperationException(string.Format(SRServiceModel.CommunicationObjectCannotBeModifiedInState, this.GetCommunicationObjectType().ToString(), _state.ToString()));
        }
 
        private Exception CreateBaseClassMethodNotCalledException(string method)
        {
            return new InvalidOperationException(string.Format(SRServiceModel.CommunicationObjectBaseClassMethodNotCalled, this.GetCommunicationObjectType().ToString(), method));
        }
 
        internal Exception CreateClosedException()
        {
            if (!_closeCalled)
            {
                return CreateAbortedException();
            }
            else
            {
                return new ObjectDisposedException(this.GetCommunicationObjectType().ToString());
            }
        }
 
        internal Exception CreateFaultedException()
        {
            string message = string.Format(SRServiceModel.CommunicationObjectFaulted1, this.GetCommunicationObjectType().ToString());
            return new CommunicationObjectFaultedException(message);
        }
 
        internal Exception CreateAbortedException()
        {
            return new CommunicationObjectAbortedException(string.Format(SRServiceModel.CommunicationObjectAborted1, this.GetCommunicationObjectType().ToString()));
        }
 
        internal bool DoneReceivingInCurrentState()
        {
            this.ThrowPending();
 
            switch (_state)
            {
                case CommunicationState.Created:
                    throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
 
                case CommunicationState.Opening:
                    throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
 
                case CommunicationState.Opened:
                    return false;
 
                case CommunicationState.Closing:
                    return true;
 
                case CommunicationState.Closed:
                    return true;
 
                case CommunicationState.Faulted:
                    return true;
 
                default:
                    throw Fx.AssertAndThrow("DoneReceivingInCurrentState: Unknown CommunicationObject.state");
            }
        }
 
        public void EndClose(IAsyncResult result)
        {
            result.ToApmEnd();
        }
 
        public void EndOpen(IAsyncResult result)
        {
            result.ToApmEnd();
        }
 
        protected void Fault()
        {
            lock (ThisLock)
            {
                if (_state == CommunicationState.Closed || _state == CommunicationState.Closing)
                    return;
 
                if (_state == CommunicationState.Faulted)
                    return;
                _state = CommunicationState.Faulted;
            }
 
            OnFaulted();
        }
 
        public void Open()
        {
            this.Open(this.DefaultOpenTimeout);
        }
 
        public void Open(TimeSpan timeout)
        {
            OpenAsyncInternal(timeout).WaitForCompletion();
        }
 
        private async Task OpenAsyncInternal(TimeSpan timeout)
        {
            await TaskHelpers.EnsureDefaultTaskScheduler();
            await ((IAsyncCommunicationObject)this).OpenAsync(timeout);
        }
 
        async Task IAsyncCommunicationObject.OpenAsync(TimeSpan timeout)
        {
            if (timeout < TimeSpan.Zero)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
                    new ArgumentOutOfRangeException("timeout", SRServiceModel.SFxTimeoutOutOfRange0));
 
            lock (ThisLock)
            {
                ThrowIfDisposedOrImmutable();
                _state = CommunicationState.Opening;
            }
 
            bool throwing = true;
            try
            {
                TimeoutHelper actualTimeout = new TimeoutHelper(timeout);
 
                OnOpening();
                if (!_onOpeningCalled)
                    throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnOpening"), Guid.Empty, this);
 
                TimeSpan remainingTime = actualTimeout.RemainingTime();
                await OnOpenAsync(remainingTime);
 
                OnOpened();
                if (!_onOpenedCalled)
                    throw TraceUtility.ThrowHelperError(this.CreateBaseClassMethodNotCalledException("OnOpened"), Guid.Empty, this);
 
                throwing = false;
            }
            finally
            {
                if (throwing)
                {
                    Fault();
                }
            }
        }
 
        protected virtual void OnClosed()
        {
            _onClosedCalled = true;
 
            lock (ThisLock)
            {
                if (_raisedClosed)
                    return;
                _raisedClosed = true;
                _state = CommunicationState.Closed;
            }
 
 
            EventHandler handler = Closed;
            if (handler != null)
            {
                try
                {
                    handler(_eventSender, EventArgs.Empty);
                }
                catch (Exception exception)
                {
                    if (Fx.IsFatal(exception))
                        throw;
 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
                }
            }
        }
 
        protected virtual void OnClosing()
        {
            _onClosingCalled = true;
 
            lock (ThisLock)
            {
                if (_raisedClosing)
                    return;
                _raisedClosing = true;
            }
 
 
            EventHandler handler = Closing;
            if (handler != null)
            {
                try
                {
                    handler(_eventSender, EventArgs.Empty);
                }
                catch (Exception exception)
                {
                    if (Fx.IsFatal(exception))
                        throw;
 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
                }
            }
        }
 
        protected virtual void OnFaulted()
        {
            lock (ThisLock)
            {
                if (_raisedFaulted)
                    return;
                _raisedFaulted = true;
            }
 
            EventHandler handler = Faulted;
            if (handler != null)
            {
                try
                {
                    handler(_eventSender, EventArgs.Empty);
                }
                catch (Exception exception)
                {
                    if (Fx.IsFatal(exception))
                        throw;
 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
                }
            }
        }
 
        protected virtual void OnOpened()
        {
            _onOpenedCalled = true;
 
            lock (ThisLock)
            {
                if (_aborted || _state != CommunicationState.Opening)
                    return;
                _state = CommunicationState.Opened;
            }
 
 
            EventHandler handler = Opened;
            if (handler != null)
            {
                try
                {
                    handler(_eventSender, EventArgs.Empty);
                }
                catch (Exception exception)
                {
                    if (Fx.IsFatal(exception))
                        throw;
 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
                }
            }
        }
 
        protected virtual void OnOpening()
        {
            _onOpeningCalled = true;
 
 
            EventHandler handler = Opening;
            if (handler != null)
            {
                try
                {
                    handler(_eventSender, EventArgs.Empty);
                }
                catch (Exception exception)
                {
                    if (Fx.IsFatal(exception))
                        throw;
 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperCallback(exception);
                }
            }
        }
 
        internal void ThrowIfFaulted()
        {
            this.ThrowPending();
 
            switch (_state)
            {
                case CommunicationState.Created:
                    break;
 
                case CommunicationState.Opening:
                    break;
 
                case CommunicationState.Opened:
                    break;
 
                case CommunicationState.Closing:
                    break;
 
                case CommunicationState.Closed:
                    break;
 
                case CommunicationState.Faulted:
                    throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
 
                default:
                    throw Fx.AssertAndThrow("ThrowIfFaulted: Unknown CommunicationObject.state");
            }
        }
 
        internal void ThrowIfAborted()
        {
            if (_aborted && !_closeCalled)
            {
                throw TraceUtility.ThrowHelperError(CreateAbortedException(), Guid.Empty, this);
            }
        }
 
        internal bool TraceOpenAndClose
        {
            get
            {
                return _traceOpenAndClose;
            }
            set
            {
                _traceOpenAndClose = value && DiagnosticUtility.ShouldUseActivity;
            }
        }
 
        internal void ThrowIfClosed()
        {
            ThrowPending();
 
            switch (_state)
            {
                case CommunicationState.Created:
                    break;
 
                case CommunicationState.Opening:
                    break;
 
                case CommunicationState.Opened:
                    break;
 
                case CommunicationState.Closing:
                    break;
 
                case CommunicationState.Closed:
                    throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
 
                case CommunicationState.Faulted:
                    throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
 
                default:
                    throw Fx.AssertAndThrow("ThrowIfClosed: Unknown CommunicationObject.state");
            }
        }
 
        protected virtual Type GetCommunicationObjectType()
        {
            return this.GetType();
        }
 
        protected internal void ThrowIfDisposed()
        {
            ThrowPending();
 
            switch (_state)
            {
                case CommunicationState.Created:
                    break;
 
                case CommunicationState.Opening:
                    break;
 
                case CommunicationState.Opened:
                    break;
 
                case CommunicationState.Closing:
                    throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
 
                case CommunicationState.Closed:
                    throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
 
                case CommunicationState.Faulted:
                    throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
 
                default:
                    throw Fx.AssertAndThrow("ThrowIfDisposed: Unknown CommunicationObject.state");
            }
        }
 
        internal void ThrowIfClosedOrOpened()
        {
            ThrowPending();
 
            switch (_state)
            {
                case CommunicationState.Created:
                    break;
 
                case CommunicationState.Opening:
                    break;
 
                case CommunicationState.Opened:
                    throw TraceUtility.ThrowHelperError(this.CreateImmutableException(), Guid.Empty, this);
 
                case CommunicationState.Closing:
                    throw TraceUtility.ThrowHelperError(this.CreateImmutableException(), Guid.Empty, this);
 
                case CommunicationState.Closed:
                    throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
 
                case CommunicationState.Faulted:
                    throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
 
                default:
                    throw Fx.AssertAndThrow("ThrowIfClosedOrOpened: Unknown CommunicationObject.state");
            }
        }
 
        protected internal void ThrowIfDisposedOrImmutable()
        {
            ThrowPending();
 
            switch (_state)
            {
                case CommunicationState.Created:
                    break;
 
                case CommunicationState.Opening:
                    throw TraceUtility.ThrowHelperError(this.CreateImmutableException(), Guid.Empty, this);
 
                case CommunicationState.Opened:
                    throw TraceUtility.ThrowHelperError(this.CreateImmutableException(), Guid.Empty, this);
 
                case CommunicationState.Closing:
                    throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
 
                case CommunicationState.Closed:
                    throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
 
                case CommunicationState.Faulted:
                    throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
 
                default:
                    throw Fx.AssertAndThrow("ThrowIfDisposedOrImmutable: Unknown CommunicationObject.state");
            }
        }
 
        protected internal void ThrowIfDisposedOrNotOpen()
        {
            ThrowPending();
 
            switch (_state)
            {
                case CommunicationState.Created:
                    throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
 
                case CommunicationState.Opening:
                    throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
 
                case CommunicationState.Opened:
                    break;
 
                case CommunicationState.Closing:
                    throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
 
                case CommunicationState.Closed:
                    throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
 
                case CommunicationState.Faulted:
                    throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
 
                default:
                    throw Fx.AssertAndThrow("ThrowIfDisposedOrNotOpen: Unknown CommunicationObject.state");
            }
        }
 
        internal void ThrowIfNotOpened()
        {
            if (_state == CommunicationState.Created || _state == CommunicationState.Opening)
                throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
        }
 
        internal void ThrowIfClosedOrNotOpen()
        {
            ThrowPending();
 
            switch (_state)
            {
                case CommunicationState.Created:
                    throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
 
                case CommunicationState.Opening:
                    throw TraceUtility.ThrowHelperError(this.CreateNotOpenException(), Guid.Empty, this);
 
                case CommunicationState.Opened:
                    break;
 
                case CommunicationState.Closing:
                    break;
 
                case CommunicationState.Closed:
                    throw TraceUtility.ThrowHelperError(this.CreateClosedException(), Guid.Empty, this);
 
                case CommunicationState.Faulted:
                    throw TraceUtility.ThrowHelperError(this.CreateFaultedException(), Guid.Empty, this);
 
                default:
                    throw Fx.AssertAndThrow("ThrowIfClosedOrNotOpen: Unknown CommunicationObject.state");
            }
        }
 
        internal void ThrowPending()
        {
        }
 
        //
        // State callbacks
        //
 
        protected abstract void OnAbort();
 
        protected abstract void OnClose(TimeSpan timeout);
 
        protected abstract IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state);
 
        protected abstract void OnEndClose(IAsyncResult result);
 
        protected abstract void OnOpen(TimeSpan timeout);
 
        protected abstract IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state);
 
        protected abstract void OnEndOpen(IAsyncResult result);
 
        internal protected virtual Task OnCloseAsync(TimeSpan timeout)
        {
            Contract.Requires(false, "OnCloseAsync needs to be implemented on derived classes");
            return TaskHelpers.CompletedTask();
        }
 
        internal protected virtual Task OnOpenAsync(TimeSpan timeout)
        {
            Contract.Requires(false, "OnOpenAsync needs to be implemented on derived classes");
            return TaskHelpers.CompletedTask();
        }
    }
 
    // This helper class exists to expose non-contract API's to other ServiceModel projects
    public static class CommunicationObjectInternal
    {
        public static void ThrowIfClosed(CommunicationObject communicationObject)
        {
            Contract.Assert(communicationObject != null);
            communicationObject.ThrowIfClosed();
        }
 
        public static void ThrowIfClosedOrOpened(CommunicationObject communicationObject)
        {
            Contract.Assert(communicationObject != null);
            communicationObject.ThrowIfClosedOrOpened();
        }
 
        public static void ThrowIfDisposedOrNotOpen(CommunicationObject communicationObject)
        {
            Contract.Assert(communicationObject != null);
            communicationObject.ThrowIfDisposedOrNotOpen();
        }
 
        public static void ThrowIfDisposed(CommunicationObject communicationObject)
        {
            Contract.Assert(communicationObject != null);
            communicationObject.ThrowIfDisposed();
        }
 
        public static TimeSpan GetInternalCloseTimeout(this CommunicationObject communicationObject)
        {
            return communicationObject.InternalCloseTimeout;
        }
 
        public static void OnClose(CommunicationObject communicationObject, TimeSpan timeout)
        {
            OnCloseAsyncInternal(communicationObject, timeout).WaitForCompletion();
        }
 
        public static IAsyncResult OnBeginClose(CommunicationObject communicationObject, TimeSpan timeout, AsyncCallback callback, object state)
        {
            return communicationObject.OnCloseAsync(timeout).ToApm(callback, state);
        }
 
        public static void OnEnd(IAsyncResult result)
        {
            result.ToApmEnd();
        }
 
        public static void OnOpen(CommunicationObject communicationObject, TimeSpan timeout)
        {
            OnOpenAsyncInternal(communicationObject, timeout).WaitForCompletion();
        }
 
        public static IAsyncResult OnBeginOpen(CommunicationObject communicationObject, TimeSpan timeout, AsyncCallback callback, object state)
        {
            return communicationObject.OnOpenAsync(timeout).ToApm(callback, state);
        }
 
        public static async Task OnCloseAsyncInternal(CommunicationObject communicationObject, TimeSpan timeout)
        {
            await TaskHelpers.EnsureDefaultTaskScheduler();
            await communicationObject.OnCloseAsync(timeout);
        }
 
        public static async Task OnOpenAsyncInternal(CommunicationObject communicationObject, TimeSpan timeout)
        {
            await TaskHelpers.EnsureDefaultTaskScheduler();
            await communicationObject.OnOpenAsync(timeout);
        }
    }
}