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