Rate this Page

Multiplexers#

Muxers (short for multiplexers) form the first level of indirection in the mailbox subsystem. While a Mailbox delivers messages to typed ports within a single actor, a MailboxMuxer delivers messages to the correct mailbox instance given an ActorId.

It acts as a dynamic registry, allowing multiple mailboxes to be addressed through a single posting interface.

This page introduces the MailboxMuxer and its role in:

  • Aggregating multiple mailbox instances

  • Dispatching incoming messages to the appropriate MailboxSender

  • Supporting dynamic binding and unbinding of mailboxes

Let’s begin by looking at the core structure of MailboxMuxer:

pub struct MailboxMuxer {
    mailboxes: Arc<DashMap<ActorId, Box<dyn MailboxSender + Send + Sync>>>,
}

The MailboxMuxer maintains a thread-safe, concurrent map from ActorId to MailboxSender trait objects. Each entry represents a live binding to a mailbox capable of receiving messages for a specific actor. This allows the muxer to act as a single dispatch point for delivering messages to any number of registered actors, abstracting over the details of how and where each mailbox is implemented.

To register a mailbox with the muxer, callers use the bind method:

impl MailboxMuxer {
    pub fn bind(&self, actor_id: ActorId, sender: impl MailboxSender + 'static) -> bool {
        match self.mailboxes.entry(actor_id) {
            Entry::Occupied(_) => false,
            Entry::Vacant(entry) => {
                entry.insert(Box::new(sender));
                true
            }
        }
    }

}

This function installs a new mapping from the given ActorId to a boxed MailboxSender. If the ActorId is already registered, the bind fails (returns false), and the existing sender is left unchanged. This ensures that actors cannot be accidentally rebound without first explicitly unbinding them—enforcing a clear handoff protocol. To rebind, the caller must invoke unbind first.

It’s crucial to recall that Mailbox itself implements the MailboxSender trait. This is what allows it to be registered directly into a MailboxMuxer. The post method of a Mailbox inspects the incoming MessageEnvelope to determine whether it is the intended recipient. If the ActorId in the envelope matches the mailbox’s own ID, the mailbox delivers the message locally: it looks up the appropriate port by index and invokes send_serialized on the matching channel. If the ActorId does not match, the mailbox delegates the message to its internal forwarder by calling self.state.forwarder.post(envelope).

With this behavior in mind, we can now define a convenience method for registering a full Mailbox:

impl MailboxMuxer {
  fn bind_mailbox(&self, mailbox: Mailbox) -> bool {
    self.bind(mailbox.actor_id().clone(), mailbox)
  }
}

To support rebinding or teardown, the muxer also provides a symmetric unbind function, which removes the sender associated with a given ActorId:

    pub(crate) fn unbind(&self, actor_id: &ActorId) {
        self.mailboxes.remove(actor_id);
    }

And of course, we can implement MailboxSender for MailboxMuxer itself—allowing it to act as a unified dispatcher for all registered mailboxes:

impl MailboxSender for MailboxMuxer {
    fn post(
        &self,
        envelope: MessageEnvelope,
        return_handle: PortHandle<Undeliverable<MessageEnvelope>>,
    ) {
        let dest_actor_id = envelope.dest().actor_id();
        match self.mailboxes.get(envelope.dest().actor_id()) {
            None => {
                let err = format!("no mailbox for actor {} registered in muxer", dest_actor_id);
                envelope.undeliverable(DeliveryError::Unroutable(err), return_handle)
            }
            Some(sender) => sender.post(envelope, return_handle),
        }
    }
}

This makes MailboxMuxer composable: it can be nested within other routers, shared across components, or substituted for a standalone mailbox in generic code. If the destination ActorId is found in the internal map, the message is forwarded to the corresponding sender. Otherwise, it is marked as undeliverable with an appropriate DeliveryError.