Commit graph

15 commits

Author SHA1 Message Date
Developer
902471b1f4 feat: ask pattern for typed request-response (Cycle 15)
Add Runtime::ask() and Ask<R> wrapper for convenient request-response.
Creates a temporary inbox, sends the request (with reply address via
closure), and provides recv_ticking() for automatic tick-until-response.
Purely sugar over the existing inbox pattern — no implicit auto-reply.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 13:26:59 +00:00
Developer
4d18874909 feat: actor groups with pub-sub broadcast (Cycle 14)
Add GroupRegistry for named actor groups. Actors join/leave groups via
rt.join_group() / ctx.join_group(), and messages can be broadcast to all
members via rt.publish_to() / ctx.publish(). Groups auto-create on first
join, auto-delete when empty, and members are auto-removed on death.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 13:22:35 +00:00
Developer
8782638193 feat: actor monitoring with Down message notifications (Cycle 13)
Add MonitorRegistry for death watch subscriptions. Actors subscribe via
ctx.monitor(target) and receive a Down { addr, reason } message when the
target dies (stop or panic). Supports stacking, demonitor, and automatic
cleanup of dead watcher subscriptions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 13:16:38 +00:00
Developer
66a8523473 feat: named actor registry with auto-cleanup on death (Cycle 12)
Add NameRegistry (String → ActorAddress) to delivery.rs with forward and
reverse maps for O(1) lookup and cleanup. Actors can be spawned with names
via rt.spawn_named() / ctx.spawn_named(), looked up via where_is(), and
names are automatically freed when actors stop or panic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 13:06:25 +00:00
Developer
9b1518b46c feat: property-based testing and extended fuzz targets (Cycle 11)
Add proptest-state-machine for stateful property testing of the runtime,
covering FIFO ordering, budget fairness, timer correctness, mailbox bounds,
and spawn tracking. Extend cargo-fuzz with stop, restart, and timer actions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 12:57:44 +00:00
Developer
d58a999db0 feat: per-worker tick-counting timers with one-shot and interval support
Adds deterministic tick-based timer scheduling to the actor runtime.
Timers are per-worker (no cross-thread sync) and tick-counted (not
wall-clock), making them suitable for simulation and testing.

API: ctx.send_after_ticks() for one-shot, ctx.send_interval_ticks()
for repeating timers. Timer messages route through the full delivery
system (local pool, cross-worker transfer, and external inboxes).

Fixes over-aggressive interval timer GC that incorrectly removed
timers targeting inboxes and cross-worker actors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 12:43:11 +00:00
Developer
e28aca099e feat: lifecycle hooks (on_start/on_stop) and graceful actor stop
Add actor lifecycle hooks and two-mode graceful stop mechanism:

- ActorInterface::on_start() called once before first message (panic = poison)
- ActorInterface::on_stop() called on graceful stop (NOT on panic - unsafe)
- ctx.stop_self() for immediate self-stop after current message
- runtime.stop_actor() for external stop (PoisonPill semantics, queued)
- Separate stats tracking: stops counter distinct from panics
- 12 new behavioral tests, 82 total passing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 12:27:08 +00:00
Developer
0213938964 fix: dead actor cleanup prevents AddressMap and ActorPool memory leak
Poisoned actors are now removed from ActorPool and AddressMap after
tick_all (phase 7). Previously they leaked indefinitely. Sends to
cleaned-up actors return Err instead of silently discarding — callers
learn the actor is gone.

