lambdaclass / lambdaclass/spawned

Unify MailboxItem<A> across tasks and threads modes

Open
#169 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
59
Forks
5
PR merge metrics
No merged PRs in 30d

Description

Why

Two recent PRs introduced ad-hoc paths for non-Handler<M> mailbox traffic, leaving the codebase with two mechanisms solving the same problem:

  1. #166 (links + trap_exit) added ExitEnvelope — a Box<dyn Envelope<A>> that bypasses Handler<M> and calls actor.exit_received() directly. Avoids forcing every linkable actor to impl Handler<Exit>, but unusual.

  2. #168 (perf fix for #157) added MailboxItem<A> enum in threads mode only, with Message(envelope) and Shutdown variants for the poison-pill wakeup pattern. Works, but introduces divergence between tasks and threads.

Both are essentially "how to deliver something through the mailbox that isn't a user protocol message" — two ad-hoc patterns when one would do.

What to build

Promote MailboxItem<A> to a formal internal type used uniformly in both modes:

pub(crate) enum MailboxItem<A: Actor> {
    Message(Box<dyn Envelope<A> + Send>),
    Exit(Exit),
    Shutdown,
}

The run_actor loop becomes structurally identical across modes — same match on the enum, differing only in await vs sync call.

Key design choices

  • Cancellation auto-wake via on_cancel callback — register at spawn time so any cancellation_token.cancel() enqueues MailboxItem::Shutdown automatically. Removes the hidden coupling where ctx.stop() and the ChildHandle cancel closure had to manually enqueue Shutdown.
  • MailboxItem<A> stays pub(crate) — internal abstraction, not a user-facing API.
  • Down is NOT moved into this mechanism — stays as Handler<Down> because monitoring is opt-in.
  • User-facing API fully preserved: ctx.link/unlink/trap_exit, Actor::exit_received, start_linked, ctx.monitor/demonitor, Handler<Down>, ctx.stop(), ChildHandle::stop() all unchanged.

What changes internally

  • ExitEnvelope removed (replaced by MailboxItem::Exit(Exit) variant)
  • SendExitFn body changes (signature unchanged) — builds the enum variant instead of a boxed envelope
  • Mailbox channel type in both modes: mpsc::Sender<MailboxItem<A>>
  • ctx.stop() in threads mode simplifies back to one line (self.cancellation_token.cancel()) — the on_cancel callback does the enqueue
  • Envelope<A> trait no longer needs to support system-message use cases — MessageEnvelope<M> is the only impl

Order of operations

Must land after #166 and #168 to avoid three-way rebase hell. Both are prerequisites:

  • #166 introduces Actor::exit_received (the dispatch point that stays)
  • #168 introduces the MailboxItem enum shape (gets promoted)

Design artifacts

Detailed proposal, design, specs, and task breakdown are checked into openspec/changes/unified-mailbox-items/:

  • proposal.md — why
  • design.md — 6 design decisions + risks + open questions
  • specs/mailbox-items/spec.md — 7 requirements with testable scenarios
  • tasks.md — ~40 implementation tasks

The OpenSpec change can be applied with /opsx:apply unified-mailbox-items once prerequisites are merged.

Acceptance criteria

  • All existing tests pass with no modification (proves user-facing behavior is preserved)
  • grep -r ExitEnvelope concurrency/ returns zero matches
  • Threads-mode shutdown latency is single-digit milliseconds (no polling regression)
  • Direct cancellation_token.cancel() (without calling ctx.stop()) wakes the actor — proves the on_cancel path
  • At least one regression test covering FIFO ordering of Exit and Message items

Credit

Surfaced during review of #168 — thanks @lakshya-sky for the perf fix that made the unification opportunity visible.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

First read openspec/changes/unified-mailbox-items/proposal.md, design.md, specs/mailbox-items/spec.md, and tasks.md, then confirm prerequisites #166 and #168 are merged. Trace run_actor, SendExitFn, and the mailbox channel definitions in concurrency/ before checking both execution modes. Done means existing tests pass, direct cancellation wakes actors, FIFO ordering is covered, threads shutdown remains single-digit milliseconds, and grep finds no ExitEnvelope matches.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
distributed-systems
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.