Manages a subscription created by a client.
상속: ISubscription, IDisposable
        /// <summary>
        /// Adds a subscription back into the queue because it has more notifications to publish.
        /// </summary>
        public void PublishCompleted(Subscription subscription, bool moreNotifications)
        {
            lock (m_lock)
            {
                for (int ii = 0; ii < m_queuedSubscriptions.Count; ii++)
                {
                    if (Object.ReferenceEquals(m_queuedSubscriptions[ii].Subscription, subscription))
                    {
                        m_queuedSubscriptions[ii].Publishing = false;

                        if (moreNotifications)
                        {
                            AssignSubscriptionToRequest(m_queuedSubscriptions[ii]);
                        }
                        else
                        {
                            m_queuedSubscriptions[ii].ReadyToPublish = false;
                            m_queuedSubscriptions[ii].Timestamp = DateTime.UtcNow;
                        }

                        break;
                    }
                }
            }
        }
        /// <summary>
        /// Adds a subscription from the publish queue.
        /// </summary>
        public void Add(Subscription subscription)
        {
            if (subscription == null) throw new ArgumentNullException("subscription");

            lock (m_lock)
            {
                QueuedSubscription queuedSubscription = new QueuedSubscription();

                queuedSubscription.Priority = subscription.Priority;
                queuedSubscription.ReadyToPublish = false;
                queuedSubscription.Timestamp = DateTime.UtcNow;
                queuedSubscription.Subscription = subscription;

                m_queuedSubscriptions.Add(queuedSubscription);      

                // TraceState("SUBSCRIPTION QUEUED");          
            }
        }
        /// <summary>
        /// Removes a subscription from the publish queue.
        /// </summary>
        public void Remove(Subscription subscription)
        {
            if (subscription == null) throw new ArgumentNullException("subscription");

            lock (m_lock)
            {
                // remove the subscription from the queue.
                for (int ii = 0; ii < m_queuedSubscriptions.Count; ii++)
                {
                    if (Object.ReferenceEquals(m_queuedSubscriptions[ii].Subscription, subscription))
                    {
                        m_queuedSubscriptions.RemoveAt(ii);
                        break;
                    }
                }

                // remove any outstanding publishes.
                if (m_queuedSubscriptions.Count == 0)
                {
                    while (m_queuedRequests.Count > 0)
                    {
                        QueuedRequest request = m_queuedRequests.First.Value;
                        request.Error = StatusCodes.BadNoSubscription;
                        request.Set();
                        m_queuedRequests.RemoveFirst();
                    }
                }
                
                // TraceState("SUBSCRIPTION REMOVED");
            }
        }
        /// <summary>
        /// Clears the queues because the session is closing.
        /// </summary>
        /// <returns>The list of subscriptions in the queue.</returns>
        public IList<Subscription> Close()
        {            
            lock (m_lock)
            {
                // TraceState("SESSION CLOSED");

                // wake up any waiting publish requests.
                m_publishEvent.Set();

                while (m_queuedRequests.Count > 0)
                {
                    QueuedRequest request = m_queuedRequests.First.Value;
                    m_queuedRequests.RemoveFirst();
                    request.Error = StatusCodes.BadSessionClosed;
                    request.Set();
                }
                
                // tell the subscriptions that the session is closed.
                Subscription[] subscriptions = new Subscription[m_queuedSubscriptions.Count];

                for (int ii = 0; ii < m_queuedSubscriptions.Count; ii++)
                {
                    subscriptions[ii] = m_queuedSubscriptions[ii].Subscription;
                    subscriptions[ii].SessionClosed();
                }

                // clear the queue.
                m_queuedSubscriptions.Clear();

                return subscriptions;
            }
        }
        /// <summary>
        /// Called when a subscription expires.
        /// </summary>
        /// <param name="subscription">The subscription.</param>
        internal void SubscriptionExpired(Subscription subscription)
        {
            lock (m_statusMessages)
            {
                StatusMessage message = new StatusMessage();
                message.SubscriptionId = subscription.Id;
                message.Message = subscription.PublishTimeout();

                Queue<StatusMessage> queue = null;

                if (subscription.SessionId != null && m_statusMessages.TryGetValue(subscription.SessionId, out queue))
                {
                    queue.Enqueue(message);
                }
            }
        }
        /// <summary>
        /// Raises an event related to a subscription.
        /// </summary>
        protected virtual void RaiseSubscriptionEvent(Subscription subscription, bool deleted)
        { 
            SubscriptionEventHandler handler = null;

            lock (m_eventLock)
            {
                handler = m_SubscriptionCreated;

                if (deleted)
                {
                    handler = m_SubscriptionDeleted;
                }
            } 

            if (handler != null)
            {
                try
                {
                    handler(subscription, deleted);
                }
                catch (Exception e)
                {
                    Utils.Trace(e, "Subscription event handler raised an exception.");
                }
            }
        }
        /// <summary>
        /// Creates a new instance of a subscription.
        /// </summary>
        protected virtual Subscription CreateSubscription(
            OperationContext context,
            uint             subscriptionId,
			double           publishingInterval,
			uint             lifetimeCount,
			uint             keepAliveCount,
            uint             maxNotificationsPerPublish,
            byte             priority,
			bool             publishingEnabled)
        {
            Subscription subscription = new Subscription(
                m_server,
                context.Session,
                subscriptionId,
                publishingInterval,
                lifetimeCount,
                keepAliveCount,
                maxNotificationsPerPublish,
                priority,
                publishingEnabled,
                m_maxMessageCount);

            return subscription;
        }
        /// <summary>
        /// Periodically checks if the sessions have timed out.
        /// </summary>
        private void PublishSubscriptions(object data)
        {
            try
            {
                Utils.Trace("Server: Publish Subscriptions Thread Started.");

                int sleepCycle = Convert.ToInt32(data, CultureInfo.InvariantCulture);
                int timeToWait = sleepCycle;

                do
                {
                    DateTime start = DateTime.UtcNow;

                    SessionPublishQueue[] queues = null;
                    Subscription[] abandonedSubscriptions = null;

                    lock (m_lock)
                    {
                        // collect active session queues.
                        queues = new SessionPublishQueue[m_publishQueues.Count];
                        m_publishQueues.Values.CopyTo(queues, 0);

                        // collect abandoned subscriptions.
                        if (m_abandonedSubscriptions != null && m_abandonedSubscriptions.Count > 0)
                        {
                            abandonedSubscriptions = new Subscription[m_abandonedSubscriptions.Count];

                            for (int ii = 0; ii < abandonedSubscriptions.Length; ii++)
                            {
                                abandonedSubscriptions[ii] = m_abandonedSubscriptions[ii];
                            }
                        }
                    }

                    // check the publish timer for each subscription.
                    for (int ii = 0; ii < queues.Length; ii++)
                    {
                        queues[ii].PublishTimerExpired();
                    }

                    // check the publish timer for each abandoned subscription.
                    if (abandonedSubscriptions != null)
                    {
                        List<Subscription> subscriptionsToDelete = new List<Subscription>();

                        for (int ii = 0; ii < abandonedSubscriptions.Length; ii++)
                        {
                            Subscription subscription = abandonedSubscriptions[ii];

                            if (subscription.PublishTimerExpired() != PublishingState.Expired)
                            {
                                continue;
                            }

                            if (subscriptionsToDelete == null)
                            {
                                subscriptionsToDelete = new List<Subscription>();
                            }

                            subscriptionsToDelete.Add(subscription);
                            SubscriptionExpired(subscription);
                            Utils.Trace("Server: Abandoned Subscription '{0}' Delete Scheduled.", subscription.Id);
                        }

                        // schedule cleanup on a background thread.
                        if (subscriptionsToDelete.Count > 0)
                        {
                            lock (m_lock)
                            {
                                for (int ii = 0; ii < subscriptionsToDelete.Count; ii++)
                                {
                                    m_abandonedSubscriptions.Remove(subscriptionsToDelete[ii]);
                                }
                            }

                            CleanupSubscriptions(m_server, subscriptionsToDelete);
                        }
                    }

                    if (m_shutdownEvent.WaitOne(timeToWait))
                    {
                        Utils.Trace("Server: Publish Subscriptions Thread Exited Normally.");
                        break;
                    }

                    int delay = (int)(DateTime.UtcNow - start).TotalMilliseconds;
                    timeToWait = sleepCycle;
                }
                while (true);
            }
            catch (Exception e)
            {
                Utils.Trace(e, "Server: Publish Subscriptions Thread Exited Unexpectedly");
            }
        }