/// <summary> /// Checks the state of the subscriptions. /// </summary> private void AssignSubscriptionToRequest(QueuedSubscription subscription) { // find a request. for (LinkedListNode <QueuedRequest> node = m_queuedRequests.First; node != null; node = node.Next) { QueuedRequest request = node.Value; StatusCode error = StatusCodes.Good; // check if expired. if (request.Deadline < DateTime.MaxValue && request.Deadline.AddMilliseconds(500) < DateTime.UtcNow) { error = StatusCodes.BadTimeout; } // check secure channel. else if (!m_session.IsSecureChannelValid(request.SecureChannelId)) { error = StatusCodes.BadSecureChannelIdInvalid; Utils.Trace("Publish abandoned because the secure channel changed."); } if (StatusCode.IsBad(error)) { // remove request. LinkedListNode <QueuedRequest> next = node.Next; m_queuedRequests.Remove(node); node = next; // wake up thread with error. request.Error = error; request.Set(); if (node == null) { break; } continue; } // remove request. m_queuedRequests.Remove(node); // Utils.Trace("PUBLISH: #000 Assigned To Subscription({0}).", subscription.Subscription.Id); request.Error = StatusCodes.Good; request.Subscription = subscription; request.Subscription.Publishing = true; request.Set(); return; } // mark it as available. subscription.ReadyToPublish = true; subscription.Timestamp = DateTime.UtcNow; }
/// <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> /// Returns a subscription that is ready to publish. /// </summary> public Subscription Publish(uint clientHandle, DateTime deadline, bool requeue, AsyncPublishOperation operation) { QueuedRequest request = null; // DateTime queueTime = DateTime.UtcNow; // DateTime dequeueTime = DateTime.UtcNow; lock (m_lock) { if (m_queuedSubscriptions.Count == 0) { // TraceState("PUBLISH ERROR (BadNoSubscription)"); throw new ServiceResultException(StatusCodes.BadNoSubscription); } // find the waiting subscription with the highest priority. List <QueuedSubscription> subscriptions = new List <QueuedSubscription>(); for (int ii = 0; ii < m_queuedSubscriptions.Count; ii++) { QueuedSubscription subscription = m_queuedSubscriptions[ii]; if (subscription.ReadyToPublish && !subscription.Publishing) { subscriptions.Add(subscription); } } // find waiting the subscription that has been waiting the longest. if (subscriptions.Count > 0) { byte maxPriority = 0; DateTime earliestTimestamp = DateTime.MaxValue; QueuedSubscription subscriptionToPublish = null; for (int ii = 0; ii < subscriptions.Count; ii++) { QueuedSubscription subscription = subscriptions[ii]; if (subscription.Priority > maxPriority) { maxPriority = subscription.Priority; earliestTimestamp = DateTime.MaxValue; } if (subscription.Priority >= maxPriority && earliestTimestamp > subscription.Timestamp) { earliestTimestamp = subscription.Timestamp; subscriptionToPublish = subscription; } } // reset subscriptions flag. m_subscriptionsWaiting = false; for (int jj = 0; jj < m_queuedSubscriptions.Count; jj++) { if (m_queuedSubscriptions[jj].ReadyToPublish) { m_subscriptionsWaiting = true; break; } } // TraceState("REQUEST #{0} ASSIGNED TO WAITING SUBSCRIPTION", clientHandle); subscriptionToPublish.Publishing = true; return(subscriptionToPublish.Subscription); } // queue request because there is nothing waiting. if (subscriptions.Count == 0) { LinkedListNode <QueuedRequest> node = m_queuedRequests.First; while (node != null) { LinkedListNode <QueuedRequest> next = node.Next; QueuedRequest queuedRequest = node.Value; StatusCode requestStatus = StatusCodes.Good; // check if expired. if (queuedRequest.Deadline < DateTime.MaxValue && queuedRequest.Deadline.AddMilliseconds(500) < DateTime.UtcNow) { requestStatus = StatusCodes.BadTimeout; } // check secure channel. else if (!m_session.IsSecureChannelValid(queuedRequest.SecureChannelId)) { requestStatus = StatusCodes.BadSecureChannelIdInvalid; } // remove bad requests. if (StatusCode.IsBad(requestStatus)) { queuedRequest.Error = requestStatus; queuedRequest.Set(); m_queuedRequests.Remove(node); } node = next; } // clear excess requests - keep the newest ones. while (m_maxPublishRequests > 0 && m_queuedRequests.Count >= m_maxPublishRequests) { request = m_queuedRequests.First.Value; request.Error = StatusCodes.BadTooManyPublishRequests; request.Set(); m_queuedRequests.RemoveFirst(); } request = new QueuedRequest(); request.SecureChannelId = SecureChannelContext.Current.SecureChannelId; request.Deadline = deadline; request.Subscription = null; request.Error = StatusCodes.Good; if (operation == null) { request.Event = new ManualResetEvent(false); } else { request.Operation = operation; } if (requeue) { m_queuedRequests.AddFirst(request); // TraceState("REQUEST #{0} RE-QUEUED", clientHandle); } else { m_queuedRequests.AddLast(request); // TraceState("REQUEST #{0} QUEUED", clientHandle); } } } // check for non-blocking operation. if (operation != null) { // TraceState("PUBLISH: #{0} Async Request Queued.", clientHandle); return(null); } // wait for subscription. ServiceResult error = request.Wait(Timeout.Infinite); // check for error. if (ServiceResult.IsGood(error)) { if (StatusCode.IsBad(request.Error)) { error = request.Error; } } // must reassign subscription on error. if (ServiceResult.IsBad(request.Error)) { if (request.Subscription != null) { lock (m_lock) { request.Subscription.Publishing = false; AssignSubscriptionToRequest(request.Subscription); } } // TraceState("REQUEST #{0} PUBLISH ERROR ({1})", clientHandle, error.StatusCode); throw new ServiceResultException(request.Error); } // must be shuting down if this is null but no error. if (request.Subscription == null) { throw new ServiceResultException(StatusCodes.BadNoSubscription); } // TraceState("REQUEST #{0} ASSIGNED", clientHandle); // return whatever was assigned. return(request.Subscription.Subscription); }