/// <summary> /// An overrideable version of the Dispose. /// </summary> protected virtual void Dispose(bool disposing) { if (disposing) { this.Error = StatusCodes.BadServerHalted; if (this.Operation != null) { this.Operation.Dispose(); this.Operation = null; } if (this.Event != null) { try { this.Event.Set(); this.Event.Close(); } catch (Exception) { // ignore errors. } } } }
/// <summary> /// Completes the publish. /// </summary> /// <param name="requeue">if set to <c>true</c> the request must be requeued.</param> /// <param name="operation">The asynchronous operation.</param> /// <param name="calldata">The calldata.</param> /// <returns></returns> public Subscription CompletePublish( bool requeue, AsyncPublishOperation operation, object calldata) { // Utils.Trace("PUBLISH: #{0} Completing", operation.RequestHandle, requeue); QueuedRequest request = (QueuedRequest)calldata; // check if need to requeue. lock (m_lock) { if (requeue) { request.Subscription = null; request.Error = StatusCodes.Good; m_queuedRequests.AddFirst(request); return(null); } } // must reassign subscription on error. if (ServiceResult.IsBad(request.Error)) { // Utils.Trace("PUBLISH: #{0} Reassigned ERROR({1})", operation.RequestHandle, 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); } // return whatever was assigned. return(request.Subscription.Subscription); }
/// <summary> /// An overrideable version of the Dispose. /// </summary> protected virtual void Dispose(bool disposing) { if (disposing) { this.Error = StatusCodes.BadServerHalted; if (this.Operation != null) { this.Operation.Dispose(); this.Operation = null; } if (this.Event != null) { try { this.Event.Set(); this.Event.Dispose(); } catch (Exception) { // ignore errors. } } } }
/// <summary> /// Completes the publish. /// </summary> /// <param name="requeue">if set to <c>true</c> the request must be requeued.</param> /// <param name="operation">The asynchronous operation.</param> /// <param name="calldata">The calldata.</param> /// <returns></returns> public Subscription CompletePublish( bool requeue, AsyncPublishOperation operation, object calldata) { Utils.Trace("PUBLISH: #{0} Completing", operation.RequestHandle, requeue); QueuedRequest request = (QueuedRequest)calldata; // check if need to requeue. lock (m_lock) { if (requeue) { request.Subscription = null; request.Error = StatusCodes.Good; m_queuedRequests.AddFirst(request); return null; } } // must reassign subscription on error. if (ServiceResult.IsBad(request.Error)) { Utils.Trace("PUBLISH: #{0} Reassigned ERROR({1})", operation.RequestHandle, 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); } // return whatever was assigned. return request.Subscription.Subscription; }
/// <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; }
/// <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); }
/// <summary> /// Begins an asynchronous publish operation. /// </summary> /// <param name="request">The request.</param> public virtual void BeginPublish(IEndpointIncomingRequest request) { PublishRequest input = (PublishRequest)request.Request; OperationContext context = ValidateRequest(input.RequestHeader, RequestType.Publish); try { AsyncPublishOperation operation = new AsyncPublishOperation(context, request, this); uint subscriptionId = 0; UInt32Collection availableSequenceNumbers = null; bool moreNotifications = false; NotificationMessage notificationMessage = null; StatusCodeCollection results = null; DiagnosticInfoCollection diagnosticInfos = null; notificationMessage = ServerInternal.SubscriptionManager.Publish( context, input.SubscriptionAcknowledgements, operation, out subscriptionId, out availableSequenceNumbers, out moreNotifications, out results, out diagnosticInfos); // request completed asychrnously. if (notificationMessage != null) { OnRequestComplete(context); operation.Response.ResponseHeader = CreateResponse(input.RequestHeader, context.StringTable); operation.Response.SubscriptionId = subscriptionId; operation.Response.AvailableSequenceNumbers = availableSequenceNumbers; operation.Response.MoreNotifications = moreNotifications; operation.Response.Results = results; operation.Response.DiagnosticInfos = diagnosticInfos; operation.Response.NotificationMessage = notificationMessage; Utils.Trace("PUBLISH: #{0} Completed Synchronously", input.RequestHeader.RequestHandle); request.OperationCompleted(operation.Response, null); } } catch (ServiceResultException e) { OnRequestComplete(context); lock (ServerInternal.DiagnosticsLock) { ServerInternal.ServerDiagnostics.RejectedRequestsCount++; if (IsSecurityError(e.StatusCode)) { ServerInternal.ServerDiagnostics.SecurityRejectedRequestsCount++; } } throw TranslateException(context, e); } }
/// <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> /// Completes the publish. /// </summary> /// <param name="context">The context.</param> /// <param name="operation">The asynchronous operation.</param> /// <returns> /// True if successful. False if the request has been requeued. /// </returns> public bool CompletePublish( OperationContext context, AsyncPublishOperation operation) { // get publish queue for session. SessionPublishQueue queue = null; lock (m_lock) { if (!m_publishQueues.TryGetValue(context.Session.Id, out queue)) { throw new ServiceResultException(StatusCodes.BadSessionClosed); } } uint subscriptionId = 0; UInt32Collection availableSequenceNumbers = null; bool moreNotifications = false; NotificationMessage message = null; Utils.Trace("Publish #{0} ReceivedFromClient", context.ClientHandle); bool requeue = false; do { // wait for a subscription to publish. Subscription subscription = queue.CompletePublish(requeue, operation, operation.Calldata); if (subscription == null) { return false; } 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); // fill in response if operation completed. if (message != null) { operation.Response.SubscriptionId = subscriptionId; operation.Response.AvailableSequenceNumbers = availableSequenceNumbers; operation.Response.MoreNotifications = moreNotifications; operation.Response.NotificationMessage = message; // update diagnostics. if (context.Session != null) { lock (context.Session.DiagnosticsLock) { SessionDiagnosticsDataType diagnostics = context.Session.SessionDiagnostics; diagnostics.CurrentPublishRequestsInQueue--; } } } return true; }
/// <summary> /// Publishes a subscription. /// </summary> public NotificationMessage Publish( OperationContext context, SubscriptionAcknowledgementCollection subscriptionAcknowledgements, AsyncPublishOperation operation, out uint subscriptionId, out UInt32Collection availableSequenceNumbers, out bool moreNotifications, out StatusCodeCollection acknowledgeResults, out DiagnosticInfoCollection acknowledgeDiagnosticInfos) { availableSequenceNumbers = null; moreNotifications = false; // get publish queue for session. SessionPublishQueue queue = null; lock (m_lock) { if (!m_publishQueues.TryGetValue(context.Session.Id, out queue)) { if (m_subscriptions.Count == 0) { throw new ServiceResultException(StatusCodes.BadNoSubscription); } throw new ServiceResultException(StatusCodes.BadSessionClosed); } } // acknowlege previous messages. queue.Acknowledge( context, subscriptionAcknowledgements, out acknowledgeResults, out acknowledgeDiagnosticInfos); // update diagnostics. if (context.Session != null) { lock (context.Session.DiagnosticsLock) { SessionDiagnosticsDataType diagnostics = context.Session.SessionDiagnostics; diagnostics.CurrentPublishRequestsInQueue++; } } // save results for asynchrounous operation. if (operation != null) { operation.Response.Results = acknowledgeResults; operation.Response.DiagnosticInfos = acknowledgeDiagnosticInfos; } // gets the next message that is ready to publish. NotificationMessage message = GetNextMessage( context, queue, operation, out subscriptionId, out availableSequenceNumbers, out moreNotifications); // if no message and no async operation then a timeout occurred. if (message == null && operation == null) { throw new ServiceResultException(StatusCodes.BadTimeout); } // return message. return message; }