Known bug class in Akka (#22990), C++ Actor Framework (#420).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 12:11:58 +00:00
Developer
1779ad63b9 feat: actor recovery via factory-based restart
Add spawn_restartable(actor, factory, max_restarts) to Runtime and Ctx.
On panic, the actor is recreated using the factory with fresh state and
cleared mailbox. After max_restarts exhausted, permanently poisoned.

AnyActor::try_restart() trait method with backward-compatible default.
Factory stored as Arc<dyn Fn() -> A + Send + Sync> — safe to read after
catch_unwind since factory fields are never touched by handle_any.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 12:05:21 +00:00
Developer
265992c3db feat: per-actor mailbox backpressure with configurable overflow
Add optional bounded mailboxes to prevent unbounded memory growth.
MailboxOverflow enum: DropNewest (discard incoming) or DropOldest
(evict oldest to make room). Default capacity=0 preserves unbounded
behavior for full backward compatibility.

messages_dropped counter added to WorkerStats for observability.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 11:54:16 +00:00
Developer
7d00e65a0a feat: load-aware actor placement + work stealing research
Replace blind round-robin placement with load-aware strategy that reads
per-worker stats (actor count + mailbox depth) and biases toward lighter
workers. Falls back to round-robin when stats are equal (initial burst).

Deep research on work stealing across Tokio (steal-half, LIFO slot),
Go (M:N scheduler), BEAM (proactive migration), and ForkJoinPool.
Full actor migration is feasible but deferred due to message-loss window.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 11:46:53 +00:00
Developer
cf616199b2 feat: shutdown unpark + bug-inspired tests
shutdown() now wakes all parked workers immediately for fast exit.
5 new tests from competitor bug patterns: ractor #310 (destructive
snapshots), kameo #185 (startup delivery), actix #515 (mailbox bypass),
plus stats-under-load and shutdown-wakes-parked validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 11:34:58 +00:00
Developer
acacc1b758 feat: thread parking for instant worker wakeup
Replace thread::sleep with thread::park_timeout in worker backoff loop.
Workers register their thread handle via OnceLock<Thread> on startup.
When send_to or spawn routes work to a worker, Thread::unpark() wakes
it instantly instead of waiting for the sleep timer to expire.

Inspired by tokio's parker state machine and Linux NO_HZ adaptive ticks.

Implementation:
- Runtime stores Vec<OnceLock<Thread>> for worker thread handles
- Workers call OnceLock::set(thread::current()) on startup
- Runtime::send_any, spawn_any, and WorkerContext cross-worker sends
  call notify_worker() → Thread::unpark() on the target worker
- TickContext carries worker_threads reference for cross-worker notification
- Zero new dependencies (std::sync::OnceLock + std:🧵:park_timeout)

Benefits:
- Parked workers wake instantly when work arrives (vs up to 1ms sleep delay)
- No overhead on hot path — unpark() is no-op if thread isn't parked
- Single-threaded tick() mode unaffected (OnceLock never set)

All 58 tests pass (52 runtime_api + 5 transport + 1 doctest).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 11:31:34 +00:00
Developer
10cb0780b7 feat: stress tests, expanded benchmarks, and research extension
Cycle 2 of the competitor analysis improvement loop.

Research additions:
- Kameo: async on tokio, dual bounded/unbounded mailbox (default 64),
  Erlang-style supervision links, vtable dispatch
- Actix: custom Vyukov lock-free MPSC queue (why it's fastest),
  256-message assertion guard (validates our budget), Context-as-Future

New stress tests (6):
- Message ordering preserved under small budget (budget=8)
- Multi-threaded: 50 senders × 100 msgs to one receiver (4 threads)
- Concurrent spawn+send of 200 actors (4 threads)
- 50-level chain spawning across 2 workers
- Panic isolation: 10 panicking + 10 healthy actors (4 threads)
- Sustained throughput: 10 batches of 100 msgs with interleaved ticks

New benchmark groups:
- msg_size: throughput and send_latency by message size (8B-4KB)
- contention: fanin (1-100 senders), cross_worker (1-4 threads)

All 57 tests pass (51 runtime_api + 5 transport + 1 doctest).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 11:24:33 +00:00
Developer
ef87f7e1b9 feat: per-actor message budget for tick fairness
Research across ractor, tokio, Erlang/OTP BEAM, Linux CFS, and libuv
revealed that tick_all drained the entire mailbox per actor per tick,
allowing one hot actor to starve all others on the same worker.

- Add `actor_message_budget` to RuntimeConfig (default: 64 msgs/actor/tick)
- Modify tick_all to break after budget messages, yielding to next actor
- budget=0 restores unlimited (backward compatible) behavior
- 3 new fairness tests validating hot-cold actor scenarios
- New fairness benchmark group (cold_latency_under_pressure, throughput_by_budget)
- Fix RuntimeConfig struct literals across workspace crates

Inspired by BEAM's 4000-reduction budget and tokio's 128-op cooperative budget.
All 45 tests pass (42 original + 3 new).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 11:11:30 +00:00