File: System\ServiceModel\Channels\ServiceChannel.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.ServiceModel.Channels
{
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime;
    using System.Runtime.Diagnostics;
    using System.ServiceModel;
    using System.ServiceModel.Activation;
    using System.ServiceModel.Description;
    using System.ServiceModel.Diagnostics;
    using System.ServiceModel.Diagnostics.Application;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Security;
    using System.Threading;
 
    // This class is sealed because the constructor could call Abort, which is virtual
    sealed class ServiceChannel : CommunicationObject, IChannel, IClientChannel, IDuplexContextChannel, IOutputChannel, IRequestChannel, IServiceChannel
    {
 
        int activityCount = 0;
        bool allowInitializationUI = true;
        bool allowOutputBatching = false;
        bool autoClose = true;
        CallOnceManager autoDisplayUIManager;
        CallOnceManager autoOpenManager;
        readonly IChannelBinder binder;
        readonly ChannelDispatcher channelDispatcher;
        ClientRuntime clientRuntime;
        readonly bool closeBinder = true;
        bool closeFactory;
        bool didInteractiveInitialization;
        bool doneReceiving;
        EndpointDispatcher endpointDispatcher;
        bool explicitlyOpened;
        ExtensionCollection<IContextChannel> extensions;
        readonly ServiceChannelFactory factory;
        readonly bool hasSession;
        readonly SessionIdleManager idleManager;
        InstanceContext instanceContext;
        ServiceThrottle instanceContextServiceThrottle;
        bool isPending;
        readonly bool isReplyChannel;
        EndpointAddress localAddress;
        readonly MessageVersion messageVersion;
        readonly bool openBinder = false;
        TimeSpan operationTimeout;
        object proxy;
        ServiceThrottle serviceThrottle;
        string terminatingOperationName;
        InstanceContext wmiInstanceContext;
        bool hasChannelStartedAutoClosing;
        bool hasIncrementedBusyCount;
        bool hasCleanedUpChannelCollections;
        EventTraceActivity eventActivity;
 
        EventHandler<UnknownMessageReceivedEventArgs> unknownMessageReceived;
 
        ServiceChannel(IChannelBinder binder, MessageVersion messageVersion, IDefaultCommunicationTimeouts timeouts)
        {
            if (binder == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("binder");
            }
 
            this.messageVersion = messageVersion;
            this.binder = binder;
            this.isReplyChannel = this.binder.Channel is IReplyChannel;
 
            IChannel innerChannel = binder.Channel;
            this.hasSession = (innerChannel is ISessionChannel<IDuplexSession>) ||
                        (innerChannel is ISessionChannel<IInputSession>) ||
                        (innerChannel is ISessionChannel<IOutputSession>);
 
            this.IncrementActivity();
            this.openBinder = (binder.Channel.State == CommunicationState.Created);
 
            this.operationTimeout = timeouts.SendTimeout;
        }
 
        internal ServiceChannel(ServiceChannelFactory factory, IChannelBinder binder)
            : this(binder, factory.MessageVersion, factory)
        {
            this.factory = factory;
            this.clientRuntime = factory.ClientRuntime;
 
            this.SetupInnerChannelFaultHandler();
 
            DispatchRuntime dispatch = factory.ClientRuntime.DispatchRuntime;
            if (dispatch != null)
            {
                this.autoClose = dispatch.AutomaticInputSessionShutdown;
            }
 
            factory.ChannelCreated(this);
        }
 
        internal ServiceChannel(IChannelBinder binder,
                                EndpointDispatcher endpointDispatcher,
                                ChannelDispatcher channelDispatcher,
                                SessionIdleManager idleManager)
            : this(binder, channelDispatcher.MessageVersion, channelDispatcher.DefaultCommunicationTimeouts)
        {
            if (endpointDispatcher == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointDispatcher");
            }
 
            this.channelDispatcher = channelDispatcher;
            this.endpointDispatcher = endpointDispatcher;
            this.clientRuntime = endpointDispatcher.DispatchRuntime.CallbackClientRuntime;
 
            this.SetupInnerChannelFaultHandler();
 
            this.autoClose = endpointDispatcher.DispatchRuntime.AutomaticInputSessionShutdown;
            this.isPending = true;
 
            IDefaultCommunicationTimeouts timeouts = channelDispatcher.DefaultCommunicationTimeouts;
            this.idleManager = idleManager;
 
            if (!binder.HasSession)
                this.closeBinder = false;
 
            if (this.idleManager != null)
            {
                bool didIdleAbort;
                this.idleManager.RegisterChannel(this, out didIdleAbort);
                if (didIdleAbort)
                {
                    this.Abort();
                }
            }
        }
 
        CallOnceManager AutoOpenManager
        {
            get
            {
                if (!this.explicitlyOpened && (this.autoOpenManager == null))
                {
                    this.EnsureAutoOpenManagers();
                }
                return this.autoOpenManager;
            }
        }
 
        CallOnceManager AutoDisplayUIManager
        {
            get
            {
                if (!this.explicitlyOpened && (this.autoDisplayUIManager == null))
                {
                    this.EnsureAutoOpenManagers();
                }
                return this.autoDisplayUIManager;
            }
        }
 
 
        internal EventTraceActivity EventActivity
        {
            get
            {
                if (this.eventActivity == null)
                {
                    //Take the id on the thread so that we know the initiating operation.
                    this.eventActivity = EventTraceActivity.GetFromThreadOrCreate();
                }
                return this.eventActivity;
            }
        }
 
        internal bool CloseFactory
        {
            get { return this.closeFactory; }
            set { this.closeFactory = value; }
        }
 
        protected override TimeSpan DefaultCloseTimeout
        {
            get { return this.CloseTimeout; }
        }
 
        protected override TimeSpan DefaultOpenTimeout
        {
            get { return this.OpenTimeout; }
        }
 
        internal DispatchRuntime DispatchRuntime
        {
            get
            {
                if (this.endpointDispatcher != null)
                {
                    return this.endpointDispatcher.DispatchRuntime;
                }
                if (this.clientRuntime != null)
                {
                    return this.clientRuntime.DispatchRuntime;
                }
                return null;
            }
        }
 
        internal MessageVersion MessageVersion
        {
            get { return this.messageVersion; }
        }
 
        internal IChannelBinder Binder
        {
            get { return this.binder; }
        }
 
        internal TimeSpan CloseTimeout
        {
            get
            {
                if (this.IsClient)
                {
                    return factory.InternalCloseTimeout;
                }
                else
                {
                    return this.ChannelDispatcher.InternalCloseTimeout;
                }
            }
        }
 
        internal ChannelDispatcher ChannelDispatcher
        {
            get { return this.channelDispatcher; }
        }
 
        internal EndpointDispatcher EndpointDispatcher
        {
            get { return this.endpointDispatcher; }
            set
            {
                lock (this.ThisLock)
                {
                    this.endpointDispatcher = value;
                    this.clientRuntime = value.DispatchRuntime.CallbackClientRuntime;
                }
            }
        }
 
        internal ServiceChannelFactory Factory
        {
            get { return this.factory; }
        }
 
        internal IChannel InnerChannel
        {
            get { return this.binder.Channel; }
        }
 
        internal bool IsPending
        {
            get { return this.isPending; }
            set { this.isPending = value; }
        }
 
        internal bool HasSession
        {
            get { return hasSession; }
        }
 
        internal bool IsClient
        {
            get { return this.factory != null; }
        }
 
        internal bool IsReplyChannel
        {
            get { return this.isReplyChannel; }
        }
 
        public Uri ListenUri
        {
            get
            {
                return this.binder.ListenUri;
            }
        }
 
        public EndpointAddress LocalAddress
        {
            get
            {
                if (this.localAddress == null)
                {
                    if (this.endpointDispatcher != null)
                    {
                        this.localAddress = this.endpointDispatcher.EndpointAddress;
                    }
                    else
                    {
                        this.localAddress = this.binder.LocalAddress;
                    }
                }
                return this.localAddress;
            }
        }
 
        internal TimeSpan OpenTimeout
        {
            get
            {
                if (this.IsClient)
                {
                    return factory.InternalOpenTimeout;
                }
                else
                {
                    return this.ChannelDispatcher.InternalOpenTimeout;
                }
            }
        }
 
        public TimeSpan OperationTimeout
        {
            get { return this.operationTimeout; }
            set
            {
                if (value < TimeSpan.Zero)
                {
                    string message = SR.GetString(SR.SFxTimeoutOutOfRange0);
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, message));
                }
                if (TimeoutHelper.IsTooLarge(value))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", value, SR.GetString(SR.SFxTimeoutOutOfRangeTooBig)));
                }
 
 
                this.operationTimeout = value;
            }
        }
 
        internal object Proxy
        {
            get
            {
                object proxy = this.proxy;
                if (proxy != null)
                    return proxy;
                else
                    return this;
            }
            set
            {
                this.proxy = value;
                base.EventSender = value;   // need to use "proxy" as open/close event source
            }
        }
 
        internal ClientRuntime ClientRuntime
        {
            get { return this.clientRuntime; }
        }
 
        public EndpointAddress RemoteAddress
        {
            get
            {
                IOutputChannel outputChannel = this.InnerChannel as IOutputChannel;
                if (outputChannel != null)
                    return outputChannel.RemoteAddress;
 
                IRequestChannel requestChannel = this.InnerChannel as IRequestChannel;
                if (requestChannel != null)
                    return requestChannel.RemoteAddress;
 
                return null;
            }
        }
 
        ProxyOperationRuntime UnhandledProxyOperation
        {
            get { return this.ClientRuntime.GetRuntime().UnhandledProxyOperation; }
        }
 
        public Uri Via
        {
            get
            {
                IOutputChannel outputChannel = this.InnerChannel as IOutputChannel;
                if (outputChannel != null)
                    return outputChannel.Via;
 
                IRequestChannel requestChannel = this.InnerChannel as IRequestChannel;
                if (requestChannel != null)
                    return requestChannel.Via;
 
                return null;
            }
        }
 
        internal InstanceContext InstanceContext
        {
            get { return this.instanceContext; }
            set { this.instanceContext = value; }
        }
 
        internal ServiceThrottle InstanceContextServiceThrottle
        {
            get { return this.instanceContextServiceThrottle; }
            set { this.instanceContextServiceThrottle = value; }
        }
 
        internal ServiceThrottle ServiceThrottle
        {
            get { return this.serviceThrottle; }
            set
            {
                this.ThrowIfDisposed();
                this.serviceThrottle = value;
            }
        }
 
        internal InstanceContext WmiInstanceContext
        {
            get { return this.wmiInstanceContext; }
            set { this.wmiInstanceContext = value; }
        }
 
        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 acesses this.clientRuntime.
            this.binder.Channel.Faulted += OnInnerChannelFaulted;
        }
 
        void BindDuplexCallbacks()
        {
            IDuplexChannel duplexChannel = this.InnerChannel as IDuplexChannel;
            if ((duplexChannel != null) && (this.factory != null) && (this.instanceContext != null))
            {
                if (this.binder is DuplexChannelBinder)
                    ((DuplexChannelBinder)this.binder).EnsurePumping();
            }
        }
 
        internal bool CanCastTo(Type t)
        {
            if (t.IsAssignableFrom(typeof(IClientChannel)))
                return true;
 
            if (t.IsAssignableFrom(typeof(IDuplexContextChannel)))
                return this.InnerChannel is IDuplexChannel;
 
            if (t.IsAssignableFrom(typeof(IServiceChannel)))
                return true;
 
            return false;
        }
 
        internal void CompletedIOOperation()
        {
            if (this.idleManager != null)
            {
                this.idleManager.CompletedActivity();
            }
        }
 
        void EnsureAutoOpenManagers()
        {
            lock (this.ThisLock)
            {
                if (!this.explicitlyOpened)
                {
                    if (this.autoOpenManager == null)
                    {
                        this.autoOpenManager = new CallOnceManager(this, CallOpenOnce.Instance);
                    }
                    if (this.autoDisplayUIManager == null)
                    {
                        this.autoDisplayUIManager = new CallOnceManager(this, CallDisplayUIOnce.Instance);
                    }
                }
            }
        }
 
        void EnsureDisplayUI()
        {
            CallOnceManager manager = this.AutoDisplayUIManager;
            if (manager != null)
            {
                manager.CallOnce(TimeSpan.MaxValue, null);
            }
            this.ThrowIfInitializationUINotCalled();
        }
 
        IAsyncResult BeginEnsureDisplayUI(AsyncCallback callback, object state)
        {
            CallOnceManager manager = this.AutoDisplayUIManager;
            if (manager != null)
            {
                return manager.BeginCallOnce(TimeSpan.MaxValue, null, callback, state);
            }
            else
            {
                return new CallOnceCompletedAsyncResult(callback, state);
            }
        }
 
        void EndEnsureDisplayUI(IAsyncResult result)
        {
            CallOnceManager manager = this.AutoDisplayUIManager;
            if (manager != null)
            {
                manager.EndCallOnce(result);
            }
            else
            {
                CallOnceCompletedAsyncResult.End(result);
            }
            this.ThrowIfInitializationUINotCalled();
        }
 
        void EnsureOpened(TimeSpan timeout)
        {
            CallOnceManager manager = this.AutoOpenManager;
            if (manager != null)
            {
                manager.CallOnce(timeout, this.autoDisplayUIManager);
            }
 
            this.ThrowIfOpening();
            this.ThrowIfDisposedOrNotOpen();
        }
 
        IAsyncResult BeginEnsureOpened(TimeSpan timeout, AsyncCallback callback, object state)
        {
            CallOnceManager manager = this.AutoOpenManager;
            if (manager != null)
            {
                return manager.BeginCallOnce(timeout, this.autoDisplayUIManager, callback, state);
            }
            else
            {
                this.ThrowIfOpening();
                this.ThrowIfDisposedOrNotOpen();
 
                return new CallOnceCompletedAsyncResult(callback, state);
            }
        }
 
        void EndEnsureOpened(IAsyncResult result)
        {
            CallOnceManager manager = this.AutoOpenManager;
            if (manager != null)
            {
                manager.EndCallOnce(result);
            }
            else
            {
                CallOnceCompletedAsyncResult.End(result);
            }
        }
 
        public T GetProperty<T>() where T : class
        {
            IChannel innerChannel = this.InnerChannel;
            if (innerChannel != null)
                return innerChannel.GetProperty<T>();
            return null;
        }
 
        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 = this.ClientRuntime.DispatchRuntime;
                if ((dispatchBehavior != null) && (dispatchBehavior.ConcurrencyMode == ConcurrencyMode.Single))
                {
                    if ((context != null) && (!context.IsUserContext) && (context.InternalServiceChannel == this))
                    {
                        if (dispatchBehavior.IsOnServer)
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxCallbackRequestReplyInOrder1, typeof(ServiceBehaviorAttribute).Name)));
                        }
                        else
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxCallbackRequestReplyInOrder1, typeof(CallbackBehaviorAttribute).Name)));
                        }
                    }
                }
            }
 
            if ((this.State == CommunicationState.Created) && !operation.IsInitiating)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxNonInitiatingOperation1, operation.Name)));
            }
 
            if (this.terminatingOperationName != null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxTerminatingOperationAlreadyCalled1, this.terminatingOperationName)));
            }
 
            if (this.hasChannelStartedAutoClosing)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ProtocolException(SR.GetString(SR.SFxClientOutputSessionAutoClosed)));
            }
 
            operation.BeforeRequest(ref rpc);
            AddMessageProperties(rpc.Request, context);
            if (!oneway && !this.ClientRuntime.ManualAddressing && rpc.Request.Version.Addressing != AddressingVersion.None)
            {
                RequestReplyCorrelator.PrepareRequest(rpc.Request);
 
                MessageHeaders headers = rpc.Request.Headers;
                EndpointAddress localAddress = this.LocalAddress;
                EndpointAddress replyTo = headers.ReplyTo;
 
                if (replyTo == null)
                {
                    headers.ReplyTo = localAddress ?? EndpointAddress.AnonymousAddress;
                }
 
                if (this.IsClient && (localAddress != null) && !localAddress.IsAnonymous)
                {
                    Uri localUri = localAddress.Uri;
 
                    if ((replyTo != null) && !replyTo.IsAnonymous && (localUri != replyTo.Uri))
                    {
                        string text = SR.GetString(SR.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 = SR.GetString(SR.SFxRequestHasInvalidFaultToOnClient, faultTo.Uri, localUri);
                        Exception error = new InvalidOperationException(text);
                        throw TraceUtility.ThrowHelperError(error, rpc.Request);
                    }
 
                    if (this.messageVersion.Addressing == AddressingVersion.WSAddressingAugust2004)
                    {
                        EndpointAddress from = headers.From;
                        if ((from != null) && !from.IsAnonymous && (localUri != from.Uri))
                        {
                            string text = SR.GetString(SR.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 (Trace.CorrelationManager.ActivityId == Guid.Empty)
                {
                    rpc.ActivityId = Guid.NewGuid();
                    FxTrace.Trace.SetAndTraceTransfer(rpc.ActivityId, true);
                }
            }
 
            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 (TD.ClientOperationPreparedIsEnabled())
            {
                string remoteAddress = string.Empty;
                if (this.RemoteAddress != null && this.RemoteAddress.Uri != null)
                {
                    remoteAddress = this.RemoteAddress.Uri.AbsoluteUri;
                }
                TD.ClientOperationPrepared(rpc.EventTraceActivity,
                                            rpc.Action,
                                            this.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 this.BeginCall(action, oneway, operation, ins, this.operationTimeout, callback, asyncState);
        }
 
        internal IAsyncResult BeginCall(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, TimeSpan timeout, AsyncCallback callback, object asyncState)
        {
            this.ThrowIfDisallowedInitializationUI();
            this.ThrowIfIdleAborted(operation);
            this.ThrowIfIsConnectionOpened(operation);
 
            ServiceModelActivity serviceModelActivity = null;
 
            if (DiagnosticUtility.ShouldUseActivity)
            {
                serviceModelActivity = ServiceModelActivity.CreateActivity(true);
                callback = TraceUtility.WrapExecuteUserCodeAsyncCallback(callback);
            }
 
            SendAsyncResult result;
 
            using (Activity boundOperation = ServiceModelActivity.BoundOperation(serviceModelActivity, true))
            {
                if (DiagnosticUtility.ShouldUseActivity)
                {
                    ServiceModelActivity.Start(serviceModelActivity, SR.GetString(SR.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 this.Call(action, oneway, operation, ins, outs, this.operationTimeout);
        }
 
        internal object Call(string action, bool oneway, ProxyOperationRuntime operation, object[] ins, object[] outs, TimeSpan timeout)
        {
            this.ThrowIfDisallowedInitializationUI();
            this.ThrowIfIdleAborted(operation);
            this.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, SR.GetString(SR.ActivityProcessAction, action), ActivityType.ProcessAction);
                }
 
                this.PrepareCall(operation, oneway, ref rpc);
 
                if (!this.explicitlyOpened)
                {
                    this.EnsureDisplayUI();
                    this.EnsureOpened(rpc.TimeoutHelper.RemainingTime());
                }
                else
                {
                    this.ThrowIfOpening();
                    this.ThrowIfDisposedOrNotOpen();
                }
 
                try
                {
                    ConcurrencyBehavior.UnlockInstanceBeforeCallout(OperationContext.Current);
 
                    if (oneway)
                    {
                        this.binder.Send(rpc.Request, rpc.TimeoutHelper.RemainingTime());
                    }
                    else
                    {
                        rpc.Reply = this.binder.Request(rpc.Request, rpc.TimeoutHelper.RemainingTime());
 
                        if (rpc.Reply == null)
                        {
                            this.ThrowIfFaulted();
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SR.GetString(SR.SFxServerDidNotReply)));
                        }
                    }
                }
                finally
                {
                    this.CompletedIOOperation();
                    CallOnceManager.SignalNextIfNonNull(this.autoOpenManager);
                    ConcurrencyBehavior.LockInstanceAfterCallout(OperationContext.Current);
                }
 
                rpc.OutputParameters = outs;
                this.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(SR.GetString(SR.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", SR.GetString(SR.AsyncEndCalledOnWrongChannel));
 
                    if (action != MessageHeaders.WildcardAction && action != sendResult.Rpc.Action)
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("result", SR.GetString(SR.AsyncEndCalledWithAnIAsyncResult));
 
                    SendAsyncResult.End(sendResult);
 
                    sendResult.Rpc.OutputParameters = outs;
                    this.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 this.activityCount);
 
            if (!((updatedActivityCount >= 0)))
            {
                throw Fx.AssertAndThrowFatal("ServiceChannel.DecrementActivity: (updatedActivityCount >= 0)");
            }
 
            if (updatedActivityCount == 0 && this.autoClose)
            {
                try
                {
                    if (this.State == CommunicationState.Opened)
                    {
                        if (this.IsClient)
                        {
                            ISessionChannel<IDuplexSession> duplexSessionChannel = this.InnerChannel as ISessionChannel<IDuplexSession>;
                            if (duplexSessionChannel != null)
                            {
                                this.hasChannelStartedAutoClosing = true;
                                duplexSessionChannel.Session.CloseOutputSession(this.CloseTimeout);
                            }
                        }
                        else
                        {
                            this.Close(this.CloseTimeout);
                        }
                    }
                }
                catch (CommunicationException e)
                {
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
                catch (TimeoutException e)
                {
                    if (TD.CloseTimeoutIsEnabled())
                    {
                        TD.CloseTimeout(e.Message);
                    }
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
                catch (ObjectDisposedException e)
                {
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
                catch (InvalidOperationException e)
                {
                    DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
                }
            }
        }
 
        internal void FireUnknownMessageReceived(Message message)
        {
            EventHandler<UnknownMessageReceivedEventArgs> handler = this.unknownMessageReceived;
            if (handler != null)
                handler(this.proxy, new UnknownMessageReceivedEventArgs(message));
        }
 
        TimeoutException GetOpenTimeoutException(TimeSpan timeout)
        {
            EndpointAddress address = this.RemoteAddress ?? this.LocalAddress;
            if (address != null)
            {
                return new TimeoutException(SR.GetString(SR.TimeoutServiceChannelConcurrentOpen2, address, timeout));
            }
            else
            {
                return new TimeoutException(SR.GetString(SR.TimeoutServiceChannelConcurrentOpen1, timeout));
            }
        }
 
        internal void HandleReceiveComplete(RequestContext context)
        {
            if (context == null && HasSession)
            {
                bool first;
                lock (this.ThisLock)
                {
                    first = !this.doneReceiving;
                    this.doneReceiving = true;
                }
 
                if (first)
                {
                    DispatchRuntime dispatchBehavior = this.ClientRuntime.DispatchRuntime;
                    if (dispatchBehavior != null)
                        dispatchBehavior.GetRuntime().InputSessionDoneReceiving(this);
 
                    this.DecrementActivity();
                }
            }
        }
 
        void HandleReply(ProxyOperationRuntime operation, ref ProxyRpc rpc)
        {
            try
            {
                //set the ID after response
                if (TraceUtility.MessageFlowTracingOnly && rpc.ActivityId != Guid.Empty)
                {
                    System.Runtime.Diagnostics.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(SR.GetString(SR.SFxReplyActionMismatch3,
                                                                                  operation.Name,
                                                                                  rpc.Reply.Headers.Action,
                                                                                  operation.ReplyAction));
                            this.TerminateIfNecessary(ref rpc);
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
                        }
                    }
                    if (operation.DeserializeReply && clientRuntime.IsFault(ref rpc.Reply))
                    {
                        MessageFault fault = MessageFault.CreateFault(rpc.Reply, this.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);
                        this.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
                        System.Runtime.Diagnostics.DiagnosticTraceBase.ActivityId = Guid.Empty;
                        rpc.ActivityId = Guid.Empty;
                    }
                }
            }
            this.TerminateIfNecessary(ref rpc);
 
            if (TD.ServiceChannelCallStopIsEnabled())
            {
                string remoteAddress = string.Empty;
                if (this.RemoteAddress != null && this.RemoteAddress.Uri != null)
                {
                    remoteAddress = this.RemoteAddress.Uri.AbsoluteUri;
                }
                TD.ServiceChannelCallStop(rpc.EventTraceActivity, rpc.Action,
                                            this.clientRuntime.ContractName,
                                            remoteAddress);
            }
 
        }
 
        void TerminateIfNecessary(ref ProxyRpc rpc)
        {
            if (rpc.Operation.IsTerminating)
            {
                this.terminatingOperationName = rpc.Operation.Name;
                TerminatingOperationBehavior.AfterReply(ref rpc);
            }
        }
 
        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));
                    }
 
                    if (string.Compare(code.Name, FaultCodeConstants.Codes.TransactionAborted, StringComparison.Ordinal) == 0)
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new ProtocolException(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 (this.HasSession)
                        {
                            this.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));
                    }
                }
            }
        }
 
        void ThrowIfIdleAborted(ProxyOperationRuntime operation)
        {
            if (this.idleManager != null && this.idleManager.DidIdleAbort)
            {
                string text = SR.GetString(SR.SFxServiceChannelIdleAborted, operation.Name);
                Exception error = new CommunicationObjectAbortedException(text);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
            }
        }
 
        void ThrowIfIsConnectionOpened(ProxyOperationRuntime operation)
        {
            if (operation.IsSessionOpenNotificationEnabled)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                    SR.GetString(SR.SFxServiceChannelCannotBeCalledBecauseIsSessionOpenNotificationEnabled, operation.Name, "Action", OperationDescription.SessionOpenedAction, "Open")));
            }
        }
 
        void ThrowIfInitializationUINotCalled()
        {
            if (!this.didInteractiveInitialization && (this.ClientRuntime.InteractiveChannelInitializers.Count > 0))
            {
                IInteractiveChannelInitializer example = this.ClientRuntime.InteractiveChannelInitializers[0];
                string text = SR.GetString(SR.SFxInitializationUINotCalled, example.GetType().ToString());
                Exception error = new InvalidOperationException(text);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
            }
        }
 
        void ThrowIfDisallowedInitializationUI()
        {
            if (!this.allowInitializationUI)
            {
                this.ThrowIfDisallowedInitializationUICore();
            }
        }
 
        void ThrowIfDisallowedInitializationUICore()
        {
            if (this.ClientRuntime.InteractiveChannelInitializers.Count > 0)
            {
                IInteractiveChannelInitializer example = this.ClientRuntime.InteractiveChannelInitializers[0];
                string text = SR.GetString(SR.SFxInitializationUIDisallowed, example.GetType().ToString());
                Exception error = new InvalidOperationException(text);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
            }
        }
 
        void ThrowIfOpening()
        {
            if (this.State == CommunicationState.Opening)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxCannotCallAutoOpenWhenExplicitOpenCalled)));
            }
        }
 
        internal void IncrementActivity()
        {
            Interlocked.Increment(ref this.activityCount);
        }
 
        void OnInnerChannelFaulted(object sender, EventArgs e)
        {
            this.Fault();
 
            if (this.HasSession)
            {
                DispatchRuntime dispatchRuntime = this.ClientRuntime.DispatchRuntime;
                if (dispatchRuntime != null)
                {
                    dispatchRuntime.GetRuntime().InputSessionFaulted(this);
                }
            }
 
            if (this.autoClose && !this.IsClient)
            {
                this.Abort();
            }
        }
 
        void AddMessageProperties(Message message, OperationContext context)
        {
            if (this.allowOutputBatching)
            {
                message.Properties.AllowOutputBatching = true;
            }
 
            if (context != null && context.InternalServiceChannel == this)
            {
                if (!context.OutgoingMessageVersion.IsMatch(message.Headers.MessageVersion))
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(
                        SR.GetString(SR.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)
        {
            this.Send(message, this.OperationTimeout);
        }
 
        public void Send(Message message, TimeSpan timeout)
        {
            ProxyOperationRuntime operation = UnhandledProxyOperation;
            this.Call(message.Headers.Action, true, operation, new object[] { message }, EmptyArray<object>.Instance, timeout);
        }
 
        public IAsyncResult BeginSend(Message message, AsyncCallback callback, object state)
        {
            return this.BeginSend(message, this.OperationTimeout, callback, state);
        }
 
        public IAsyncResult BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state)
        {
            ProxyOperationRuntime operation = UnhandledProxyOperation;
            return this.BeginCall(message.Headers.Action, true, operation, new object[] { message }, timeout, callback, state);
        }
 
        public void EndSend(IAsyncResult result)
        {
            this.EndCall(MessageHeaders.WildcardAction, EmptyArray<object>.Instance, result);
        }
 
        public Message Request(Message message)
        {
            return this.Request(message, this.OperationTimeout);
        }
 
        public Message Request(Message message, TimeSpan timeout)
        {
            ProxyOperationRuntime operation = UnhandledProxyOperation;
            return (Message)this.Call(message.Headers.Action, false, operation, new object[] { message }, EmptyArray<object>.Instance, timeout);
        }
 
        public IAsyncResult BeginRequest(Message message, AsyncCallback callback, object state)
        {
            return this.BeginRequest(message, this.OperationTimeout, callback, state);
        }
 
        public IAsyncResult BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state)
        {
            ProxyOperationRuntime operation = this.UnhandledProxyOperation;
            return this.BeginCall(message.Headers.Action, false, operation, new object[] { message }, timeout, callback, state);
        }
 
        public Message EndRequest(IAsyncResult result)
        {
            return (Message)this.EndCall(MessageHeaders.WildcardAction, EmptyArray<object>.Instance, result);
        }
 
        protected override void OnAbort()
        {
            if (this.idleManager != null)
            {
                this.idleManager.CancelTimer();
            }
 
            this.binder.Abort();
 
            if (this.factory != null)
                this.factory.ChannelDisposed(this);
 
            if (this.closeFactory)
            {
                if (this.factory != null)
                    this.factory.Abort();
            }
 
            CleanupChannelCollections();
 
            ServiceThrottle serviceThrottle = this.serviceThrottle;
            if (serviceThrottle != null)
                serviceThrottle.DeactivateChannel();
 
            //rollback the attached transaction if one is present
            if ((this.instanceContext != null) && this.HasSession)
            {
                if (instanceContext.HasTransaction)
                {
                    instanceContext.Transaction.CompletePendingTransaction(instanceContext.Transaction.Attached, new Exception()); // error!=null forces Tx rollback
                }
            }
 
            DecrementBusyCount();
        }
 
        protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            if (this.idleManager != null)
            {
                this.idleManager.CancelTimer();
            }
 
            if (this.factory != null)
            {
                this.factory.ChannelDisposed(this);
            }
 
            if (this.InstanceContext != null && this.InstanceContext.HasTransaction)
            {
                this.InstanceContext.CompleteAttachedTransaction();
            }
 
            if (this.closeBinder)
            {
                if (this.closeFactory)
                {
                    return new ChainedAsyncResult(timeout, callback, state,
                        new ChainedBeginHandler(this.InnerChannel.BeginClose), new ChainedEndHandler(this.InnerChannel.EndClose),
                        new ChainedBeginHandler(this.factory.BeginClose), new ChainedEndHandler(this.factory.EndClose));
                }
                else
                {
                    return this.InnerChannel.BeginClose(timeout, callback, state);
                }
            }
            else
            {
                if (this.closeFactory)
                    return factory.BeginClose(timeout, callback, state);
                else
                    return new CompletedAsyncResult(callback, state);
            }
        }
 
        protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
        {
            this.ThrowIfDisallowedInitializationUI();
            this.ThrowIfInitializationUINotCalled();
 
            if (this.autoOpenManager == null)
            {
                this.explicitlyOpened = true;
            }
 
            if (this.HasSession && !this.IsClient)
            {
                IncrementBusyCount();
            }
 
            this.TraceChannelOpenStarted();
 
            if (this.openBinder)
            {
                return this.InnerChannel.BeginOpen(timeout, callback, state);
            }
            else
            {
                return new CompletedAsyncResult(callback, state);
            }
        }
 
        protected override void OnClose(TimeSpan timeout)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
 
            if (this.idleManager != null)
            {
                this.idleManager.CancelTimer();
            }
 
            if (this.factory != null)
            {
                this.factory.ChannelDisposed(this);
            }
 
            if (this.InstanceContext != null && this.InstanceContext.HasTransaction)
            {
                this.InstanceContext.CompleteAttachedTransaction();
            }
 
            if (this.closeBinder)
                this.InnerChannel.Close(timeoutHelper.RemainingTime());
 
            if (this.closeFactory)
                this.factory.Close(timeoutHelper.RemainingTime());
 
            CleanupChannelCollections();
 
            ServiceThrottle serviceThrottle = this.serviceThrottle;
            if (serviceThrottle != null)
            {
                serviceThrottle.DeactivateChannel();
            }
 
            DecrementBusyCount();
        }
 
        protected override void OnEndClose(IAsyncResult result)
        {
            if (this.closeBinder)
            {
                if (this.closeFactory)
                    ChainedAsyncResult.End(result);
                else
                    this.InnerChannel.EndClose(result);
            }
            else
            {
                if (this.closeFactory)
                    factory.EndClose(result);
                else
                    CompletedAsyncResult.End(result);
            }
 
            CleanupChannelCollections();
 
            ServiceThrottle serviceThrottle = this.serviceThrottle;
            if (serviceThrottle != null)
            {
                serviceThrottle.DeactivateChannel();
            }
 
            DecrementBusyCount();
        }
 
        protected override void OnEndOpen(IAsyncResult result)
        {
            if (this.openBinder)
                InnerChannel.EndOpen(result);
            else
                CompletedAsyncResult.End(result);
            this.BindDuplexCallbacks();
            this.CompletedIOOperation();
 
            this.TraceChannelOpenCompleted();
        }
 
        protected override void OnOpen(TimeSpan timeout)
        {
            this.ThrowIfDisallowedInitializationUI();
            this.ThrowIfInitializationUINotCalled();
 
            if (this.autoOpenManager == null)
            {
                this.explicitlyOpened = true;
            }
 
            if (this.HasSession && !this.IsClient)
            {
                IncrementBusyCount();
            }
 
            this.TraceChannelOpenStarted();
 
            if (this.openBinder)
            {
                this.InnerChannel.Open(timeout);
            }
 
            this.BindDuplexCallbacks();
            this.CompletedIOOperation();
 
            this.TraceChannelOpenCompleted();
        }
 
        void CleanupChannelCollections()
        {
            if (!this.hasCleanedUpChannelCollections)
            {
                lock (this.ThisLock)
                {
                    if (!this.hasCleanedUpChannelCollections)
                    {
                        if (this.InstanceContext != null && this.InstanceContext.State != CommunicationState.Closed && this.InstanceContext.State != CommunicationState.Faulted)
                        {
                            //Only attempt to remove the channel if the state is not closed or faulted.
                            try
                            {
                                this.InstanceContext.OutgoingChannels.Remove((IChannel)this.proxy);
                            }
                            catch (CommunicationException) { }  // Race condition if InstanceContext was faulted since the state check. Ignoring Exception                                
                            catch (ObjectDisposedException) { } // Race condition if InstanceContext was closed since the state check. Ignoring Exception
                        }
 
                        if (this.WmiInstanceContext != null)
                        {
                            this.WmiInstanceContext.WmiChannels.Remove((IChannel)this.proxy);
                        }
 
                        this.hasCleanedUpChannelCollections = true;
                    }
                }
            }
        }
 
        void IncrementBusyCount()
        {
            lock (this.ThisLock)
            {
                if (this.State == CommunicationState.Opening)
                {
                    AspNetEnvironment.Current.IncrementBusyCount();
                    if (AspNetEnvironment.Current.TraceIncrementBusyCountIsEnabled())
                    {
                        AspNetEnvironment.Current.TraceIncrementBusyCount(this.GetType().FullName);
                    }
                    this.hasIncrementedBusyCount = true;
                }
            }
        }
 
        void DecrementBusyCount()
        {
            lock (this.ThisLock)
            {
                if (this.hasIncrementedBusyCount)
                {
                    AspNetEnvironment.Current.DecrementBusyCount();
                    if (AspNetEnvironment.Current.TraceDecrementBusyCountIsEnabled())
                    {
                        AspNetEnvironment.Current.TraceDecrementBusyCount(this.GetType().FullName);
                    }
                    this.hasIncrementedBusyCount = false;
                }
            }
        }
 
        #endregion
 
        #region IClientChannel Members
 
        bool IDuplexContextChannel.AutomaticInputSessionShutdown
        {
            get { return this.autoClose; }
            set { this.autoClose = value; }
        }
 
        bool IClientChannel.AllowInitializationUI
        {
            get { return this.allowInitializationUI; }
            set
            {
                this.ThrowIfDisposedOrImmutable();
                this.allowInitializationUI = value;
            }
        }
 
        bool IContextChannel.AllowOutputBatching
        {
            get { return this.allowOutputBatching; }
            set { this.allowOutputBatching = value; }
        }
 
        bool IClientChannel.DidInteractiveInitialization
        {
            get { return this.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);
        }
 
        IDuplexSession GetDuplexSessionOrThrow()
        {
            if (this.InnerChannel == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.channelIsNotAvailable0)));
            }
 
            ISessionChannel<IDuplexSession> duplexSessionChannel = this.InnerChannel as ISessionChannel<IDuplexSession>;
            if (duplexSessionChannel == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.channelDoesNotHaveADuplexSession0)));
            }
 
            return duplexSessionChannel.Session;
        }
 
        IExtensionCollection<IContextChannel> IExtensibleObject<IContextChannel>.Extensions
        {
            get
            {
                lock (this.ThisLock)
                {
                    if (this.extensions == null)
                        this.extensions = new ExtensionCollection<IContextChannel>((IContextChannel)this.Proxy, this.ThisLock);
                    return this.extensions;
                }
            }
        }
 
        InstanceContext IDuplexContextChannel.CallbackInstance
        {
            get { return this.instanceContext; }
            set
            {
                lock (this.ThisLock)
                {
                    if (this.instanceContext != null)
                    {
                        this.instanceContext.OutgoingChannels.Remove((IChannel)this.proxy);
                    }
 
                    this.instanceContext = value;
 
                    if (this.instanceContext != null)
                    {
                        this.instanceContext.OutgoingChannels.Add((IChannel)this.proxy);
                    }
                }
            }
        }
 
        IInputSession IContextChannel.InputSession
        {
            get
            {
                if (this.InnerChannel != null)
                {
                    ISessionChannel<IInputSession> inputSession = this.InnerChannel as ISessionChannel<IInputSession>;
                    if (inputSession != null)
                        return inputSession.Session;
 
                    ISessionChannel<IDuplexSession> duplexSession = this.InnerChannel as ISessionChannel<IDuplexSession>;
                    if (duplexSession != null)
                        return duplexSession.Session;
                }
 
                return null;
            }
        }
 
        IOutputSession IContextChannel.OutputSession
        {
            get
            {
                if (this.InnerChannel != null)
                {
                    ISessionChannel<IOutputSession> outputSession = this.InnerChannel as ISessionChannel<IOutputSession>;
                    if (outputSession != null)
                        return outputSession.Session;
 
                    ISessionChannel<IDuplexSession> duplexSession = this.InnerChannel as ISessionChannel<IDuplexSession>;
                    if (duplexSession != null)
                        return duplexSession.Session;
                }
 
                return null;
            }
        }
 
        string IContextChannel.SessionId
        {
            get
            {
                if (this.InnerChannel != null)
                {
                    ISessionChannel<IInputSession> inputSession = this.InnerChannel as ISessionChannel<IInputSession>;
                    if (inputSession != null)
                        return inputSession.Session.Id;
 
                    ISessionChannel<IOutputSession> outputSession = this.InnerChannel as ISessionChannel<IOutputSession>;
                    if (outputSession != null)
                        return outputSession.Session.Id;
 
                    ISessionChannel<IDuplexSession> duplexSession = this.InnerChannel as ISessionChannel<IDuplexSession>;
                    if (duplexSession != null)
                        return duplexSession.Session.Id;
                }
 
                return null;
            }
        }
 
        event EventHandler<UnknownMessageReceivedEventArgs> IClientChannel.UnknownMessageReceived
        {
            add
            {
                lock (this.ThisLock)
                {
                    this.unknownMessageReceived += value;
                }
            }
            remove
            {
                lock (this.ThisLock)
                {
                    this.unknownMessageReceived -= value;
                }
            }
        }
 
        public void DisplayInitializationUI()
        {
            this.ThrowIfDisallowedInitializationUI();
 
            if (this.autoDisplayUIManager == null)
            {
                this.explicitlyOpened = true;
            }
 
            this.ClientRuntime.GetRuntime().DisplayInitializationUI(this);
            this.didInteractiveInitialization = true;
        }
 
        public IAsyncResult BeginDisplayInitializationUI(AsyncCallback callback, object state)
        {
            this.ThrowIfDisallowedInitializationUI();
 
            if (this.autoDisplayUIManager == null)
            {
                this.explicitlyOpened = true;
            }
 
            return this.ClientRuntime.GetRuntime().BeginDisplayInitializationUI(this, callback, state);
        }
 
        public void EndDisplayInitializationUI(IAsyncResult result)
        {
            this.ClientRuntime.GetRuntime().EndDisplayInitializationUI(result);
            this.didInteractiveInitialization = true;
        }
 
        void IDisposable.Dispose()
        {
            this.Close();
        }
 
        #endregion
 
        void TraceChannelOpenStarted()
        {
            if (TD.ClientChannelOpenStartIsEnabled() && this.endpointDispatcher == null)
            {
                TD.ClientChannelOpenStart(this.EventActivity);
            }
            else if (TD.ServiceChannelOpenStartIsEnabled())
            {
                TD.ServiceChannelOpenStart(this.EventActivity);
            }
 
            if (DiagnosticUtility.ShouldTraceInformation)
            {
                Dictionary<string, string> values = new Dictionary<string, string>(4);
                bool traceNeeded = false;
                DispatchRuntime behavior = this.DispatchRuntime;
                if (behavior != null)
                {
                    if (behavior.Type != null)
                    {
                        values["ServiceType"] = behavior.Type.AssemblyQualifiedName;
                    }
                    values["ContractNamespace"] = this.clientRuntime.ContractNamespace;
                    values["ContractName"] = this.clientRuntime.ContractName;
                    traceNeeded = true;
                }
                if ((this.endpointDispatcher != null) && (this.endpointDispatcher.ListenUri != null))
                {
                    values["Uri"] = this.endpointDispatcher.ListenUri.ToString();
                    traceNeeded = true;
                }
                if (traceNeeded)
                {
                    TraceUtility.TraceEvent(TraceEventType.Information, TraceCode.ServiceChannelLifetime,
                        SR.GetString(SR.TraceCodeServiceChannelLifetime),
                        new DictionaryTraceRecord(values), this, null);
                }
            }            
        }
 
        void TraceChannelOpenCompleted()
        {
            if (this.endpointDispatcher == null && TD.ClientChannelOpenStopIsEnabled())
            {
                TD.ClientChannelOpenStop(this.EventActivity);
            }
            else if (TD.ServiceChannelOpenStopIsEnabled())
            {
                TD.ServiceChannelOpenStop(this.EventActivity);
            }
        }
 
        static void TraceServiceChannelCallStart(EventTraceActivity eventTraceActivity, bool isSynchronous)
        {
            if (TD.ServiceChannelCallStartIsEnabled())
            {
                if (isSynchronous)
                {
                    TD.ServiceChannelCallStart(eventTraceActivity);
                }
                else
                {
                    TD.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.
 
        class SendAsyncResult : TraceAsyncResult
        {
            readonly bool isOneWay;
            readonly ProxyOperationRuntime operation;
            internal ProxyRpc Rpc;
            OperationContext operationContext;
 
            static AsyncCallback ensureInteractiveInitCallback = Fx.ThunkCallback(EnsureInteractiveInitCallback);
            static AsyncCallback ensureOpenCallback = Fx.ThunkCallback(EnsureOpenCallback);
            static AsyncCallback 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)
            {
                this.Rpc = new ProxyRpc(channel, operation, action, inputParameters, timeout);
                this.isOneWay = isOneWay;
                this.operation = operation;
                this.operationContext = OperationContext.Current;
            }
 
            internal void Begin()
            {
                this.Rpc.Channel.PrepareCall(this.operation, this.isOneWay, ref this.Rpc);
 
                if (this.Rpc.Channel.explicitlyOpened)
                {
                    this.Rpc.Channel.ThrowIfOpening();
                    this.Rpc.Channel.ThrowIfDisposedOrNotOpen();
                    this.StartSend(true);
                }
                else
                {
                    this.StartEnsureInteractiveInit();
                }
            }
 
            void StartEnsureInteractiveInit()
            {
                IAsyncResult result = this.Rpc.Channel.BeginEnsureDisplayUI(ensureInteractiveInitCallback, this);
 
                if (result.CompletedSynchronously)
                {
                    this.FinishEnsureInteractiveInit(result, true);
                }
            }
 
            static void EnsureInteractiveInitCallback(IAsyncResult result)
            {
                if (!result.CompletedSynchronously)
                {
                    ((SendAsyncResult)result.AsyncState).FinishEnsureInteractiveInit(result, false);
                }
            }
 
            void FinishEnsureInteractiveInit(IAsyncResult result, bool completedSynchronously)
            {
                Exception exception = null;
 
                try
                {
                    this.Rpc.Channel.EndEnsureDisplayUI(result);
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e) || completedSynchronously)
                    {
                        throw;
                    }
                    exception = e;
                }
 
                if (exception != null)
                {
                    this.CallComplete(completedSynchronously, exception);
                }
                else
                {
                    this.StartEnsureOpen(completedSynchronously);
                }
            }
 
            void StartEnsureOpen(bool completedSynchronously)
            {
                TimeSpan timeout = this.Rpc.TimeoutHelper.RemainingTime();
                IAsyncResult result = null;
                Exception exception = null;
 
                try
                {
                    result = this.Rpc.Channel.BeginEnsureOpened(timeout, ensureOpenCallback, this);
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e) || completedSynchronously)
                    {
                        throw;
                    }
                    exception = e;
                }
 
                if (exception != null)
                {
                    this.CallComplete(completedSynchronously, exception);
                }
                else if (result.CompletedSynchronously)
                {
                    this.FinishEnsureOpen(result, completedSynchronously);
                }
            }
 
            static void EnsureOpenCallback(IAsyncResult result)
            {
                if (!result.CompletedSynchronously)
                {
                    ((SendAsyncResult)result.AsyncState).FinishEnsureOpen(result, false);
                }
            }
 
            void FinishEnsureOpen(IAsyncResult result, bool completedSynchronously)
            {
                Exception exception = null;
                using (ServiceModelActivity.BoundOperation(this.Rpc.Activity))
                {
                    try
                    {
                        this.Rpc.Channel.EndEnsureOpened(result);
                    }
                    catch (Exception e)
                    {
                        if (Fx.IsFatal(e) || completedSynchronously)
                        {
                            throw;
                        }
                        exception = e;
                    }
 
                    if (exception != null)
                    {
                        this.CallComplete(completedSynchronously, exception);
                    }
                    else
                    {
                        this.StartSend(completedSynchronously);
                    }
                }
            }
 
            void StartSend(bool completedSynchronously)
            {
                TimeSpan timeout = this.Rpc.TimeoutHelper.RemainingTime();
                IAsyncResult result = null;
                Exception exception = null;
 
                try
                {
                    ConcurrencyBehavior.UnlockInstanceBeforeCallout(this.operationContext);
 
                    if (this.isOneWay)
                    {
                        result = this.Rpc.Channel.binder.BeginSend(this.Rpc.Request, timeout, sendCallback, this);
                    }
                    else
                    {
                        result = this.Rpc.Channel.binder.BeginRequest(this.Rpc.Request, timeout, sendCallback, this);
                    }
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
                    if (completedSynchronously)
                    {
                        ConcurrencyBehavior.LockInstanceAfterCallout(this.operationContext);
                        throw;
                    }
                    exception = e;
                }
                finally
                {
                    CallOnceManager.SignalNextIfNonNull(this.Rpc.Channel.autoOpenManager);
                }
 
                if (exception != null)
                {
                    this.CallComplete(completedSynchronously, exception);
                }
                else if (result.CompletedSynchronously)
                {
                    this.FinishSend(result, completedSynchronously);
                }
            }
 
            static void SendCallback(IAsyncResult result)
            {
                if (!result.CompletedSynchronously)
                {
                    ((SendAsyncResult)result.AsyncState).FinishSend(result, false);
                }
            }
 
            void FinishSend(IAsyncResult result, bool completedSynchronously)
            {
                Exception exception = null;
 
                try
                {
                    if (this.isOneWay)
                    {
                        this.Rpc.Channel.binder.EndSend(result);
                    }
                    else
                    {
                        this.Rpc.Reply = this.Rpc.Channel.binder.EndRequest(result);
 
                        if (this.Rpc.Reply == null)
                        {
                            this.Rpc.Channel.ThrowIfFaulted();
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(SR.GetString(SR.SFxServerDidNotReply)));
                        }
                    }
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                    {
                        throw;
                    }
                    if (completedSynchronously)
                    {
                        ConcurrencyBehavior.LockInstanceAfterCallout(this.operationContext);
                        throw;
                    }
                    exception = e;
                }
 
                this.CallComplete(completedSynchronously, exception);
            }
 
            void CallComplete(bool completedSynchronously, Exception exception)
            {
                this.Rpc.Channel.CompletedIOOperation();
                this.Complete(completedSynchronously, exception);
            }
 
            public static void End(SendAsyncResult result)
            {
                try
                {
                    AsyncResult.End<SendAsyncResult>(result);
                }
                finally
                {
                    ConcurrencyBehavior.LockInstanceAfterCallout(result.operationContext);
                }
            }
        }
 
        interface ICallOnce
        {
            void Call(ServiceChannel channel, TimeSpan timeout);
            IAsyncResult BeginCall(ServiceChannel channel, TimeSpan timeout, AsyncCallback callback, object state);
            void EndCall(ServiceChannel channel, IAsyncResult result);
        }
 
        class CallDisplayUIOnce : ICallOnce
        {
            static CallDisplayUIOnce instance;
 
            internal static CallDisplayUIOnce Instance
            {
                get
                {
                    if (CallDisplayUIOnce.instance == null)
                    {
                        CallDisplayUIOnce.instance = new CallDisplayUIOnce();
                    }
                    return CallDisplayUIOnce.instance;
                }
            }
 
            [Conditional("DEBUG")]
            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)
            {
                this.ValidateTimeoutIsMaxValue(timeout);
                channel.DisplayInitializationUI();
            }
 
            IAsyncResult ICallOnce.BeginCall(ServiceChannel channel, TimeSpan timeout, AsyncCallback callback, object state)
            {
                this.ValidateTimeoutIsMaxValue(timeout);
                return channel.BeginDisplayInitializationUI(callback, state);
            }
 
            void ICallOnce.EndCall(ServiceChannel channel, IAsyncResult result)
            {
                channel.EndDisplayInitializationUI(result);
            }
        }
 
        class CallOpenOnce : ICallOnce
        {
            static CallOpenOnce instance;
 
            internal static CallOpenOnce Instance
            {
                get
                {
                    if (CallOpenOnce.instance == null)
                    {
                        CallOpenOnce.instance = new CallOpenOnce();
                    }
                    return CallOpenOnce.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);
            }
        }
 
        class CallOnceManager
        {
            readonly ICallOnce callOnce;
            readonly ServiceChannel channel;
            bool isFirst = true;
            Queue<IWaiter> queue;
 
            static Action<object> signalWaiter = new Action<object>(CallOnceManager.SignalWaiter);
 
            internal CallOnceManager(ServiceChannel channel, ICallOnce callOnce)
            {
                this.callOnce = callOnce;
                this.channel = channel;
                this.queue = new Queue<IWaiter>();
            }
 
            object ThisLock
            {
                get { return this; }
            }
 
            internal void CallOnce(TimeSpan timeout, CallOnceManager cascade)
            {
                SyncWaiter waiter = null;
                bool first = false;
 
                if (this.queue != null)
                {
                    lock (this.ThisLock)
                    {
                        if (this.queue != null)
                        {
                            if (this.isFirst)
                            {
                                first = true;
                                this.isFirst = false;
                            }
                            else
                            {
                                waiter = new SyncWaiter(this);
                                this.queue.Enqueue(waiter);
                            }
                        }
                    }
                }
 
                CallOnceManager.SignalNextIfNonNull(cascade);
 
                if (first)
                {
                    bool throwing = true;
                    try
                    {
                        this.callOnce.Call(this.channel, timeout);
                        throwing = false;
                    }
                    finally
                    {
                        if (throwing)
                        {
                            this.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 (this.queue != null)
                {
                    lock (this.ThisLock)
                    {
                        if (this.queue != null)
                        {
                            if (this.isFirst)
                            {
                                first = true;
                                this.isFirst = false;
                            }
                            else
                            {
                                waiter = new AsyncWaiter(this, timeout, callback, state);
                                this.queue.Enqueue(waiter);
                            }
                        }
                    }
                }
 
                CallOnceManager.SignalNextIfNonNull(cascade);
 
                if (first)
                {
                    bool throwing = true;
                    try
                    {
                        IAsyncResult result = this.callOnce.BeginCall(this.channel, timeout, callback, state);
                        throwing = false;
                        return result;
                    }
                    finally
                    {
                        if (throwing)
                        {
                            this.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
                    {
                        this.callOnce.EndCall(this.channel, result);
                        throwing = false;
                    }
                    finally
                    {
                        if (throwing)
                        {
                            this.SignalNext();
                        }
                    }
                }
            }
 
            static internal void SignalNextIfNonNull(CallOnceManager manager)
            {
                if (manager != null)
                {
                    manager.SignalNext();
                }
            }
 
            internal void SignalNext()
            {
                if (this.queue == null)
                {
                    return;
                }
 
                IWaiter waiter = null;
 
                lock (this.ThisLock)
                {
                    if (this.queue != null)
                    {
                        if (this.queue.Count > 0)
                        {
                            waiter = this.queue.Dequeue();
                        }
                        else
                        {
                            this.queue = null;
                        }
                    }
                }
 
                if (waiter != null)
                {
                    ActionItem.Schedule(CallOnceManager.signalWaiter, waiter);
                }
            }
 
            static void SignalWaiter(object state)
            {
                ((IWaiter)state).Signal();
            }
 
            interface IWaiter
            {
                void Signal();
            }
 
            class SyncWaiter : IWaiter
            {
                ManualResetEvent wait = new ManualResetEvent(false);
                CallOnceManager manager;
                bool isTimedOut = false;
                bool isSignaled = false;
                int waitCount = 0;
 
                internal SyncWaiter(CallOnceManager manager)
                {
                    this.manager = manager;
                }
 
                bool ShouldSignalNext
                {
                    get { return this.isTimedOut && this.isSignaled; }
                }
 
                void IWaiter.Signal()
                {
                    wait.Set();
                    this.CloseWaitHandle();
 
                    bool signalNext;
                    lock (this.manager.ThisLock)
                    {
                        this.isSignaled = true;
                        signalNext = this.ShouldSignalNext;
                    }
                    if (signalNext)
                    {
                        this.manager.SignalNext();
                    }
                }
 
                internal bool Wait(TimeSpan timeout)
                {
                    try
                    {
                        if (!TimeoutHelper.WaitOne(this.wait, timeout))
                        {
                            bool signalNext;
                            lock (this.manager.ThisLock)
                            {
                                this.isTimedOut = true;
                                signalNext = this.ShouldSignalNext;
                            }
                            if (signalNext)
                            {
                                this.manager.SignalNext();
                            }
                        }
                    }
                    finally
                    {
                        this.CloseWaitHandle();
                    }
 
                    return !this.isTimedOut;
                }
 
                void CloseWaitHandle()
                {
                    if (Interlocked.Increment(ref this.waitCount) == 2)
                    {
                        this.wait.Close();
                    }
                }
            }
 
            class AsyncWaiter : AsyncResult, IWaiter
            {
                static Action<object> timerCallback = new Action<object>(AsyncWaiter.TimerCallback);
 
                CallOnceManager manager;
                TimeSpan timeout;
                IOThreadTimer timer;
 
                internal AsyncWaiter(CallOnceManager manager, TimeSpan timeout,
                                     AsyncCallback callback, object state)
                    : base(callback, state)
                {
                    this.manager = manager;
                    this.timeout = timeout;
 
                    if (timeout != TimeSpan.MaxValue)
                    {
                        this.timer = new IOThreadTimer(timerCallback, this, false);
                        this.timer.Set(timeout);
                    }
                }
 
                internal static void End(IAsyncResult result)
                {
                    AsyncResult.End<AsyncWaiter>(result);
                }
 
                void IWaiter.Signal()
                {
                    if ((this.timer == null) || this.timer.Cancel())
                    {
                        this.Complete(false);
                        this.manager.channel.Closed -= this.OnClosed;
                    }
                    else
                    {
                        this.manager.SignalNext();
                    }
                }
 
                void OnClosed(object sender, EventArgs e)
                {
                    if ((this.timer == null) || this.timer.Cancel())
                    {
                        this.Complete(false, this.manager.channel.CreateClosedException());
                    }
                }
 
                static void TimerCallback(object state)
                {
                    AsyncWaiter _this = (AsyncWaiter)state;
                    _this.Complete(false, _this.manager.channel.GetOpenTimeoutException(_this.timeout));
                }
            }
        }
 
        class CallOnceCompletedAsyncResult : AsyncResult
        {
            internal CallOnceCompletedAsyncResult(AsyncCallback callback, object state)
                : base(callback, state)
            {
                this.Complete(true);
            }
 
            static internal void End(IAsyncResult result)
            {
                AsyncResult.End<CallOnceCompletedAsyncResult>(result);
            }
        }
 
        internal class SessionIdleManager
        {
            readonly IChannelBinder binder;
            ServiceChannel channel;
            readonly long idleTicks;
            long lastActivity;
            readonly IOThreadTimer timer;
            static Action<object> timerCallback;
            bool didIdleAbort;
            bool isTimerCancelled;
            object thisLock;
 
            SessionIdleManager(IChannelBinder binder, TimeSpan idle)
            {
                this.binder = binder;
                this.timer = new IOThreadTimer(GetTimerCallback(), this, false);
                this.idleTicks = Ticks.FromTimeSpan(idle);
                this.timer.SetAt(Ticks.Now + this.idleTicks);
                this.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 this.didIdleAbort;
                    }
                }
            }
 
            internal void CancelTimer()
            {
                lock (thisLock)
                {
                    this.isTimerCancelled = true;
                    this.timer.Cancel();
                }
            }
 
            internal void CompletedActivity()
            {
                Interlocked.Exchange(ref this.lastActivity, Ticks.Now);
            }
 
            internal void RegisterChannel(ServiceChannel channel, out bool didIdleAbort)
            {
                lock (thisLock)
                {
                    this.channel = channel;
                    didIdleAbort = this.didIdleAbort;
                }
            }
 
            static Action<object> GetTimerCallback()
            {
                if (SessionIdleManager.timerCallback == null)
                {
                    SessionIdleManager.timerCallback = SessionIdleManager.TimerCallback;
                }
                return SessionIdleManager.timerCallback;
            }
 
            static void TimerCallback(object state)
            {
                ((SessionIdleManager)state).TimerCallback();
            }
 
            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 this.lastActivity, 0, 0);
                long abortTime = last + this.idleTicks;
 
                lock (thisLock)
                {
                    if (Ticks.Now > abortTime)
                    {
                        if (TD.SessionIdleTimeoutIsEnabled())
                        {
                            string listenUri = string.Empty;
                            if (this.binder.ListenUri != null)
                            {
                                listenUri = this.binder.ListenUri.AbsoluteUri;                                
                            }
 
                            TD.SessionIdleTimeout(listenUri); 
                        }
 
                        this.didIdleAbort = true;
                        if (this.channel != null)
                        {
                            this.channel.Abort();
                        }
                        else
                        {
                            this.binder.Abort();
                        }
                    }
                    else
                    {
                        if (!this.isTimerCancelled && binder.Channel.State != CommunicationState.Faulted && binder.Channel.State != CommunicationState.Closed)
                        {
                            this.timer.SetAt(abortTime);
                        }
                    }
                }
            }
        }
    }
}