File: System\ServiceModel\Channels\ServiceChannel.cs
Web Access
Project: src\src\System.ServiceModel.Primitives\src\System.ServiceModel.Primitives.csproj (System.ServiceModel.Primitives)
// 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.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Runtime;
using System.Runtime.Diagnostics;
using System.ServiceModel.Description;
using System.ServiceModel.Diagnostics;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Security;
using System.Threading;
using System.Threading.Tasks;
 
namespace System.ServiceModel.Channels
{
    // This class is sealed because the constructor could call Abort, which is virtual
    // IAsyncDisposable is declared on ServiceChannel as placing it on IClientChannel along with IDisposable would be a breaking change. Any existing external implementations of
    // IClientChannel would be invalid as they don't implement IAsyncDisposable.
    internal sealed class ServiceChannel : CommunicationObject, IChannel, IClientChannel, IDuplexContextChannel, IOutputChannel, IRequestChannel, IServiceChannel, IAsyncDisposable
    {
        private int _activityCount = 0;
        private bool _allowInitializationUI = true;
        private bool _allowOutputBatching = false;
        private bool _autoClose = true;
        private CallOnceManager _autoDisplayUIManager;
        private CallOnceManager _autoOpenManager;
        private readonly ChannelDispatcher _channelDispatcher;
        private readonly bool _closeBinder = true;
        private bool _didInteractiveInitialization;
        private bool _doneReceiving;
        private EndpointDispatcher _endpointDispatcher;
        private bool _explicitlyOpened;
        private ExtensionCollection<IContextChannel> _extensions;
        private readonly SessionIdleManager _idleManager;
        private EndpointAddress _localAddress;
        private readonly bool _openBinder = false;
        private TimeSpan _operationTimeout;
        private object _proxy;
        private string _terminatingOperationName;
        private bool _hasChannelStartedAutoClosing;
        private bool _hasCleanedUpChannelCollections;
        private EventTraceActivity _eventActivity;
 
        private EventHandler<UnknownMessageReceivedEventArgs> _unknownMessageReceived;
 
        private ServiceChannel(IChannelBinder binder, MessageVersion messageVersion, IDefaultCommunicationTimeouts timeouts)
        {
            MessageVersion = messageVersion;
            Binder = binder ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(binder));
            IsReplyChannel = Binder.Channel is IReplyChannel;
 
            IChannel innerChannel = binder.Channel;
            HasSession = (innerChannel is ISessionChannel<IDuplexSession>) ||
                        (innerChannel is ISessionChannel<IInputSession>) ||
                        (innerChannel is ISessionChannel<IOutputSession>);
 
            IncrementActivity();
            _openBinder = (binder.Channel.State == CommunicationState.Created);
 
            _operationTimeout = timeouts.SendTimeout;
        }
 
        internal ServiceChannel(ServiceChannelFactory factory, IChannelBinder binder)
            : this(binder, factory.MessageVersion, factory)
        {
            Factory = factory;
            ClientRuntime = factory.ClientRuntime;
 
            SetupInnerChannelFaultHandler();
 
            DispatchRuntime dispatch = factory.ClientRuntime.DispatchRuntime;
            if (dispatch != null)
            {
                _autoClose = dispatch.AutomaticInputSessionShutdown;
            }
 
            factory.ChannelCreated(this);
        }
 
        internal ServiceChannel(IChannelBinder binder,
                                EndpointDispatcher endpointDispatcher,
                                ChannelDispatcher channelDispatcher,
                                SessionIdleManager idleManager)
            : this(binder, channelDispatcher.MessageVersion, channelDispatcher.DefaultCommunicationTimeouts)
        {
            _channelDispatcher = channelDispatcher;
            _endpointDispatcher = endpointDispatcher ?? throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull(nameof(endpointDispatcher));
            ClientRuntime = endpointDispatcher.DispatchRuntime.CallbackClientRuntime;
 
            SetupInnerChannelFaultHandler();
 
            _autoClose = endpointDispatcher.DispatchRuntime.AutomaticInputSessionShutdown;
            IsPending = true;
 
            IDefaultCommunicationTimeouts timeouts = channelDispatcher.DefaultCommunicationTimeouts;
            _idleManager = idleManager;
 
            if (!binder.HasSession)
            {
                _closeBinder = false;
            }
 
            if (_idleManager != null)
            {
                bool didIdleAbort;
                _idleManager.RegisterChannel(this, out didIdleAbort);
                if (didIdleAbort)
                {
                    Abort();
                }
            }
        }
 
        private CallOnceManager AutoOpenManager
        {
            get
            {
                if (!_explicitlyOpened && (_autoOpenManager == null))
                {
                    EnsureAutoOpenManagers();
                }
                return _autoOpenManager;
            }
        }
 
        private CallOnceManager AutoDisplayUIManager
        {
            get
            {
                if (!_explicitlyOpened && (_autoDisplayUIManager == null))
                {
                    EnsureAutoOpenManagers();
                }
                return _autoDisplayUIManager;
            }
        }
 
 
        internal EventTraceActivity EventActivity
        {
            get
            {
                if (_eventActivity == null)
                {
                    //Take the id on the thread so that we know the initiating operation.
                    _eventActivity = EventTraceActivity.GetFromThreadOrCreate();
                }
                return _eventActivity;
            }
        }
 
        internal bool CloseFactory { get; set; }
 
        protected override TimeSpan DefaultCloseTimeout
        {
            get { return CloseTimeout; }
        }
 
        protected override TimeSpan DefaultOpenTimeout
        {
            get { return OpenTimeout; }
        }
 
        internal DispatchRuntime DispatchRuntime
        {
            get
            {
                if (_endpointDispatcher != null)
                {
                    return _endpointDispatcher.DispatchRuntime;
                }
                if (ClientRuntime != null)
                {
                    return ClientRuntime.DispatchRuntime;
                }
                return null;
            }
        }
 
        internal MessageVersion MessageVersion { get; }
 
        internal IChannelBinder Binder { get; }
 
        internal TimeSpan CloseTimeout
        {
            get
            {
                if (IsClient)
                {
                    return Factory.InternalCloseTimeout;
                }
                else
                {
                    return ChannelDispatcher.InternalCloseTimeout;
                }
            }
        }
 
        internal ChannelDispatcher ChannelDispatcher
        {
            get { return _channelDispatcher; }
        }
 
        internal EndpointDispatcher EndpointDispatcher
        {
            get { return _endpointDispatcher; }
            set
            {
                lock (ThisLock)
                {
                    _endpointDispatcher = value;
                    ClientRuntime = value.DispatchRuntime.CallbackClientRuntime;
                }
            }
        }
 
        internal ServiceChannelFactory Factory { get; }
 
        internal IChannel InnerChannel
        {
            get { return Binder.Channel; }
        }
 
        internal bool IsPending { get; set; }
 
        internal bool HasSession { get; }
 
        internal bool IsClient
        {
            get { return Factory != null; }
        }
 
        internal bool IsReplyChannel { get; }
 
        public Uri ListenUri
        {
            get
            {
                return Binder.ListenUri;
            }
        }
 
        public EndpointAddress LocalAddress
        {
            get
            {
                if (_localAddress == null)
                {
                    if (_endpointDispatcher != null)
                    {
                        _localAddress = _endpointDispatcher.EndpointAddress;
                    }
                    else
                    {
                        _localAddress = Binder.LocalAddress;
                    }
                }
                return _localAddress;
            }
        }
 
        internal TimeSpan OpenTimeout
        {
            get
            {
                if (IsClient)
                {
                    return Factory.InternalOpenTimeout;
                }
                else
                {
                    return ChannelDispatcher.InternalOpenTimeout;
                }
            }
        }
 
