File: System\ServiceModel\InstanceContext.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
 
namespace System.ServiceModel
{
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Runtime;
    using System.Runtime.CompilerServices;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Diagnostics;
    using System.ServiceModel.Dispatcher;
    using System.Threading;
    using System.ServiceModel.Diagnostics.Application;
 
    public sealed class InstanceContext : CommunicationObject, IExtensibleObject<InstanceContext>
    {
        internal static InstanceContextEmptyCallback NotifyEmptyCallback = new InstanceContextEmptyCallback(InstanceContext.NotifyEmpty);
        internal static InstanceContextIdleCallback NotifyIdleCallback = new InstanceContextIdleCallback(InstanceContext.NotifyIdle);
 
        bool autoClose;
        InstanceBehavior behavior;
        ServiceChannelManager channels;
        ConcurrencyInstanceContextFacet concurrency;
        ExtensionCollection<InstanceContext> extensions;
        readonly ServiceHostBase host;
        QuotaThrottle quotaThrottle;
        ServiceThrottle serviceThrottle;
        int instanceContextManagerIndex;
        object serviceInstanceLock = new object();
        SynchronizationContext synchronizationContext;
        TransactionInstanceContextFacet transaction;
        object userObject;
        bool wellKnown;
        SynchronizedCollection<IChannel> wmiChannels;
        bool isUserCreated;
 
        public InstanceContext(object implementation)
            : this(null, implementation)
        {
        }
 
        public InstanceContext(ServiceHostBase host, object implementation)
            : this(host, implementation, true)
        {
        }
 
        internal InstanceContext(ServiceHostBase host, object implementation, bool isUserCreated)
            : this(host, implementation, true, isUserCreated)
        {
        }
 
        internal InstanceContext(ServiceHostBase host, object implementation, bool wellKnown, bool isUserCreated)
        {
            this.host = host;
            if (implementation != null)
            {
                this.userObject = implementation;
                this.wellKnown = wellKnown;
            }
            this.autoClose = false;
            this.channels = new ServiceChannelManager(this);
            this.isUserCreated = isUserCreated;
        }
 
        public InstanceContext(ServiceHostBase host)
            : this(host, true)
        {
        }
 
        internal InstanceContext(ServiceHostBase host, bool isUserCreated)
        {
            if (host == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("host"));
            }
 
            this.host = host;
            this.autoClose = true;
            this.channels = new ServiceChannelManager(this, NotifyEmptyCallback);
            this.isUserCreated = isUserCreated;
        }
 
        internal bool IsUserCreated
        {
            get { return this.isUserCreated; }
            set { this.isUserCreated = value; }
        }
 
        internal bool IsWellKnown
        {
            get { return this.wellKnown; }
        }
 
        internal bool AutoClose
        {
            get { return this.autoClose; }
            set { this.autoClose = value; }
        }
 
        internal InstanceBehavior Behavior
        {
            get { return this.behavior; }
            set
            {
                if (this.behavior == null)
                {
                    this.behavior = value;
                }
            }
        }
 
        internal ConcurrencyInstanceContextFacet Concurrency
        {
            get
            {
                if (this.concurrency == null)
                {
                    lock (this.ThisLock)
                    {
                        if (this.concurrency == null)
                            this.concurrency = new ConcurrencyInstanceContextFacet();
                    }
                }
 
                return this.concurrency;
            }
        }
 
        internal static InstanceContext Current
        {
            get { return OperationContext.Current != null ? OperationContext.Current.InstanceContext : null; }
        }
 
        protected override TimeSpan DefaultCloseTimeout
        {
            get
            {
                if (this.host != null)
                {
                    return this.host.CloseTimeout;
                }
                else
                {
                    return ServiceDefaults.CloseTimeout;
                }
            }
        }
 
        protected override TimeSpan DefaultOpenTimeout
        {
            get
            {
                if (this.host != null)
                {
                    return this.host.OpenTimeout;
                }
                else
                {
                    return ServiceDefaults.OpenTimeout;
                }
            }
        }
 
