mailbox: Phase 4b — send_dyn bare-pid escape hatch (RFC 014 §4.6)
The explicit fallible fallback for when only an untyped Pid is in hand (off a Down, out of a future members()), so the typed send_to / send-by-name stay the default and callers don't grow workarounds around a missing primitive. - send_dyn::<M>(Pid, msg): one line over the shared send_to_pid core. Same identity-bound, no-redirect liveness as send_to (Dead once the incarnation is gone); the difference is the explicit M, so NoChannel is a real runtime outcome here rather than the debug_assert it collapses to on the typed paths. - Takes Pid<Erased> specifically: a typed Pid<A> must .erase() to reach it, so opting into the fallible downcast stays visible at the call site. tests/registry.rs: send_dyn_delivers_and_reports_wrong_type (right type delivers; live actor with no channel for the asked type -> NoChannel) and send_dyn_to_dead_pid_is_dead. Full suite + order-checker green, warning-free. This completes the RFC 014 send surface: Name<M> (re-resolving), Pid<A> (typed direct), send_dyn (bare-pid). send_after (§6) can now target any of them — its Dest question is answered.
This commit is contained in:
+3
-1
@@ -56,7 +56,9 @@ pub use monitor::{demonitor, monitor, Down, DownReason, Monitor, MonitorId};
|
|||||||
pub use mutex::{LockTimeout, Mutex, MutexGuard};
|
pub use mutex::{LockTimeout, Mutex, MutexGuard};
|
||||||
pub use pid::{Addressable, Erased, Name, Pid, RawPid};
|
pub use pid::{Addressable, Erased, Name, Pid, RawPid};
|
||||||
pub use pg::{join, leave, members, pick, Incarnation, Member, NodeId};
|
pub use pg::{join, leave, members, pick, Incarnation, Member, NodeId};
|
||||||
pub use registry::{install, register, send, send_to, unregister, whereis, RegisterError, SendError};
|
pub use registry::{
|
||||||
|
install, register, send, send_dyn, send_to, unregister, whereis, RegisterError, SendError,
|
||||||
|
};
|
||||||
pub use runtime::{init, Config, Runtime};
|
pub use runtime::{init, Config, Runtime};
|
||||||
pub use scheduler::{
|
pub use scheduler::{
|
||||||
block_on_io, request_stop, run, self_pid, sleep, spawn, spawn_under, wait_readable,
|
block_on_io, request_stop, run, self_pid, sleep, spawn, spawn_under, wait_readable,
|
||||||
|
|||||||
@@ -398,3 +398,21 @@ fn send_to_pid<M: Send + 'static>(
|
|||||||
pub fn send_to<A: Addressable>(pid: Pid<A>, msg: A::Msg) -> Result<(), SendError<A::Msg>> {
|
pub fn send_to<A: Addressable>(pid: Pid<A>, msg: A::Msg) -> Result<(), SendError<A::Msg>> {
|
||||||
with_runtime(|inner| send_to_pid::<A::Msg>(inner, pid.erase(), msg))
|
with_runtime(|inner| send_to_pid::<A::Msg>(inner, pid.erase(), msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The explicit bare-pid escape hatch (RFC 014 §4.6): deliver `msg` of type `M`
|
||||||
|
/// to `pid` when all you hold is an untyped [`Pid`] — a pid off a [`Down`], or
|
||||||
|
/// out of a future `members()` — so the typed [`send_to`] is unavailable.
|
||||||
|
///
|
||||||
|
/// This is the one send whose message type can genuinely be wrong: the actor
|
||||||
|
/// may be live yet expose no channel for `M`, returning [`SendError::NoChannel`]
|
||||||
|
/// (on the typed paths that downcast collapses to a `debug_assert`). It is
|
||||||
|
/// named and documented as the fallible fallback so the typed `Pid<A>` /
|
||||||
|
/// `Name<M>` paths stay the obvious default and an agentic caller reaches for a
|
||||||
|
/// present primitive instead of inventing a workaround. Liveness is identical
|
||||||
|
/// to [`send_to`]: identity-bound, no redirect, [`SendError::Dead`] once the
|
||||||
|
/// addressed incarnation is gone. Panics if called outside `Runtime::run()`.
|
||||||
|
///
|
||||||
|
/// [`Down`]: crate::Down
|
||||||
|
pub fn send_dyn<M: Send + 'static>(pid: Pid, msg: M) -> Result<(), SendError<M>> {
|
||||||
|
with_runtime(|inner| send_to_pid::<M>(inner, pid, msg))
|
||||||
|
}
|
||||||
|
|||||||
+42
-2
@@ -6,8 +6,8 @@
|
|||||||
//! register-then-send race without busy-waiting on `whereis`.
|
//! register-then-send race without busy-waiting on `whereis`.
|
||||||
|
|
||||||
use smarm::{
|
use smarm::{
|
||||||
channel, install, register, run, send, send_to, spawn, unregister, whereis, Addressable, Name,
|
channel, install, register, run, send, send_dyn, send_to, spawn, unregister, whereis,
|
||||||
Pid, RegisterError, SendError,
|
Addressable, Name, Pid, RegisterError, SendError,
|
||||||
};
|
};
|
||||||
|
|
||||||
const SVC: Name<u64> = Name::new("svc");
|
const SVC: Name<u64> = Name::new("svc");
|
||||||
@@ -209,3 +209,43 @@ fn send_to_does_not_redirect_after_takeover() {
|
|||||||
b.join().unwrap();
|
b.join().unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- RFC 014 §4.6: explicit bare-pid escape hatch ---------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn send_dyn_delivers_and_reports_wrong_type() {
|
||||||
|
run(|| {
|
||||||
|
let (ready_tx, ready_rx) = channel::<()>();
|
||||||
|
let (done_tx, done_rx) = channel::<()>();
|
||||||
|
let (tx, rx) = channel::<u64>();
|
||||||
|
let h = spawn(move || {
|
||||||
|
register(Name::<u64>::new("dyn"), tx).unwrap(); // publishes a u64 channel
|
||||||
|
ready_tx.send(()).unwrap();
|
||||||
|
assert_eq!(rx.recv().unwrap(), 3);
|
||||||
|
done_rx.recv().unwrap(); // stay alive for the wrong-type probe
|
||||||
|
});
|
||||||
|
ready_rx.recv().unwrap();
|
||||||
|
let p = h.pid(); // a bare Pid<Erased>, as if recovered off a Down
|
||||||
|
send_dyn::<u64>(p, 3u64).unwrap(); // right type: delivered
|
||||||
|
// Live actor, but it has no channel for &str — the genuinely-fallible case.
|
||||||
|
assert!(matches!(send_dyn::<&'static str>(p, "nope"), Err(SendError::NoChannel(_))));
|
||||||
|
done_tx.send(()).unwrap();
|
||||||
|
h.join().unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn send_dyn_to_dead_pid_is_dead() {
|
||||||
|
run(|| {
|
||||||
|
let (ready_tx, ready_rx) = channel::<()>();
|
||||||
|
let (tx, _rx) = channel::<u64>();
|
||||||
|
let h = spawn(move || {
|
||||||
|
register(Name::<u64>::new("dyn2"), tx).unwrap();
|
||||||
|
ready_tx.send(()).unwrap(); // then return -> die
|
||||||
|
});
|
||||||
|
ready_rx.recv().unwrap();
|
||||||
|
let p = h.pid();
|
||||||
|
h.join().unwrap(); // dead; the bare pid now names a dead incarnation
|
||||||
|
assert!(matches!(send_dyn::<u64>(p, 1u64), Err(SendError::Dead(_))));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user