/// <summary> /// Will at least try to process all queued system messages: in case of /// failure simply drop and go on to the next, because there is nothing to /// restart here (failure is in <see cref="ActorCell"/> somewhere …). In case the mailbox /// becomes closed (because of processing a <see cref="Terminate"/> message), dump all /// already dequeued message to deadLetters. /// </summary> private void ProcessAllSystemMessages() { Exception interruption = null; var messageList = SystemDrain(SystemMessageList.LNil); while (messageList.NonEmpty && !IsClosed()) { var msg = messageList.Head; messageList = messageList.Tail; msg.Unlink(); DebugPrint("{0} processing system message {1} with {2}", Actor.Self, msg, string.Join(",", Actor.GetChildren())); // we know here that SystemInvoke ensures that only "fatal" exceptions get rethrown Actor.SystemInvoke(msg); // don't ever execute normal message when system message present! if (messageList.IsEmpty && !IsClosed()) { messageList = SystemDrain(SystemMessageList.LNil); } } /* * if we closed the mailbox, we must dump the remaining system messages * to deadLetters (this is essential for DeathWatch) */ var dlm = Actor.Dispatcher.Mailboxes.DeadLetterMailbox; while (messageList.NonEmpty) { var msg = messageList.Head; messageList = messageList.Tail; msg.Unlink(); try { dlm.SystemEnqueue(Actor.Self, msg); } catch (Exception ex) { Actor.System.EventStream.Publish(new Error(ex, GetType().FullName, GetType(), $"error while enqueuing {msg} to deadletters: {ex.Message}")); } } // if we got an interruption while handling system messages, rethrow it if (interruption != null) { // no need to clear interrupted flag in CLR, unlike JVM throw interruption; } }
/// <summary> /// Dispatches a <see cref="ISystemMessage"/> from a mailbox to an <see cref="ActorCell"/> /// </summary> public virtual void SystemDispatch(ActorCell cell, Envelope envelope) { cell.SystemInvoke(envelope); }
private void Run() { if (_isClosed) { return; } var throughputDeadlineTime = dispatcher.ThroughputDeadlineTime; ActorCell.UseThreadContext(() => { //if ThroughputDeadlineTime is enabled, start a stopwatch if (throughputDeadlineTime.HasValue && throughputDeadlineTime.Value > 0) { if (_deadLineTimer != null) { _deadLineTimer.Restart(); } else { _deadLineTimer = Stopwatch.StartNew(); } } //we are about to process all enqueued messages hasUnscheduledMessages = false; Envelope envelope; //start with system messages, they have the highest priority while (_systemMessages.TryDequeue(out envelope)) { Mailbox.DebugPrint(ActorCell.Self + " processing system message " + envelope); // TODO: Add + " with " + ActorCell.GetChildren()); ActorCell.SystemInvoke(envelope); } //we should process x messages in this run var left = dispatcher.Throughput; //try dequeue a user message while (!_isSuspended && !_isClosed && _userMessages.TryDequeue(out envelope)) { Mailbox.DebugPrint(ActorCell.Self + " processing message " + envelope); //run the receive handler ActorCell.Invoke(envelope); //check if any system message have arrived while processing user messages if (_systemMessages.TryDequeue(out envelope)) { //handle system message Mailbox.DebugPrint(ActorCell.Self + " processing system message " + envelope); // TODO: Add + " with " + ActorCell.GetChildren()); ActorCell.SystemInvoke(envelope); break; } left--; if (_isClosed) { return; } //if deadline time have expired, stop and break if (throughputDeadlineTime.HasValue && throughputDeadlineTime.Value > 0 && _deadLineTimer.ElapsedTicks > throughputDeadlineTime.Value) { _deadLineTimer.Stop(); break; } //we are done processing messages for this run if (left == 0) { break; } } Interlocked.Exchange(ref status, MailboxStatus.Idle); //there are still messages that needs to be processed if (_systemMessages.Count > 0 || (!_isSuspended && _userMessages.Count > 0)) { //we still need has unscheduled messages for external info. //e.g. repointable actor ref uses it //TODO: will this be enough for external parties to work? hasUnscheduledMessages = true; //this is subject of a race condition //but that doesn't matter, since if the above "if" misses //the "Post" that adds the new message will still schedule //this specific call is just to deal with existing messages //that wasn't scheduled due to dispatcher throughput beeing reached //or system messages arriving during user message processing Schedule(); } }); }
/// <summary> /// Runs the Message Pump. /// </summary> private void Run() { if (_isClosed) { return; } ActorCell.UseThreadContext(() => { //if ThroughputDeadlineTime is enabled, start a stopwatch if (dispatcher.ThroughputDeadlineTime.HasValue) { if (_deadLineTimer != null) { _deadLineTimer.Restart(); } else { _deadLineTimer = Stopwatch.StartNew(); } } //we are about to process all enqueued messages hasUnscheduledMessages = false; Envelope envelope; //start with system messages, they have the highest priority while (_systemMessages.TryDequeue(out envelope)) { ActorCell.SystemInvoke(envelope); } //we should process x messages in this run int left = dispatcher.Throughput; //try dequeue a user message while (_userMessages.TryDequeue(out envelope)) { //run the receive handler ActorCell.Invoke(envelope); //check if any system message have arrived while processing user messages if (_systemMessages.TryDequeue(out envelope)) { //handle system message ActorCell.SystemInvoke(envelope); break; } left--; if (_isClosed) { return; } //if deadline time have expired, stop and break if (dispatcher.ThroughputDeadlineTime.HasValue && _deadLineTimer.ElapsedTicks > dispatcher.ThroughputDeadlineTime.Value) { _deadLineTimer.Stop(); break; } //we are done processing messages for this run if (left == 0) { break; } } //there are still messages that needs to be processed if (_userMessages.Count > 0) { hasUnscheduledMessages = true; } if (hasUnscheduledMessages) { dispatcher.Schedule(Run); } else { Interlocked.Exchange(ref status, MailboxStatus.Idle); } }); }