        public IExtensionCollection<InstanceContext> Extensions
        {
            get
            {
                this.ThrowIfClosed();
                lock (this.ThisLock)
                {
                    if (this.extensions == null)
                        this.extensions = new ExtensionCollection<InstanceContext>(this, this.ThisLock);
                    return this.extensions;
                }
            }
        }
 
        internal bool HasTransaction
        {
            get { return (this.transaction != null) && !object.Equals(this.transaction.Attached, null); }
        }
 
        public ICollection<IChannel> IncomingChannels
        {
            get
            {
                this.ThrowIfClosed();
                return channels.IncomingChannels;
            }
        }
 
        bool IsBusy
        {
            get
            {
                if (this.State == CommunicationState.Closed)
                    return false;
                return this.channels.IsBusy;
            }
        }
 
        bool IsSingleton
        {
            get
            {
                return ((this.behavior != null) &&
                        InstanceContextProviderBase.IsProviderSingleton(this.behavior.InstanceContextProvider));
            }
        }
 
        public ICollection<IChannel> OutgoingChannels
        {
            get
            {
                this.ThrowIfClosed();
                return channels.OutgoingChannels;
            }
        }
 
        public ServiceHostBase Host
        {
            get
            {
                this.ThrowIfClosed();
                return this.host;
            }
        }
 
        public int ManualFlowControlLimit
        {
            get { return this.EnsureQuotaThrottle().Limit; }
            set { this.EnsureQuotaThrottle().SetLimit(value); }
        }
 
        internal QuotaThrottle QuotaThrottle
        {
            get { return this.quotaThrottle; }
        }
 
        internal ServiceThrottle ServiceThrottle
        {
            get { return this.serviceThrottle; }
            set
            {
                this.ThrowIfDisposed();
                this.serviceThrottle = value;
            }
        }
 
        internal int InstanceContextManagerIndex
        {
            get { return this.instanceContextManagerIndex; }
            set { this.instanceContextManagerIndex = value; }
        }
 
        public SynchronizationContext SynchronizationContext
        {
            get { return this.synchronizationContext; }
            set
            {
                this.ThrowIfClosedOrOpened();
                this.synchronizationContext = value;
            }
        }
 
        new internal object ThisLock
        {
            get { return base.ThisLock; }
        }
 
        internal TransactionInstanceContextFacet Transaction
        {
            get
            {
                if (this.transaction == null)
                {
                    lock (this.ThisLock)
                    {
                        if (this.transaction == null)
                            this.transaction = new TransactionInstanceContextFacet(this);
                    }
                }
 
                return this.transaction;
            }
        }
 
        internal object UserObject
        {
            get { return this.userObject; }
        }
 
        internal ICollection<IChannel> WmiChannels
        {
            get
            {
                if (this.wmiChannels == null)
                {
                    lock (this.ThisLock)
                    {
                        if (this.wmiChannels == null)
                        {
                            this.wmiChannels = new SynchronizedCollection<IChannel>();
                        }
                    }
                }
                return this.wmiChannels;
            }
        }
 
        protected override void OnAbort()
        {
            channels.Abort();
            this.Unload();
        }
 
        internal IAsyncResult BeginCloseInput(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return channels.BeginCloseInput(timeout, callback, state);
        }
 
        internal void BindRpc(ref MessageRpc rpc)
        {
            this.ThrowIfClosed();
            this.channels.IncrementActivityCount();
            rpc.SuccessfullyBoundInstance = true;
        }
 