        public TimeSpan OperationTimeout
        {
            get { return _operationTimeout; }
            set
            {
                if (value < TimeSpan.Zero)
                {
                    string message = SRP.SFxTimeoutOutOfRange0;
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, message));
                }
                if (TimeoutHelper.IsTooLarge(value))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException(nameof(value), value, SRP.SFxTimeoutOutOfRangeTooBig));
                }
 
 
                _operationTimeout = value;
            }
        }
 
        internal object Proxy
        {
            get
            {
                object proxy = _proxy;
                if (proxy != null)
                {
                    return proxy;
                }
                else
                {
                    return this;
                }
            }
            set
            {
                _proxy = value;
                EventSender = value;   // need to use "proxy" as open/close event source
            }
        }
 
        internal ClientRuntime ClientRuntime { get; private set; }
 
        public EndpointAddress RemoteAddress
        {
            get
            {
                IOutputChannel outputChannel = InnerChannel as IOutputChannel;
                if (outputChannel != null)
                {
                    return outputChannel.RemoteAddress;
                }
 
                IRequestChannel requestChannel = InnerChannel as IRequestChannel;
                if (requestChannel != null)
                {
                    return requestChannel.RemoteAddress;
                }
 
                return null;
            }
        }
 
        private ProxyOperationRuntime UnhandledProxyOperation
        {
            get { return ClientRuntime.GetRuntime().UnhandledProxyOperation; }
        }
 
        public Uri Via
        {
            get
            {
                IOutputChannel outputChannel = InnerChannel as IOutputChannel;
                if (outputChannel != null)
                {
                    return outputChannel.Via;
                }
 
                IRequestChannel requestChannel = InnerChannel as IRequestChannel;
                if (requestChannel != null)
                {
                    return requestChannel.Via;
                }
 
                return null;
            }
        }
 
        internal InstanceContext InstanceContext { get; set; }
 
        private void SetupInnerChannelFaultHandler()
        {
            // need to call this method after this.binder and this.clientRuntime are set to prevent a potential 
            // NullReferenceException in this method or in the OnInnerChannelFaulted method; 
            // because this method accesses this.binder and OnInnerChannelFaulted accesses this.clientRuntime.
            Binder.Channel.Faulted += OnInnerChannelFaulted;
        }
 
        private void BindDuplexCallbacks()
        {
            IDuplexChannel duplexChannel = InnerChannel as IDuplexChannel;
            if ((duplexChannel != null) && (Factory != null) && (InstanceContext != null))
            {
                if (Binder is DuplexChannelBinder)
                {
                    ((DuplexChannelBinder)Binder).EnsurePumping();
                }
            }
        }
 
        internal bool CanCastTo(Type t)
        {
            if (t.IsAssignableFrom(typeof(IClientChannel)))
            {
                return true;
            }
 
            if (t.IsAssignableFrom(typeof(IDuplexContextChannel)))
            {
                return InnerChannel is IDuplexChannel;
            }
 
            if (t.IsAssignableFrom(typeof(IServiceChannel)))
            {
                return true;
            }
 
            return false;
        }
 
        internal void CompletedIOOperation()
        {
            if (_idleManager != null)
            {
                _idleManager.CompletedActivity();
            }
        }
 
        private void EnsureAutoOpenManagers()
        {
            lock (ThisLock)
            {
                if (!_explicitlyOpened)
                {
                    if (_autoOpenManager == null)
                    {
                        _autoOpenManager = new CallOnceManager(this, CallOpenOnce.Instance);
                    }
                    if (_autoDisplayUIManager == null)
                    {
                        _autoDisplayUIManager = new CallOnceManager(this, CallDisplayUIOnce.Instance);
                    }
                }
            }
        }
 
        private void EnsureDisplayUI()
        {
            CallOnceManager manager = AutoDisplayUIManager;
            if (manager != null)
            {
                manager.CallOnce(TimeSpan.MaxValue, null);
            }
            ThrowIfInitializationUINotCalled();
        }
 
        private IAsyncResult BeginEnsureDisplayUI(AsyncCallback callback, object state)
        {
            CallOnceManager manager = AutoDisplayUIManager;
            if (manager != null)
            {
                return manager.BeginCallOnce(TimeSpan.MaxValue, null, callback, state);
            }
            else
            {
                return new CallOnceCompletedAsyncResult(callback, state);
            }
        }
 
        private void EndEnsureDisplayUI(IAsyncResult result)
        {
            CallOnceManager manager = AutoDisplayUIManager;
            if (manager != null)
            {
                manager.EndCallOnce(result);
            }
            else
            {
                CallOnceCompletedAsyncResult.End(result);
            }
            ThrowIfInitializationUINotCalled();
        }
 
        private void EnsureOpened(TimeSpan timeout)
        {
            CallOnceManager manager = AutoOpenManager;
            if (manager != null)
            {
                manager.CallOnce(timeout, _autoDisplayUIManager);
            }
 
            ThrowIfOpening();
            ThrowIfDisposedOrNotOpen();
        }
 
        private IAsyncResult BeginEnsureOpened(TimeSpan timeout, AsyncCallback callback, object state)
        {
            CallOnceManager manager = AutoOpenManager;
            if (manager != null)
            {
                return manager.BeginCallOnce(timeout, _autoDisplayUIManager, callback, state);
            }
            else
            {
                ThrowIfOpening();
                ThrowIfDisposedOrNotOpen();
 
                return new CallOnceCompletedAsyncResult(callback, state);
            }
        }
 
        private void EndEnsureOpened(IAsyncResult result)
        {
            CallOnceManager manager = AutoOpenManager;
            if (manager != null)
            {
                manager.EndCallOnce(result);
            }
            else
            {
                CallOnceCompletedAsyncResult.End(result);
            }
        }
 
        public T GetProperty<T>() where T : class
        {
            IChannel innerChannel = InnerChannel;
            if (innerChannel != null)
            {
                return innerChannel.GetProperty<T>();
            }
 
            return null;
        }
 
        private void PrepareCall(ProxyOperationRuntime operation, bool oneway, ref ProxyRpc rpc)
        {
            OperationContext context = OperationContext.Current;
            // Doing a request reply callback when dispatching in-order deadlocks.
            // We never receive the reply until we finish processing the current message.
            if (!oneway)
            {
                DispatchRuntime dispatchBehavior = ClientRuntime.DispatchRuntime;
                if ((dispatchBehavior != null) && (dispatchBehavior.ConcurrencyMode == ConcurrencyMode.Single))
                {
                    if ((context != null) && (!context.IsUserContext) && (context.InternalServiceChannel == this))
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.SFxCallbackRequestReplyInOrder1, typeof(CallbackBehaviorAttribute).Name)));
                    }
                }
            }
 
            if ((State == CommunicationState.Created) && !operation.IsInitiating)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.SFxNonInitiatingOperation1, operation.Name)));
            }
 
            if (_terminatingOperationName != null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.Format(SRP.SFxTerminatingOperationAlreadyCalled1, _terminatingOperationName)));
            }
 
            if (_hasChannelStartedAutoClosing)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SRP.SFxClientOutputSessionAutoClosed));
            }
 
            operation.BeforeRequest(ref rpc);
            AddMessageProperties(rpc.Request, context);
            if (!oneway && !ClientRuntime.ManualAddressing && rpc.Request.Version.Addressing != AddressingVersion.None)
            {
                RequestReplyCorrelator.PrepareRequest(rpc.Request);
 
                MessageHeaders headers = rpc.Request.Headers;
                EndpointAddress localAddress = LocalAddress;
                EndpointAddress replyTo = headers.ReplyTo;
 
                if (replyTo == null)
                {
                    headers.ReplyTo = localAddress ?? EndpointAddress.AnonymousAddress;
                }
 
                if (IsClient && (localAddress != null) && !localAddress.IsAnonymous)
                {
                    Uri localUri = localAddress.Uri;
 
                    if ((replyTo != null) && !replyTo.IsAnonymous && (localUri != replyTo.Uri))
                    {
                        string text = SRP.Format(SRP.SFxRequestHasInvalidReplyToOnClient, replyTo.Uri, localUri);
                        Exception error = new InvalidOperationException(text);
                        throw TraceUtility.ThrowHelperError(error, rpc.Request);
                    }
 
                    EndpointAddress faultTo = headers.FaultTo;
                    if ((faultTo != null) && !faultTo.IsAnonymous && (localUri != faultTo.Uri))
                    {
                        string text = SRP.Format(SRP.SFxRequestHasInvalidFaultToOnClient, faultTo.Uri, localUri);
                        Exception error = new InvalidOperationException(text);
                        throw TraceUtility.ThrowHelperError(error, rpc.Request);
                    }
 
                    if (MessageVersion.Addressing == AddressingVersion.WSAddressingAugust2004)
                    {
                        EndpointAddress from = headers.From;
                        if ((from != null) && !from.IsAnonymous && (localUri != from.Uri))
                        {
                            string text = SRP.Format(SRP.SFxRequestHasInvalidFromOnClient, from.Uri, localUri);
                            Exception error = new InvalidOperationException(text);
                            throw TraceUtility.ThrowHelperError(error, rpc.Request);
                        }
                    }
                }
            }
 
            if (TraceUtility.MessageFlowTracingOnly)
            {
                //always set a new ID if none provided
            }
 
            if (rpc.Activity != null)
            {
                TraceUtility.SetActivity(rpc.Request, rpc.Activity);
                if (TraceUtility.ShouldPropagateActivity)
                {
                    TraceUtility.AddActivityHeader(rpc.Request);
                }
            }
            else if (TraceUtility.PropagateUserActivity || TraceUtility.ShouldPropagateActivity)
            {
                TraceUtility.AddAmbientActivityToMessage(rpc.Request);
            }
            operation.Parent.BeforeSendRequest(ref rpc);
 
 
            //Attach and transfer Activity
            if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
            {
                TraceClientOperationPrepared(ref rpc);
            }
 
            TraceUtility.MessageFlowAtMessageSent(rpc.Request, rpc.EventTraceActivity);
 
            if (MessageLogger.LogMessagesAtServiceLevel)
            {
                MessageLogger.LogMessage(ref rpc.Request, (oneway ? MessageLoggingSource.ServiceLevelSendDatagram : MessageLoggingSource.ServiceLevelSendRequest) | MessageLoggingSource.LastChance);
            }
        }
 
        private void TraceClientOperationPrepared(ref ProxyRpc rpc)
        {
            //Retrieve the old id on the RPC and attach the id on the message since we have a message id now.
            Guid previousId = rpc.EventTraceActivity != null ? rpc.EventTraceActivity.ActivityId : Guid.Empty;
            EventTraceActivity requestActivity = EventTraceActivityHelper.TryExtractActivity(rpc.Request);
            if (requestActivity == null)
            {
                requestActivity = EventTraceActivity.GetFromThreadOrCreate();
                EventTraceActivityHelper.TryAttachActivity(rpc.Request, requestActivity);
            }
            rpc.EventTraceActivity = requestActivity;
 
            if (WcfEventSource.Instance.ClientOperationPreparedIsEnabled())
            {
                string remoteAddress = string.Empty;
                if (RemoteAddress != null && RemoteAddress.Uri != null)
                {
                    remoteAddress = RemoteAddress.Uri.AbsoluteUri;
                }
                WcfEventSource.Instance.ClientOperationPrepared(rpc.EventTraceActivity,
                                            rpc.Action,
                                            ClientRuntime.ContractName,
                                            remoteAddress,
                                            previousId);
            }
        }
 
        internal static IAsyncResult BeginCall(ServiceChannel channel, ProxyOperationRuntime operation, object[] ins, AsyncCallback callback, object asyncState)
        {
            Fx.Assert(channel != null, "'channel' MUST NOT be NULL.");
            Fx.Assert(operation != null, "'operation' MUST NOT be NULL.");
            return channel.BeginCall(operation.Action, operation.IsOneWay, operation, ins, channel._operationTimeout, callback, asyncState);
        }
 
        internal IAsyncResult BeginCall(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, AsyncCallback callback, object asyncState)
        {
            return BeginCall(action, oneway, operation, ins, _operationTimeout, callback, asyncState);
        }
 
        internal IAsyncResult BeginCall(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, TimeSpan timeout, AsyncCallback callback, object asyncState)
        {
            ThrowIfDisallowedInitializationUI();
            ThrowIfIdleAborted(operation);
            ThrowIfIsConnectionOpened(operation);
 
            ServiceModelActivity serviceModelActivity = null;
 
            if (DiagnosticUtility.ShouldUseActivity)
            {
                serviceModelActivity = ServiceModelActivity.CreateActivity(true);
                callback = TraceUtility.WrapExecuteUserCodeAsyncCallback(callback);
            }
 
            SendAsyncResult result;
 
            using (var boundOperation = ServiceModelActivity.BoundOperation(serviceModelActivity, true))
            {
                if (DiagnosticUtility.ShouldUseActivity)
                {
                    ServiceModelActivity.Start(serviceModelActivity, SRP.Format(SRP.ActivityProcessAction, action), ActivityType.ProcessAction);
                }
 
                result = new SendAsyncResult(this, operation, action, ins, oneway, timeout, callback, asyncState);
                if (DiagnosticUtility.ShouldUseActivity)
                {
                    result._rpc.Activity = serviceModelActivity;
                }
 
                TraceServiceChannelCallStart(result._rpc.EventTraceActivity, false);
 
                result.Begin();
            }
 
            return result;
        }
 
        internal object Call(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, object[] outs)
        {
            return Call(action, oneway, operation, ins, outs, _operationTimeout);
        }
 
        internal object Call(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, object[] outs, TimeSpan timeout)
        {
            ThrowIfDisallowedInitializationUI();
            ThrowIfIdleAborted(operation);
            ThrowIfIsConnectionOpened(operation);
 
            ProxyRpc rpc = new ProxyRpc(this, operation, action, ins, timeout);
 
            TraceServiceChannelCallStart(rpc.EventTraceActivity, true);
 
            using (rpc.Activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null)
            {
                if (DiagnosticUtility.ShouldUseActivity)
                {
                    ServiceModelActivity.Start(rpc.Activity, SRP.Format(SRP.ActivityProcessAction, action), ActivityType.ProcessAction);
                }
 
                PrepareCall(operation, oneway, ref rpc);
 
                if (!_explicitlyOpened)
                {
                    EnsureDisplayUI();
                    EnsureOpened(rpc.TimeoutHelper.RemainingTime());
                }
                else
                {
                    ThrowIfOpening();
                    ThrowIfDisposedOrNotOpen();
                }
 
                try
                {
                    ConcurrencyBehavior.UnlockInstanceBeforeCallout(OperationContext.Current);
 
                    if (oneway)
                    {
                        Binder.Send(rpc.Request, rpc.TimeoutHelper.RemainingTime());
                    }
                    else
                    {
                        rpc.Reply = Binder.Request(rpc.Request, rpc.TimeoutHelper.RemainingTime());
 
                        if (rpc.Reply == null)
                        {
                            ThrowIfFaulted();
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SRP.SFxServerDidNotReply));
                        }
                    }
                }
                finally
                {
                    CompletedIOOperation();
                    CallOnceManager.SignalNextIfNonNull(_autoOpenManager);
                    ConcurrencyBehavior.LockInstanceAfterCallout(OperationContext.Current);
                }
 
                rpc.OutputParameters = outs;
                HandleReply(operation, ref rpc);
            }
            return rpc.ReturnValue;
        }
 
        internal object EndCall(string action, object[] outs, IAsyncResult result)
        {
            SendAsyncResult sendResult = result as SendAsyncResult;
            if (sendResult == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SRP.SFxInvalidCallbackIAsyncResult));
            }
 
            using (ServiceModelActivity rpcActivity = sendResult._rpc.Activity)
            {
                using (ServiceModelActivity.BoundOperation(rpcActivity, true))
                {
                    if (sendResult._rpc.Activity != null && DiagnosticUtility.ShouldUseActivity)
                    {
                        sendResult._rpc.Activity.Resume();
                    }
                    if (sendResult._rpc.Channel != this)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("result", SRP.AsyncEndCalledOnWrongChannel);
                    }
 
                    if (action != MessageHeaders.WildcardAction && action != sendResult._rpc.Action)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("result", SRP.AsyncEndCalledWithAnIAsyncResult);
                    }
 
                    SendAsyncResult.End(sendResult);
 
                    sendResult._rpc.OutputParameters = outs;
                    HandleReply(sendResult._rpc.Operation, ref sendResult._rpc);
 
                    if (sendResult._rpc.Activity != null)
                    {
                        sendResult._rpc.Activity = null;
                    }
                    return sendResult._rpc.ReturnValue;
                }
            }
        }
 
        internal void DecrementActivity()
        {
            int updatedActivityCount = Interlocked.Decrement(ref _activityCount);
 
            if (!((updatedActivityCount >= 0)))
            {
                throw Fx.AssertAndThrowFatal("ServiceChannel.DecrementActivity: (updatedActivityCount >= 0)");
            }
 
            if (updatedActivityCount == 0 && _autoClose)
            {
                try
                {
                    if (State == CommunicationState.Opened)
                    {
                        if (IsClient)
                        {
                            ISessionChannel<IDuplexSession> duplexSessionChannel = InnerChannel as ISessionChannel<IDuplexSession>;
                            if (duplexSessionChannel != null)
                            {
                                _hasChannelStartedAutoClosing = true;
                                duplexSessionChannel.Session.CloseOutputSession(CloseTimeout);
                            }
                        }
                        else
                        {
                            Close(CloseTimeout);
                        }
                    }
                }
                catch (CommunicationException)
                {
                }
                catch (TimeoutException e)
                {
                    if (WcfEventSource.Instance.CloseTimeoutIsEnabled())
                    {
                        WcfEventSource.Instance.CloseTimeout(e.Message);
                    }
                }
                catch (ObjectDisposedException)
                {
                }
                catch (InvalidOperationException)
                {
                }
            }
        }
 
        internal void FireUnknownMessageReceived(Message message)
        {
            EventHandler<UnknownMessageReceivedEventArgs> handler = _unknownMessageReceived;
            if (handler != null)
            {
                handler(_proxy, new UnknownMessageReceivedEventArgs(message));
            }
        }
 
        private TimeoutException GetOpenTimeoutException(TimeSpan timeout)
        {
            EndpointAddress address = RemoteAddress ?? LocalAddress;
            if (address != null)
            {
                return new TimeoutException(SRP.Format(SRP.TimeoutServiceChannelConcurrentOpen2, address, timeout));
            }
            else
            {
                return new TimeoutException(SRP.Format(SRP.TimeoutServiceChannelConcurrentOpen1, timeout));
            }
        }
 
        internal void HandleReceiveComplete(RequestContext context)
        {
            if (context == null && HasSession)
            {
                bool first;
                lock (ThisLock)
                {
                    first = !_doneReceiving;
                    _doneReceiving = true;
                }
 
                if (first)
                {
                    DispatchRuntime dispatchBehavior = ClientRuntime.DispatchRuntime;
                    DecrementActivity();
                }
            }
        }
 
        private void HandleReply(ProxyOperationRuntime operation, ref ProxyRpc rpc)
        {
            try
            {
                //set the ID after response
                if (TraceUtility.MessageFlowTracingOnly && rpc.ActivityId != Guid.Empty)
                {
                    DiagnosticTraceBase.ActivityId = rpc.ActivityId;
                }
 
                if (rpc.Reply != null)
                {
                    TraceUtility.MessageFlowAtMessageReceived(rpc.Reply, null, rpc.EventTraceActivity, false);
 
                    if (MessageLogger.LogMessagesAtServiceLevel)
                    {
                        MessageLogger.LogMessage(ref rpc.Reply, MessageLoggingSource.ServiceLevelReceiveReply | MessageLoggingSource.LastChance);
                    }
                    operation.Parent.AfterReceiveReply(ref rpc);
 
                    if ((operation.ReplyAction != MessageHeaders.WildcardAction) && !rpc.Reply.IsFault && rpc.Reply.Headers.Action != null)
                    {
                        if (String.CompareOrdinal(operation.ReplyAction, rpc.Reply.Headers.Action) != 0)
                        {
                            Exception error = new ProtocolException(SRP.Format(SRP.SFxReplyActionMismatch3,
                                                                                  operation.Name,
                                                                                  rpc.Reply.Headers.Action,
                                                                                  operation.ReplyAction));
                            TerminateIfNecessary(ref rpc);
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
                        }
                    }
                    if (operation.DeserializeReply && ClientRuntime.IsFault(ref rpc.Reply))
                    {
                        MessageFault fault = MessageFault.CreateFault(rpc.Reply, ClientRuntime.MaxFaultSize);
                        string action = rpc.Reply.Headers.Action;
                        if (action == rpc.Reply.Version.Addressing.DefaultFaultAction)
                        {
                            action = null;
                        }
                        ThrowIfFaultUnderstood(rpc.Reply, fault, action, rpc.Reply.Version, rpc.Channel.GetProperty<FaultConverter>());
                        FaultException fe = rpc.Operation.FaultFormatter.Deserialize(fault, action);
                        TerminateIfNecessary(ref rpc);
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(fe);
                    }
 
                    operation.AfterReply(ref rpc);
                }
            }
            finally
            {
                if (operation.SerializeRequest)
                {
                    rpc.Request.Close();
                }
 
                OperationContext operationContext = OperationContext.Current;
                bool consumed = ((rpc.Reply != null) && (rpc.Reply.State != MessageState.Created));
 
                if ((operationContext != null) && operationContext.IsUserContext)
                {
                    operationContext.SetClientReply(rpc.Reply, consumed);
                }
                else if (consumed)
                {
                    rpc.Reply.Close();
                }
 
                if (TraceUtility.MessageFlowTracingOnly)
                {
                    if (rpc.ActivityId != Guid.Empty)
                    {
                        //reset the ID as it was created internally - ensures each call is uniquely correlatable
                        DiagnosticTraceBase.ActivityId = Guid.Empty;
                        rpc.ActivityId = Guid.Empty;
                    }
                }
            }
            TerminateIfNecessary(ref rpc);
 
            if (WcfEventSource.Instance.ServiceChannelCallStopIsEnabled())
            {
                string remoteAddress = string.Empty;
                if (RemoteAddress != null && RemoteAddress.Uri != null)
                {
                    remoteAddress = RemoteAddress.Uri.AbsoluteUri;
                }
                WcfEventSource.Instance.ServiceChannelCallStop(rpc.EventTraceActivity, rpc.Action,
                                            ClientRuntime.ContractName,
                                            remoteAddress);
            }
        }
 
        private void TerminateIfNecessary(ref ProxyRpc rpc)
        {
            if (rpc.Operation.IsTerminating)
            {
                _terminatingOperationName = rpc.Operation.Name;
                TerminatingOperationBehavior.AfterReply(ref rpc);
            }
        }
 
        private void ThrowIfFaultUnderstood(Message reply, MessageFault fault, string action, MessageVersion version, FaultConverter faultConverter)
        {
            Exception exception;
            if (faultConverter != null && faultConverter.TryCreateException(reply, fault, out exception))
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(exception);
            }
 
            bool checkSender;
            bool checkReceiver;
            FaultCode code;
 
            if (version.Envelope == EnvelopeVersion.Soap11)
            {
                checkSender = true;
                checkReceiver = true;
                code = fault.Code;
            }
            else
            {
                checkSender = fault.Code.IsSenderFault;
                checkReceiver = fault.Code.IsReceiverFault;
                code = fault.Code.SubCode;
            }
 
            if (code == null)
            {
                return;
            }
 
            if (code.Namespace == null)
            {
                return;
            }
 
            if (checkSender)
            {
                if (string.Compare(code.Namespace, FaultCodeConstants.Namespaces.NetDispatch, StringComparison.Ordinal) == 0)
                {
                    if (string.Compare(code.Name, FaultCodeConstants.Codes.SessionTerminated, StringComparison.Ordinal) == 0)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new ChannelTerminatedException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text));
                    }
                }
 
                // throw SecurityAccessDeniedException explicitly
                if (string.Compare(code.Namespace, SecurityVersion.Default.HeaderNamespace.Value, StringComparison.Ordinal) == 0)
                {
                    if (string.Compare(code.Name, SecurityVersion.Default.FailedAuthenticationFaultCode.Value, StringComparison.Ordinal) == 0)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new SecurityAccessDeniedException(fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text));
                    }
                }
            }
 
            if (checkReceiver)
            {
                if (string.Compare(code.Namespace, FaultCodeConstants.Namespaces.NetDispatch, StringComparison.Ordinal) == 0)
                {
                    if (string.Compare(code.Name, FaultCodeConstants.Codes.InternalServiceFault, StringComparison.Ordinal) == 0)
                    {
                        if (HasSession)
                        {
                            Fault();
                        }
                        if (fault.HasDetail)
                        {
                            ExceptionDetail detail = fault.GetDetail<ExceptionDetail>();
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new FaultException<ExceptionDetail>(detail, fault.Reason, fault.Code, action));
                        }
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new FaultException(fault, action));
                    }
                    if (string.Compare(code.Name, FaultCodeConstants.Codes.DeserializationFailed, StringComparison.Ordinal) == 0)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new ProtocolException(
                            fault.Reason.GetMatchingTranslation(CultureInfo.CurrentCulture).Text));
                    }
                }
            }
        }
 
        private void ThrowIfIdleAborted(ProxyOperationRuntime operation)
        {
            if (_idleManager != null && _idleManager.DidIdleAbort)
            {
                string text = SRP.Format(SRP.SFxServiceChannelIdleAborted, operation.Name);
                Exception error = new CommunicationObjectAbortedException(text);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
            }
        }
 
        private void ThrowIfIsConnectionOpened(ProxyOperationRuntime operation)
        {
            if (operation.IsSessionOpenNotificationEnabled)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                    SRP.Format(SRP.SFxServiceChannelCannotBeCalledBecauseIsSessionOpenNotificationEnabled, operation.Name, "Action", OperationDescription.SessionOpenedAction, "Open")));
            }
        }
 
        private void ThrowIfInitializationUINotCalled()
        {
            if (!_didInteractiveInitialization && (ClientRuntime.InteractiveChannelInitializers.Count > 0))
            {
                IInteractiveChannelInitializer example = ClientRuntime.InteractiveChannelInitializers[0];
                string text = SRP.Format(SRP.SFxInitializationUINotCalled, example.GetType().ToString());
                Exception error = new InvalidOperationException(text);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
            }
        }
 
        private void ThrowIfDisallowedInitializationUI()
        {
            if (!_allowInitializationUI)
            {
                ThrowIfDisallowedInitializationUICore();
            }
        }
 
        private void ThrowIfDisallowedInitializationUICore()
        {
            if (ClientRuntime.InteractiveChannelInitializers.Count > 0)
            {
                IInteractiveChannelInitializer example = ClientRuntime.InteractiveChannelInitializers[0];
                string text = SRP.Format(SRP.SFxInitializationUIDisallowed, example.GetType().ToString());
                Exception error = new InvalidOperationException(text);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
            }
        }
 
        private void ThrowIfOpening()
        {
            if (State == CommunicationState.Opening)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.SFxCannotCallAutoOpenWhenExplicitOpenCalled));
            }
        }
 
        internal void IncrementActivity()
        {
            Interlocked.Increment(ref _activityCount);
        }
 
        private void OnInnerChannelFaulted(object sender, EventArgs e)
        {
            Fault();
 
            if (HasSession)
            {
                DispatchRuntime dispatchRuntime = ClientRuntime.DispatchRuntime;
            }
 
            if (_autoClose && !IsClient)
            {
                Abort();
            }
        }
 
        private void AddMessageProperties(Message message, OperationContext context)
        {
            if (_allowOutputBatching)
            {
                message.Properties.AllowOutputBatching = true;
            }
 
            if (context != null && context.InternalServiceChannel == this)
            {
                if (!context.OutgoingMessageVersion.IsMatch(message.Headers.MessageVersion))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                        SRP.Format(SRP.SFxVersionMismatchInOperationContextAndMessage2, context.OutgoingMessageVersion, message.Headers.MessageVersion)
                        ));
                }
 
                if (context.HasOutgoingMessageHeaders)
                {
                    message.Headers.CopyHeadersFrom(context.OutgoingMessageHeaders);
                }
 
                if (context.HasOutgoingMessageProperties)
                {
                    message.Properties.CopyProperties(context.OutgoingMessageProperties);
                }
            }
        }
 
        #region IChannel Members
        public void Send(Message message)
        {
            Send(message, OperationTimeout);
        }
 
        public void Send(Message message, TimeSpan timeout)
        {
            ProxyOperationRuntime operation = UnhandledProxyOperation;
            Call(message.Headers.Action, true, operation, new object[] { message }, Array.Empty<object>(), timeout);
        }
 
        public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state)
        {
            return BeginSend(message, OperationTimeout, callback, state);
        }
 
        public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)
        {
            ProxyOperationRuntime operation = UnhandledProxyOperation;
            return BeginCall(message.Headers.Action, true, operation, new object[] { message }, timeout, callback, state);
        }
 
        public void EndSend(IAsyncResult result)
        {
            EndCall(MessageHeaders.WildcardAction, Array.Empty<object>(), result);
        }
 
        public Message Request(Message message)
        {
            return Request(message, OperationTimeout);
        }
 
        public Message Request(Message message, TimeSpan timeout)
        {
            ProxyOperationRuntime operation = UnhandledProxyOperation;
            return (Message)Call(message.Headers.Action, false, operation, new object[] { message }, Array.Empty<object>(), timeout);
        }
 
        public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state)
        {
            return BeginRequest(message, OperationTimeout, callback, state);
        }
 
        public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state)
        {
            ProxyOperationRuntime operation = UnhandledProxyOperation;
            return BeginCall(message.Headers.Action, false, operation, new object[] { message }, timeout, callback, state);
        }
 
        public Message EndRequest(IAsyncResult result)
        {
            return (Message)EndCall(MessageHeaders.WildcardAction, Array.Empty<object>(), result);
        }
 
        protected override void OnAbort()
        {
            if (_idleManager != null)
            {
                _idleManager.CancelTimer();
            }
 
            Binder.Abort();
 
            if (Factory != null)
            {
                Factory.ChannelDisposed(this);
            }
 
            if (CloseFactory)
            {
                if (Factory != null)
                {
                    Factory.Abort();
                }
            }
 
            CleanupChannelCollections();
        }
 
        protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return CommunicationObjectInternal.OnBeginClose(this, timeout, callback, state);
        }
 
        protected override void OnEndClose(IAsyncResult result)
        {
            CommunicationObjectInternal.OnEnd(result);
        }
 
        protected override void OnClose(TimeSpan timeout)
        {
            CommunicationObjectInternal.OnClose(this, timeout);
        }
 
        protected internal override async Task OnCloseAsync(TimeSpan timeout)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
 
            if (_idleManager != null)
            {
                _idleManager.CancelTimer();
            }
 
            if (Factory != null)
            {
                Factory.ChannelDisposed(this);
            }
 
            if (_closeBinder)
            {
                await CloseOtherAsync(InnerChannel, timeoutHelper.RemainingTime());
            }
 
            if (CloseFactory)
            {
                await CloseOtherAsync(Factory, timeoutHelper.RemainingTime());
            }
 
            CleanupChannelCollections();
        }
 
        protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return CommunicationObjectInternal.OnBeginOpen(this, timeout, callback, state);
        }
 
        protected override void OnEndOpen(IAsyncResult result)
        {
            CommunicationObjectInternal.OnEnd(result);
        }
 
        protected override void OnOpen(TimeSpan timeout)
        {
            CommunicationObjectInternal.OnOpen(this, timeout);
        }
 
        protected internal override async Task OnOpenAsync(TimeSpan timeout)
        {
            ThrowIfDisallowedInitializationUI();
            ThrowIfInitializationUINotCalled();
 
            if (_autoOpenManager == null)
            {
                _explicitlyOpened = true;
            }
 
            TraceChannelOpenStarted();
 
            if (_openBinder)
            {
                await OpenOtherAsync(InnerChannel, timeout);
            }
 
            BindDuplexCallbacks();
            CompletedIOOperation();
 
            TraceChannelOpenCompleted();
        }
 
        private void CleanupChannelCollections()
        {
            if (!_hasCleanedUpChannelCollections)
            {
                lock (ThisLock)
                {
                    if (!_hasCleanedUpChannelCollections)
                    {
                        if (InstanceContext != null)
                        {
                            InstanceContext.OutgoingChannels.Remove((IChannel)_proxy);
                        }
 
                        _hasCleanedUpChannelCollections = true;
                    }
                }
            }
        }
 
        #endregion
 
        #region IClientChannel Members
 
        bool IDuplexContextChannel.AutomaticInputSessionShutdown
        {
            get { return _autoClose; }
            set { _autoClose = value; }
        }
 
        bool IClientChannel.AllowInitializationUI
        {
            get { return _allowInitializationUI; }
            set
            {
                ThrowIfDisposedOrImmutable();
                _allowInitializationUI = value;
            }
        }
 
        bool IContextChannel.AllowOutputBatching
        {
            get { return _allowOutputBatching; }
            set { _allowOutputBatching = value; }
        }
 
        bool IClientChannel.DidInteractiveInitialization
        {
            get { return _didInteractiveInitialization; }
        }
 
        IAsyncResult IDuplexContextChannel.BeginCloseOutputSession(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return GetDuplexSessionOrThrow().BeginCloseOutputSession(timeout, callback, state);
        }
 
        void IDuplexContextChannel.EndCloseOutputSession(IAsyncResult result)
        {
            GetDuplexSessionOrThrow().EndCloseOutputSession(result);
        }
 
        void IDuplexContextChannel.CloseOutputSession(TimeSpan timeout)
        {
            GetDuplexSessionOrThrow().CloseOutputSession(timeout);
        }
 
        private IDuplexSession GetDuplexSessionOrThrow()
        {
            if (InnerChannel == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.channelIsNotAvailable0));
            }
 
            ISessionChannel<IDuplexSession> duplexSessionChannel = InnerChannel as ISessionChannel<IDuplexSession>;
            if (duplexSessionChannel == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRP.channelDoesNotHaveADuplexSession0));
            }
 
            return duplexSessionChannel.Session;
        }
 
        IExtensionCollection<IContextChannel> IExtensibleObject<IContextChannel>.Extensions
        {
            get
            {
                lock (ThisLock)
                {
                    if (_extensions == null)
                    {
                        _extensions = new ExtensionCollection<IContextChannel>((IContextChannel)Proxy, ThisLock);
                    }
 
                    return _extensions;
                }
            }
        }
 
        InstanceContext IDuplexContextChannel.CallbackInstance
        {
            get { return InstanceContext; }
            set
            {
                lock (ThisLock)
                {
                    if (InstanceContext != null)
                    {
                        InstanceContext.OutgoingChannels.Remove((IChannel)_proxy);
                    }
 
                    InstanceContext = value;
 
                    if (InstanceContext != null)
                    {
                        InstanceContext.OutgoingChannels.Add((IChannel)_proxy);
                    }
                }
            }
        }
 
        IInputSession IContextChannel.InputSession
        {
            get
            {
                if (InnerChannel != null)
                {
                    ISessionChannel<IInputSession> inputSession = InnerChannel as ISessionChannel<IInputSession>;
                    if (inputSession != null)
                    {
                        return inputSession.Session;
                    }
 
                    ISessionChannel<IDuplexSession> duplexSession = InnerChannel as ISessionChannel<IDuplexSession>;
                    if (duplexSession != null)
                    {
                        return duplexSession.Session;
                    }
                }
 
                return null;
            }
        }
 
        IOutputSession IContextChannel.OutputSession
        {
            get
            {
                if (InnerChannel != null)
                {
                    ISessionChannel<IOutputSession> outputSession = InnerChannel as ISessionChannel<IOutputSession>;
                    if (outputSession != null)
                    {
                        return outputSession.Session;
                    }
 
                    ISessionChannel<IDuplexSession> duplexSession = InnerChannel as ISessionChannel<IDuplexSession>;
                    if (duplexSession != null)
                    {
                        return duplexSession.Session;
                    }
                }
 
                return null;
            }
        }
 
        string IContextChannel.SessionId
        {
            get
            {
                if (InnerChannel != null)
                {
                    ISessionChannel<IInputSession> inputSession = InnerChannel as ISessionChannel<IInputSession>;
                    if (inputSession != null)
                    {
                        return inputSession.Session.Id;
                    }
 
                    ISessionChannel<IOutputSession> outputSession = InnerChannel as ISessionChannel<IOutputSession>;
                    if (outputSession != null)
                    {
                        return outputSession.Session.Id;
                    }
 
                    ISessionChannel<IDuplexSession> duplexSession = InnerChannel as ISessionChannel<IDuplexSession>;
                    if (duplexSession != null)
                    {
                        return duplexSession.Session.Id;
                    }
                }
 
                return null;
            }
        }
 
        event EventHandler<UnknownMessageReceivedEventArgs> IClientChannel.UnknownMessageReceived
        {
            add
            {
                lock (ThisLock)
                {
                    _unknownMessageReceived += value;
                }
            }
            remove
            {
                lock (ThisLock)
                {
                    _unknownMessageReceived -= value;
                }
            }
        }
 
        public void DisplayInitializationUI()
        {
            ThrowIfDisallowedInitializationUI();
 
            if (_autoDisplayUIManager == null)
            {
                _explicitlyOpened = true;
            }
 
            ClientRuntime.GetRuntime().DisplayInitializationUI(this);
            _didInteractiveInitialization = true;
        }
 
        public IAsyncResult BeginDisplayInitializationUI(AsyncCallback callback, object state)
        {
            ThrowIfDisallowedInitializationUI();
 
            if (_autoDisplayUIManager == null)
            {
                _explicitlyOpened = true;
            }
 
            return ClientRuntime.GetRuntime().BeginDisplayInitializationUI(this, callback, state);
        }
 
        public void EndDisplayInitializationUI(IAsyncResult result)
        {
            ClientRuntime.GetRuntime().EndDisplayInitializationUI(result);
            _didInteractiveInitialization = true;
        }
 
        void IDisposable.Dispose()
        {
            Close();
        }
 
        async ValueTask IAsyncDisposable.DisposeAsync()
        {
            try
            {
                // Only want to call Close if it is in the Opened state
                if (State == CommunicationState.Opened)
                {
                    await ((IAsyncCommunicationObject)this).CloseAsync(DefaultCloseTimeout);
                }
                // Anything not closed by this point should be aborted
                if (State != CommunicationState.Closed)
                {
                    Abort();
                }
            }
            catch (CommunicationException)
            {
                Abort();
            }
            catch (TimeoutException)
            {
                Abort();
            }
        }
 
        #endregion
 
        private void TraceChannelOpenStarted()
        {
            if (WcfEventSource.Instance.ClientChannelOpenStartIsEnabled() && _endpointDispatcher == null)
            {
                WcfEventSource.Instance.ClientChannelOpenStart(EventActivity);
            }
            else if (WcfEventSource.Instance.ServiceChannelOpenStartIsEnabled())
            {
                WcfEventSource.Instance.ServiceChannelOpenStart(EventActivity);
            }
        }
 
        private void TraceChannelOpenCompleted()
        {
            if (_endpointDispatcher == null && WcfEventSource.Instance.ClientChannelOpenStopIsEnabled())
            {
                WcfEventSource.Instance.ClientChannelOpenStop(EventActivity);
            }
            else if (WcfEventSource.Instance.ServiceChannelOpenStopIsEnabled())
            {
                WcfEventSource.Instance.ServiceChannelOpenStop(EventActivity);
            }
        }
 
        private static void TraceServiceChannelCallStart(EventTraceActivity eventTraceActivity, bool isSynchronous)
        {
            if (WcfEventSource.Instance.ServiceChannelCallStartIsEnabled())
            {
                if (isSynchronous)
                {
                    WcfEventSource.Instance.ServiceChannelCallStart(eventTraceActivity);
                }
                else
                {
                    WcfEventSource.Instance.ServiceChannelBeginCallStart(eventTraceActivity);
                }
            }
        }
 
        // Invariants for signalling the CallOnce manager.
        //
        // 1) If a Call, BeginCall, or EndCall on the channel throws,
        //    the manager will SignalNext itself.
        // 2) If a Waiter times out, it will SignalNext its manager
        //    once it is both timed out and signalled.
        // 3) Once Call or EndCall returns successfully, it guarantees
        //    that SignalNext will be called once the // next stage
        //    has sufficiently completed.
 
        internal class SendAsyncResult : TraceAsyncResult
        {
            private readonly bool _isOneWay;
            private readonly ProxyOperationRuntime _operation;
            internal ProxyRpc _rpc;
            private OperationContext _operationContext;
 
            private static AsyncCallback s_ensureInteractiveInitCallback = Fx.ThunkCallback(EnsureInteractiveInitCallback);
            private static AsyncCallback s_ensureOpenCallback = Fx.ThunkCallback(EnsureOpenCallback);
            private static AsyncCallback s_sendCallback = Fx.ThunkCallback(SendCallback);
 
            internal SendAsyncResult(ServiceChannel channel, ProxyOperationRuntime operation,
                                     string action, object[] inputParameters, bool isOneWay, TimeSpan timeout,
                                     AsyncCallback userCallback, object userState)
                : base(userCallback, userState)
            {
                _rpc = new ProxyRpc(channel, operation, action, inputParameters, timeout);
                _isOneWay = isOneWay;
                _operation = operation;
                _operationContext = OperationContext.Current;
            }
 
            internal void Begin()
            {
                _rpc.Channel.PrepareCall(_operation, _isOneWay, ref _rpc);
 
                if (_rpc.Channel._explicitlyOpened)
                {
                    _rpc.Channel.ThrowIfOpening();
                    _rpc.Channel.ThrowIfDisposedOrNotOpen();
                    StartSend(true);
                }
                else
                {
                    StartEnsureInteractiveInit();
                }
            }
 
            private void StartEnsureInteractiveInit()
            {
                IAsyncResult result = _rpc.Channel.BeginEnsureDisplayUI(s_ensureInteractiveInitCallback, this);
 
                if (result.CompletedSynchronously)
                {
                    FinishEnsureInteractiveInit(result, true);
                }
            }
 
            private static void EnsureInteractiveInitCallback(IAsyncResult result)
            {
                if (!result.CompletedSynchronously)
                {
                    ((SendAsyncResult)result.AsyncState).FinishEnsureInteractiveInit(result, false);
                }
            }
 
            private void FinishEnsureInteractiveInit(IAsyncResult result, bool completedSynchronously)
            {
                Exception exception = null;
 
                try
                {
                    _rpc.Channel.EndEnsureDisplayUI(result);
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e) || completedSynchronously)
                    {
                        throw;
                    }
                    exception = e;
                }
 
                if (exception != null)
                {
                    CallComplete(completedSynchronously, exception);
                }
                else
                {
                    StartEnsureOpen(completedSynchronously);
                }
            }
 
            private void StartEnsureOpen(bool completedSynchronously)
            {
                TimeSpan timeout = _rpc.TimeoutHelper.RemainingTime();
                IAsyncResult result = null;
                Exception exception = null;
 
                try
                {
                    result = _rpc.Channel.BeginEnsureOpened(timeout, s_ensureOpenCallback, this);
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e) || completedSynchronously)
                    {
                        throw;
                    }
                    exception = e;
                }
 
                if (exception != null)
                {
                    CallComplete(completedSynchronously, exception);
                }
                else if (result.CompletedSynchronously)
                {
                    FinishEnsureOpen(result, completedSynchronously);
                }
            }
 
            private static void EnsureOpenCallback(IAsyncResult result)
            {
                if (!result.CompletedSynchronously)
                {
                    ((SendAsyncResult)result.AsyncState).FinishEnsureOpen(result, false);
                }
            }
 
            private void FinishEnsureOpen(IAsyncResult result, bool completedSynchronously)
            {
                Exception exception = null;
                using (ServiceModelActivity.BoundOperation(_rpc.Activity))
                {
                    try
                    {
                        _rpc.Channel.EndEnsureOpened(result);
                    }
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e) || completedSynchronously)
                        {
                            throw;
                        }
                        exception = e;
                    }
 
                    if (exception != null)
                    {
                        CallComplete(completedSynchronously, exception);
                    }
                    else
                    {
                        StartSend(completedSynchronously);
                    }
                }
            }
 
            private void StartSend(bool completedSynchronously)
            {
                TimeSpan timeout = _rpc.TimeoutHelper.RemainingTime();
                IAsyncResult result = null;
                Exception exception = null;
 
                try
                {
                    ConcurrencyBehavior.UnlockInstanceBeforeCallout(_operationContext);
 
                    if (_isOneWay)
                    {
                        result = _rpc.Channel.Binder.BeginSend(_rpc.Request, timeout, s_sendCallback, this);
                    }
                    else
                    {
                        result = _rpc.Channel.Binder.BeginRequest(_rpc.Request, timeout, s_sendCallback, this);
                    }
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
                    if (completedSynchronously)
                    {
                        ConcurrencyBehavior.LockInstanceAfterCallout(_operationContext);
                        throw;
                    }
                    exception = e;
                }
                finally
                {
                    CallOnceManager.SignalNextIfNonNull(_rpc.Channel._autoOpenManager);
                }
 
                if (exception != null)
                {
                    CallComplete(completedSynchronously, exception);
                }
                else if (result.CompletedSynchronously)
                {
                    FinishSend(result, completedSynchronously);
                }
            }
 
            private static void SendCallback(IAsyncResult result)
            {
                if (!result.CompletedSynchronously)
                {
                    ((SendAsyncResult)result.AsyncState).FinishSend(result, false);
                }
            }
 
            private void FinishSend(IAsyncResult result, bool completedSynchronously)
            {
                Exception exception = null;
 
                try
                {
                    if (_isOneWay)
                    {
                        _rpc.Channel.Binder.EndSend(result);
                    }
                    else
                    {
                        _rpc.Reply = _rpc.Channel.Binder.EndRequest(result);
 
                        if (_rpc.Reply == null)
                        {
                            _rpc.Channel.ThrowIfFaulted();
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SRP.SFxServerDidNotReply));
                        }
                    }
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
                    if (completedSynchronously)
                    {
                        ConcurrencyBehavior.LockInstanceAfterCallout(_operationContext);
                        throw;
                    }
                    exception = e;
                }
 
                CallComplete(completedSynchronously, exception);
            }
 
            private void CallComplete(bool completedSynchronously, Exception exception)
            {
                _rpc.Channel.CompletedIOOperation();
                Complete(completedSynchronously, exception);
            }
 
            public static void End(SendAsyncResult result)
            {
                try
                {
                    AsyncResult.End<SendAsyncResult>(result);
                }
                finally
                {
                    ConcurrencyBehavior.LockInstanceAfterCallout(result._operationContext);
                }
            }
        }
 
        internal interface ICallOnce
        {
            void Call(ServiceChannel channel, TimeSpan timeout);
            IAsyncResult BeginCall(ServiceChannel channel, TimeSpan timeout, AsyncCallback callback, object state);
            void EndCall(ServiceChannel channel, IAsyncResult result);
        }
 
        internal class CallDisplayUIOnce : ICallOnce
        {
            private static CallDisplayUIOnce s_instance;
 
            internal static CallDisplayUIOnce Instance
            {
                get
                {
                    if (s_instance == null)
                    {
                        s_instance = new CallDisplayUIOnce();
                    }
                    return s_instance;
                }
            }
            [Conditional("DEBUG")]
 
            private void ValidateTimeoutIsMaxValue(TimeSpan timeout)
            {
                if (timeout != TimeSpan.MaxValue)
                {
                    Fx.Assert("non-MaxValue timeout for displaying interactive initialization UI");
                }
            }
 
            void ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
            {
                ValidateTimeoutIsMaxValue(timeout);
                channel.DisplayInitializationUI();
            }
 
            IAsyncResult ICallOnce.BeginCall(ServiceChannel channel, TimeSpan timeout, AsyncCallback callback, object state)
            {
                ValidateTimeoutIsMaxValue(timeout);
                return channel.BeginDisplayInitializationUI(callback, state);
            }
 
            void ICallOnce.EndCall(ServiceChannel channel, IAsyncResult result)
            {
                channel.EndDisplayInitializationUI(result);
            }
        }
 
        internal class CallOpenOnce : ICallOnce
        {
            private static CallOpenOnce s_instance;
 
            internal static CallOpenOnce Instance
            {
                get
                {
                    if (s_instance == null)
                    {
                        s_instance = new CallOpenOnce();
                    }
                    return s_instance;
                }
            }
 
            void ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
            {
                channel.Open(timeout);
            }
 
            IAsyncResult ICallOnce.BeginCall(ServiceChannel channel, TimeSpan timeout, AsyncCallback callback, object state)
            {
                return channel.BeginOpen(timeout, callback, state);
            }
 
            void ICallOnce.EndCall(ServiceChannel channel, IAsyncResult result)
            {
                channel.EndOpen(result);
            }
        }
 
        internal class CallOnceManager
        {
            private readonly ICallOnce _callOnce;
            private readonly ServiceChannel _channel;
            private bool _isFirst = true;
            private Queue<IWaiter> _queue;
 
            private static Action<object> s_signalWaiter = new Action<object>(SignalWaiter);
 
            internal CallOnceManager(ServiceChannel channel, ICallOnce callOnce)
            {
                _callOnce = callOnce;
                _channel = channel;
                _queue = new Queue<IWaiter>();
 
                // Detect when the channel becomes unusable
                ChannelIsAvailable = true;
                _channel.Closing += (s, e) =>
                {
                    SetChannelUnavailable();
                };
 
                _channel.Faulted += (s, e) =>
                {
                    SetChannelUnavailable();
                };
            }
 
            private object ThisLock
            {
                get { return this; }
            }
 
            internal bool ChannelIsAvailable { get; private set; }
 
            // Called when the channel becomes unusable during the open
            // or waiting for queued waiters to complete.  This method
            // ensures any waiters' WaitHandles are set so they complete
            // rather than timing out.
            private void SetChannelUnavailable()
            {
                ChannelIsAvailable = false;
                SignalNext();
            }
 
            internal void CallOnce(TimeSpan timeout, CallOnceManager cascade)
            {
                SyncWaiter waiter = null;
                bool first = false;
 
                if (_queue != null)
                {
                    lock (ThisLock)
                    {
                        if (_queue != null)
                        {
                            if (_isFirst)
                            {
                                first = true;
                                _isFirst = false;
                            }
                            else
                            {
                                waiter = new SyncWaiter(this);
                                _queue.Enqueue(waiter);
                            }
                        }
                    }
                }
 
                SignalNextIfNonNull(cascade);
 
                if (first)
                {
                    bool throwing = true;
                    try
                    {
                        _callOnce.Call(_channel, timeout);
                        throwing = false;
                    }
                    finally
                    {
                        if (throwing)
                        {
                            SignalNext();
                        }
                    }
                }
                else if (waiter != null)
                {
                    waiter.Wait(timeout);
                }
            }
 
            internal IAsyncResult BeginCallOnce(TimeSpan timeout, CallOnceManager cascade,
                                                AsyncCallback callback, object state)
            {
                AsyncWaiter waiter = null;
                bool first = false;
 
                if (_queue != null)
                {
                    lock (ThisLock)
                    {
                        if (_queue != null)
                        {
                            if (_isFirst)
                            {
                                first = true;
                                _isFirst = false;
                            }
                            else
                            {
                                waiter = new AsyncWaiter(this, timeout, callback, state);
                                _queue.Enqueue(waiter);
                            }
                        }
                    }
                }
 
                SignalNextIfNonNull(cascade);
 
                if (first)
                {
                    bool throwing = true;
                    try
                    {
                        IAsyncResult result = _callOnce.BeginCall(_channel, timeout, callback, state);
                        throwing = false;
                        return result;
                    }
                    finally
                    {
                        if (throwing)
                        {
                            SignalNext();
                        }
                    }
                }
                else if (waiter != null)
                {
                    return waiter;
                }
                else
                {
                    return new CallOnceCompletedAsyncResult(callback, state);
                }
            }
 
            internal void EndCallOnce(IAsyncResult result)
            {
                if (result is CallOnceCompletedAsyncResult)
                {
                    CallOnceCompletedAsyncResult.End(result);
                }
                else if (result is AsyncWaiter)
                {
                    AsyncWaiter.End(result);
                }
                else
                {
                    bool throwing = true;
                    try
                    {
                        _callOnce.EndCall(_channel, result);
                        throwing = false;
                    }
                    finally
                    {
                        if (throwing)
                        {
                            SignalNext();
                        }
                    }
                }
            }
 
            static internal void SignalNextIfNonNull(CallOnceManager manager)
            {
                if (manager != null)
                {
                    manager.SignalNext();
                }
            }
 
            internal void SignalNext()
            {
                if (_queue == null)
                {
                    return;
                }
 
                IWaiter waiter = null;
 
                lock (ThisLock)
                {
                    if (_queue != null)
                    {
                        if (_queue.Count > 0)
                        {
                            waiter = _queue.Dequeue();
                        }
                        else
                        {
                            _queue = null;
                        }
                    }
                }
 
                if (waiter != null)
                {
                    ActionItem.Schedule(s_signalWaiter, waiter);
                }
            }
 
            private static void SignalWaiter(object state)
            {
                ((IWaiter)state).Signal();
            }
 
            private interface IWaiter
            {
                void Signal();
            }
 
            internal class SyncWaiter : IWaiter
            {
                private ManualResetEvent _wait = new ManualResetEvent(false);
                private CallOnceManager _manager;
                private bool _isTimedOut = false;
                private bool _isSignaled = false;
                private int _waitCount = 0;
 
                internal SyncWaiter(CallOnceManager manager)
                {
                    _manager = manager;
                }
 
                private bool ShouldSignalNext
                {
                    get { return (_isTimedOut || !_manager.ChannelIsAvailable) && _isSignaled; }
                }
 
                void IWaiter.Signal()
                {
                    _wait.Set();
                    CloseWaitHandle();
 
                    bool signalNext;
                    lock (_manager.ThisLock)
                    {
                        _isSignaled = true;
                        signalNext = ShouldSignalNext;
                    }
                    if (signalNext)
                    {
                        _manager.SignalNext();
                    }
                }
 
                internal bool Wait(TimeSpan timeout)
                {
                    try
                    {
                        // The wait can be ended by success, a timeout, or the
                        // channel becoming unavailable for further use
                        bool timedOut = !TimeoutHelper.WaitOne(_wait, timeout);
                        if (timedOut || !_manager.ChannelIsAvailable)
                        {
                            bool signalNext;
                            lock (_manager.ThisLock)
                            {
                                _isTimedOut = timedOut;
                                signalNext = ShouldSignalNext;
                            }
                            if (signalNext)
                            {
                                _manager.SignalNext();
                            }
                        }
                    }
                    finally
                    {
                        CloseWaitHandle();
                    }
 
                    return !_isTimedOut;
                }
 
                private void CloseWaitHandle()
                {
                    if (Interlocked.Increment(ref _waitCount) == 2)
                    {
                        _wait.Dispose();
                    }
                }
            }
 
            internal class AsyncWaiter : AsyncResult, IWaiter
            {
                private static TimerCallback s_timeoutCallback = new TimerCallback(Fx.ThunkCallback<object>(TimeoutCallback));
 
                private CallOnceManager _manager;
                private TimeSpan _timeout;
                private Timer _timer;
 
                internal AsyncWaiter(CallOnceManager manager, TimeSpan timeout,
                                     AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    _manager = manager;
                    _timeout = timeout;
 
                    if (timeout != TimeSpan.MaxValue)
                    {
                        _timer = new Timer(s_timeoutCallback, this, timeout, Timeout.InfiniteTimeSpan);
                    }
                }
 
                internal static void End(IAsyncResult result)
                {
                    AsyncResult.End<AsyncWaiter>(result);
                }
 
                void IWaiter.Signal()
                {
                    if ((_timer == null) || _timer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan))
                    {
                        Complete(false);
                        _manager._channel.Closed -= OnClosed;
                    }
                    else
                    {
                        _manager.SignalNext();
                    }
                }
 
                private void OnClosed(object sender, EventArgs e)
                {
                    if ((_timer == null) || _timer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan))
                    {
                        Complete(false, _manager._channel.CreateClosedException());
                    }
                }
 
                private static void TimeoutCallback(object state)
                {
                    AsyncWaiter _this = (AsyncWaiter)state;
                    _this.Complete(false, _this._manager._channel.GetOpenTimeoutException(_this._timeout));
                }
            }
        }
 
        private class CallOnceCompletedAsyncResult : AsyncResult
        {
            internal CallOnceCompletedAsyncResult(AsyncCallback callback, object state)
                : base(callback, state)
            {
                Complete(true);
            }
 
            static internal void End(IAsyncResult result)
            {
                AsyncResult.End<CallOnceCompletedAsyncResult>(result);
            }
        }
 
        internal class SessionIdleManager
        {
            private readonly IChannelBinder _binder;
            private ServiceChannel _channel;
            private readonly long _idleTicks;
            private long _lastActivity;
            private readonly Timer _timer;
            private static Action<object> s_timerCallback;
            private bool _didIdleAbort;
            private bool _isTimerCancelled;
            private object _thisLock;
 
            private SessionIdleManager(IChannelBinder binder, TimeSpan idle)
            {
                _binder = binder;
                _timer = new Timer(new TimerCallback(GetTimerCallback()), this, idle, Timeout.InfiniteTimeSpan);
                _idleTicks = Ticks.FromTimeSpan(idle);
                _thisLock = new Object();
            }
 
            internal static SessionIdleManager CreateIfNeeded(IChannelBinder binder, TimeSpan idle)
            {
                if (binder.HasSession && (idle != TimeSpan.MaxValue))
                {
                    return new SessionIdleManager(binder, idle);
                }
                else
                {
                    return null;
                }
            }
 
            internal bool DidIdleAbort
            {
                get
                {
                    lock (_thisLock)
                    {
                        return _didIdleAbort;
                    }
                }
            }
 
            internal void CancelTimer()
            {
                lock (_thisLock)
                {
                    _isTimerCancelled = true;
                    _timer.Change(Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
                }
            }
 
            internal void CompletedActivity()
            {
                Interlocked.Exchange(ref _lastActivity, Ticks.Now);
            }
 
            internal void RegisterChannel(ServiceChannel channel, out bool didIdleAbort)
            {
                lock (_thisLock)
                {
                    _channel = channel;
                    didIdleAbort = _didIdleAbort;
                }
            }
 
            private static Action<object> GetTimerCallback()
            {
                if (s_timerCallback == null)
                {
                    s_timerCallback = TimerCallback;
                }
                return s_timerCallback;
            }
 
            private static void TimerCallback(object state)
            {
                ((SessionIdleManager)state).TimerCallback();
            }
 
            private void TimerCallback()
            {
                // This reads lastActivity atomically without changing its value.
                // (it only sets if it is zero, and then it sets it to zero).
                long last = Interlocked.CompareExchange(ref _lastActivity, 0, 0);
                long abortTime = last + _idleTicks;
 
                lock (_thisLock)
                {
                    long ticksNow = Ticks.Now;
                    if (ticksNow > abortTime)
                    {
                        if (WcfEventSource.Instance.SessionIdleTimeoutIsEnabled())
                        {
                            string listenUri = string.Empty;
                            if (_binder.ListenUri != null)
                            {
                                listenUri = _binder.ListenUri.AbsoluteUri;
                            }
 
                            WcfEventSource.Instance.SessionIdleTimeout(listenUri);
                        }
 
                        _didIdleAbort = true;
                        if (_channel != null)
                        {
                            _channel.Abort();
                        }
                        else
                        {
                            _binder.Abort();
                        }
                    }
                    else
                    {
                        if (!_isTimerCancelled && _binder.Channel.State != CommunicationState.Faulted && _binder.Channel.State != CommunicationState.Closed)
                        {
                            _timer.Change(Ticks.ToTimeSpan(abortTime - ticksNow), Timeout.InfiniteTimeSpan);
                        }
                    }
                }
            }
        }
    }
}