Manages the publish queues for a session.
Inheritance: IDisposable
		/// <summary>
		/// Creates a new subscription.
		/// </summary>
        public virtual void CreateSubscription(
            OperationContext context,
            double           requestedPublishingInterval,
            uint             requestedLifetimeCount,
            uint             requestedMaxKeepAliveCount,
            uint             maxNotificationsPerPublish,
            bool             publishingEnabled,
            byte             priority,
            out uint         subscriptionId,
            out double       revisedPublishingInterval,
            out uint         revisedLifetimeCount,
            out uint         revisedMaxKeepAliveCount)
        {
            subscriptionId = 0;
            revisedPublishingInterval = 0;
            revisedLifetimeCount = 0;
            revisedMaxKeepAliveCount = 0;
            
            uint publishingIntervalCount = 0;
            Subscription subscription = null;

            // get sessin from context.
            Session session = context.Session;

            // assign new identifier.
            subscriptionId = Utils.IncrementIdentifier(ref m_lastSubscriptionId);

            // calculate publishing interval.
            revisedPublishingInterval = CalculatePublishingInterval(requestedPublishingInterval);

            // calculate the keep alive count.
            revisedMaxKeepAliveCount = CalculateKeepAliveCount(revisedPublishingInterval, requestedMaxKeepAliveCount);

            // calculate the lifetime count.
            revisedLifetimeCount = CalculateLifetimeCount(revisedPublishingInterval, revisedMaxKeepAliveCount, requestedLifetimeCount);

            // calculate the max notification count.
            maxNotificationsPerPublish = CalculateMaxNotificationsPerPublish(maxNotificationsPerPublish);

            // create the subscription.
            subscription = CreateSubscription(
                context,
                subscriptionId,
                revisedPublishingInterval,
                revisedLifetimeCount,
                revisedMaxKeepAliveCount,
                maxNotificationsPerPublish,
                priority,
                publishingEnabled);
                
            lock (m_lock)
            {
                if (m_subscriptions.Count >= m_maxSubscriptionCount)
                {
                    throw new ServiceResultException(StatusCodes.BadTooManySubscriptions);
                }

                // save subscription.
                m_subscriptions.Add(subscriptionId, subscription);
                
                // create/update publish queue.
                SessionPublishQueue queue = null;

                if (!m_publishQueues.TryGetValue(session.Id, out queue))
                {
                    m_publishQueues[session.Id] = queue = new SessionPublishQueue(m_server, session, m_maxPublishRequestCount);
                }
                
                queue.Add(subscription);

                // get the count for the diagnostics.
                publishingIntervalCount = GetPublishingIntervalCount();
            }

            lock (m_statusMessages)
            {
                Queue<StatusMessage> messagesQueue = null;
                if (!m_statusMessages.TryGetValue(session.Id, out messagesQueue))
                {
                    m_statusMessages[session.Id] = new Queue<StatusMessage>();
                }
            }
            
            lock (m_server.DiagnosticsWriteLock)
            {
                ServerDiagnosticsSummaryDataType diagnostics = m_server.ServerDiagnostics;
                diagnostics.CurrentSubscriptionCount++;
                diagnostics.CumulatedSubscriptionCount++;
                diagnostics.PublishingIntervalCount = publishingIntervalCount;
            }

            if (context.Session != null)
            {
                lock (context.Session.DiagnosticsLock)
                {
                    SessionDiagnosticsDataType diagnostics = context.Session.SessionDiagnostics;
                    diagnostics.CurrentSubscriptionsCount++;
                }
            }

            // raise subscription event.
            RaiseSubscriptionEvent(subscription, false);
        }
		/// <summary>
		/// Publishes a subscription.
		/// </summary>
        public NotificationMessage GetNextMessage(
            OperationContext context,
            SessionPublishQueue queue,
            AsyncPublishOperation operation,
            out uint subscriptionId, 
            out UInt32Collection availableSequenceNumbers, 
            out bool moreNotifications)
        {
            subscriptionId = 0;
            availableSequenceNumbers = null;
            moreNotifications = false;

            NotificationMessage message = null;

            try
            {
                Utils.Trace("Publish #{0} ReceivedFromClient", context.ClientHandle);

                // check for status messages.
                lock (m_statusMessages)
                {
                    Queue<StatusMessage> statusQueue = null;

                    if (m_statusMessages.TryGetValue(context.SessionId, out statusQueue))
                    {
                        if (statusQueue.Count > 0)
                        {
                            StatusMessage status = statusQueue.Dequeue();
                            subscriptionId = status.SubscriptionId;
                            return status.Message;
                        }
                    }
                }

                bool requeue = false;

                do
                {
                    // wait for a subscription to publish.
                    Subscription subscription = queue.Publish(
                        context.ClientHandle,
                        context.OperationDeadline,
                        requeue,
                        operation);

                    if (subscription == null)
                    {
                        Utils.Trace("Publish #{0} Timeout", context.ClientHandle);
                        return null;
                    }

                    subscriptionId = subscription.Id;
                    moreNotifications = false;

                    // publish notifications.
                    try
                    {
                        requeue = false;

                        message = subscription.Publish(
                            context,
                            out availableSequenceNumbers,
                            out moreNotifications);

                        // a null message indicates a false alarm and that there were no notifications
                        // to publish and that the request needs to be requeued.
                        if (message != null)
                        {
                            break;
                        }

                        Utils.Trace("Publish False Alarm - Request #{0} Requeued.", context.ClientHandle);
                        requeue = true;
                    }
                    finally
                    {
                        queue.PublishCompleted(subscription, moreNotifications);
                    }
                }
                while (requeue);
            }
            finally
            {            
                // update diagnostics.
                if (context.Session != null)
                {
                    lock (context.Session.DiagnosticsLock)
                    {
                        SessionDiagnosticsDataType diagnostics = context.Session.SessionDiagnostics;
                        diagnostics.CurrentPublishRequestsInQueue--;
                    }
                }
            }

            return message;
        }
        /// <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");
            }
        }