        internal void BindIncomingChannel(ServiceChannel channel)
        {
            this.ThrowIfDisposed();
 
            channel.InstanceContext = this;
            IChannel proxy = (IChannel)channel.Proxy;
            this.channels.AddIncomingChannel(proxy);
 
            // CSDMain 265783: Memory Leak on Chat Stress test scenario
            // There's a race condition while on one thread we received a new request from underlying sessionful channel
            // and on another thread we just aborted the channel. So the channel will be added to the IncomingChannels list of 
            // ServiceChannelManager and never get a chance to be removed.
            if (proxy != null)
            {
                CommunicationState state = channel.State;
                if (state == CommunicationState.Closing
                    || state == CommunicationState.Closed
                    || state == CommunicationState.Faulted)
                {
                    this.channels.RemoveChannel(proxy);
                }
            }
        }
 
 
        void CloseIfNotBusy()
        {
            if (!(this.State != CommunicationState.Created && this.State != CommunicationState.Opening))
            {
                Fx.Assert("InstanceContext.CloseIfNotBusy: (this.State != CommunicationState.Created && this.State != CommunicationState.Opening)");
            }
 
            if (this.State != CommunicationState.Opened)
                return;
 
            if (this.IsBusy)
                return;
 
            if (this.behavior.CanUnload(this) == false)
                return;
 
            try
            {
                if (this.State == CommunicationState.Opened)
                    this.Close();
            }
            catch (ObjectDisposedException e)
            {
                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
            }
            catch (InvalidOperationException e)
            {
                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
            }
            catch (CommunicationException e)
            {
                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
            }
            catch (TimeoutException e)
            {
                if (TD.CloseTimeoutIsEnabled())
                {
                    TD.CloseTimeout(e.Message);
                }
                DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
            }
        }
 
        internal void CloseInput(TimeSpan timeout)
        {
            channels.CloseInput(timeout);
        }
 
        internal void EndCloseInput(IAsyncResult result)
        {
            channels.EndCloseInput(result);
        }
 
        [MethodImpl(MethodImplOptions.NoInlining)]
        internal void CompleteAttachedTransaction()
        {
            Exception error = null;
 
            if (!this.behavior.TransactionAutoCompleteOnSessionClose)
            {
                error = new Exception();
                if (DiagnosticUtility.ShouldTraceInformation)
                    TraceUtility.TraceEvent(TraceEventType.Information,
                                                                    TraceCode.TxCompletionStatusAbortedOnSessionClose,
                                                                    SR.GetString(SR.TraceCodeTxCompletionStatusAbortedOnSessionClose,
                                                                                    transaction.Attached.TransactionInformation.LocalIdentifier)
                                                                    );
 
            }
            else if (DiagnosticUtility.ShouldTraceInformation)
            {
                TraceUtility.TraceEvent(TraceEventType.Information,
                                                                TraceCode.TxCompletionStatusCompletedForTACOSC,
                                                                SR.GetString(SR.TraceCodeTxCompletionStatusCompletedForTACOSC,
                                                                                transaction.Attached.TransactionInformation.LocalIdentifier)
                                                                );
            }
 
            transaction.CompletePendingTransaction(transaction.Attached, error);
            transaction.Attached = null;
        }
 
        QuotaThrottle EnsureQuotaThrottle()
        {
            lock (this.ThisLock)
            {
                if (this.quotaThrottle == null)
                {
                    this.quotaThrottle = new QuotaThrottle(ImmutableDispatchRuntime.GotDynamicInstanceContext, this.ThisLock);
                    this.quotaThrottle.Owner = "InstanceContext";
                }
                return this.quotaThrottle;
            }
        }
 
        internal void FaultInternal()
        {
            this.Fault();
        }
 
        public object GetServiceInstance()
        {
            return this.GetServiceInstance(null);
        }
 
        public object GetServiceInstance(Message message)
        {
            lock (this.serviceInstanceLock)
            {
                this.ThrowIfClosedOrNotOpen();
 
                object current = this.userObject;
 
                if (current != null)
                {
                    return current;
                }
 
                if (this.behavior == null)
                {
                    Exception error = new InvalidOperationException(SR.GetString(SR.SFxInstanceNotInitialized));
                    if (message != null)
                    {
                        throw TraceUtility.ThrowHelperError(error, message);
                    }
                    else
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(error);
                    }
                }
 
                object newUserObject;
                if (message != null)
                {
                    newUserObject = this.behavior.GetInstance(this, message);
                }
                else
                {
                    newUserObject = this.behavior.GetInstance(this);
                }
                if (newUserObject != null)
                {
                    this.SetUserObject(newUserObject);
                }
 
