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