public Publish ( uint clientHandle, System.DateTime deadline, bool requeue, |
||
clientHandle | uint | |
deadline | System.DateTime | |
requeue | bool | |
operation | ||
return |
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> /// 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; }