                return newUserObject;
            }
        }
 
        public int IncrementManualFlowControlLimit(int incrementBy)
        {
            return this.EnsureQuotaThrottle().IncrementLimit(incrementBy);
        }
 
        void Load()
        {
            if (this.behavior != null)
            {
                this.behavior.Initialize(this);
            }
 
            if (this.host != null)
            {
                this.host.BindInstance(this);
            }
        }
 
        static void NotifyEmpty(InstanceContext instanceContext)
        {
            if (instanceContext.autoClose)
            {
                instanceContext.CloseIfNotBusy();
            }
        }
 
        static void NotifyIdle(InstanceContext instanceContext)
        {
            instanceContext.CloseIfNotBusy();
        }
 
        protected override IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return new CloseAsyncResult(timeout, callback, state, this);
        }
 
        protected override void OnEndClose(IAsyncResult result)
        {
            CloseAsyncResult.End(result);
        }
 
        protected override IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return new CompletedAsyncResult(callback, state);
        }
 
        protected override void OnEndOpen(IAsyncResult result)
        {
            CompletedAsyncResult.End(result);
        }
 
        protected override void OnClose(TimeSpan timeout)
        {
            channels.Close(timeout);
            this.Unload();
        }
 
        protected override void OnClosed()
        {
            base.OnClosed();
 
            ServiceThrottle throttle = this.serviceThrottle;
            if (throttle != null)
            {
                throttle.DeactivateInstanceContext();
            }
        }
 
        protected override void OnFaulted()
        {
            base.OnFaulted();
 
            if (this.IsSingleton && (this.host != null))
            {
                this.host.FaultInternal();
            }
        }
 
        protected override void OnOpen(TimeSpan timeout)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
        }
 
        protected override void OnOpened()
        {
            base.OnOpened();
        }
 
        protected override void OnOpening()
        {
            this.Load();
            base.OnOpening();
        }
 
        public void ReleaseServiceInstance()
        {
            this.ThrowIfDisposedOrNotOpen();
            this.SetUserObject(null);
        }
 
        void SetUserObject(object newUserObject)
        {
            if (this.behavior != null && !this.wellKnown)
            {
                object oldUserObject = Interlocked.Exchange(ref this.userObject, newUserObject);
 
                if ((oldUserObject != null) && (this.host != null) && !Object.Equals(oldUserObject, this.host.DisposableInstance))
                {
                    this.behavior.ReleaseInstance(this, oldUserObject);
                }
            }
        }
 
        internal void UnbindRpc(ref MessageRpc rpc)
        {
            if (rpc.InstanceContext == this && rpc.SuccessfullyBoundInstance)
            {
                this.channels.DecrementActivityCount();
            }
        }
 
        internal void UnbindIncomingChannel(ServiceChannel channel)
        {
            this.channels.RemoveChannel((IChannel)channel.Proxy);
        }
 
        void Unload()
        {
            this.SetUserObject(null);
 
            if (this.host != null)
            {
                this.host.UnbindInstance(this);
            }
        }
 
        class CloseAsyncResult : AsyncResult
        {
            InstanceContext instanceContext;
            TimeoutHelper timeoutHelper;
 
            public CloseAsyncResult(TimeSpan timeout, AsyncCallback callback, object state, InstanceContext instanceContext)
                : base(callback, state)
            {
                this.timeoutHelper = new TimeoutHelper(timeout);
                this.instanceContext = instanceContext;
                IAsyncResult result = this.instanceContext.channels.BeginClose(this.timeoutHelper.RemainingTime(), PrepareAsyncCompletion(new AsyncCompletion(CloseChannelsCallback)), this);
                if (result.CompletedSynchronously && CloseChannelsCallback(result))
                {
                    base.Complete(true);
                }
            }
 
            public static void End(IAsyncResult result)
            {
                AsyncResult.End<CloseAsyncResult>(result);
            }
 
            bool CloseChannelsCallback(IAsyncResult result)
            {
                Fx.Assert(object.ReferenceEquals(this, result.AsyncState), "AsyncState should be this");
                this.instanceContext.channels.EndClose(result);
                this.instanceContext.Unload();
                return true;
            }
        }
    }
}