monitor: widen stamp eligibility to watchable = named ∪ exported (soak sig 5)
The terminal record existed for watches that raced their target's death, but
e43c673 scoped its stamp to named tenancies — and the pid-identity watch
surface (§4 Slice 3) targets arbitrary actors, including anonymous ones whose
pids cross the boundary in contract replies. The first wild pid-face hit
(width-20 soak, pid_watch_test.exs:47, 1/600 full-suite: a monitor installed
while the child was alive delivered :noproc instead of {:smarm_exit, :panic})
is exactly the residual a0ba9be's commit body deferred.
ever_named becomes `watchable`, with a second set-site: mark_watchable(pid),
which the bridge calls wherever a smarm pid is encoded across the boundary —
BEAM can only watch pids it holds, and can only hold pids that crossed.
Anonymous never-exported churn (holder threads, egress tasks) stays
ineligible, preserving e43c673's LIFO-eviction protection unchanged.
mark_watchable takes the cold lock before the liveness screen: finalize
publishes Done and reads the bit under the same lock, so the mark either
lands before the death stamps or observes the tenancy dead and no-ops —
no lost-stamp window, and marking a corpse cannot invent history (pinned
in the test alongside the mark-while-alive stamp).
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
//! caller's deliberate act, not a semantics change.
|
||||
|
||||
use smarm::{
|
||||
init, request_stop, resolve_name, terminal_reason, CallError, Config, DownReason, GenServer,
|
||||
init, mark_watchable, request_stop, resolve_name, terminal_reason, CallError, Config, DownReason, GenServer,
|
||||
GenServerBuilder, GenServerName, NameResolution,
|
||||
};
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -59,6 +59,10 @@ impl GenServer for Filler {
|
||||
struct Observed {
|
||||
exit_reason: Option<DownReason>,
|
||||
anon_reason: Option<DownReason>,
|
||||
/// Anonymous but export-marked while alive — must stamp (sig 5).
|
||||
marked_reason: Option<DownReason>,
|
||||
/// Marked only after death — must remain unknowable.
|
||||
marked_late_reason: Option<DownReason>,
|
||||
panic_reason: Option<DownReason>,
|
||||
stopped_reason: Option<DownReason>,
|
||||
live_reason: Option<DownReason>,
|
||||
@@ -126,6 +130,22 @@ fn terminal_record_recovers_the_reason_a_raced_watch_lost() {
|
||||
let _ = h.join();
|
||||
let anon_reason = terminal_reason(pid_anon);
|
||||
|
||||
// --- mark_watchable: the bridge's export-seam eligibility (sig 5).
|
||||
// An anonymous actor marked while alive stamps like a named one ...
|
||||
let h = smarm::spawn(|| loop {
|
||||
smarm::sleep(Duration::from_millis(2));
|
||||
});
|
||||
let pid_marked = h.pid();
|
||||
mark_watchable(pid_marked);
|
||||
request_stop(pid_marked);
|
||||
let _ = h.join();
|
||||
let marked_reason = terminal_reason(pid_marked);
|
||||
|
||||
// ... while marking a pid whose tenancy already ended is a no-op:
|
||||
// the history is honestly unknowable, not retroactively invented.
|
||||
mark_watchable(pid_anon);
|
||||
let marked_late_reason = terminal_reason(pid_anon);
|
||||
|
||||
// --- The named target: live readings first. -----------------------
|
||||
let target = GenServerBuilder::new(Target)
|
||||
.named(TARGET)
|
||||
@@ -197,6 +217,8 @@ fn terminal_record_recovers_the_reason_a_raced_watch_lost() {
|
||||
unknown_resolution,
|
||||
corpse_resolution_matches,
|
||||
resolution_after_prune,
|
||||
marked_reason,
|
||||
marked_late_reason,
|
||||
corpse_reason_after_prune,
|
||||
corpse_reason_after_reuse,
|
||||
corpse_reason_after_tenant_death,
|
||||
@@ -211,6 +233,15 @@ fn terminal_record_recovers_the_reason_a_raced_watch_lost() {
|
||||
"anonymous deaths must not stamp: {o:?}"
|
||||
);
|
||||
assert_eq!(o.panic_reason, Some(DownReason::Panic), "{o:?}");
|
||||
assert_eq!(
|
||||
o.marked_reason,
|
||||
Some(DownReason::Stopped),
|
||||
"mark_watchable while alive must make the death stamp: {o:?}"
|
||||
);
|
||||
assert_eq!(
|
||||
o.marked_late_reason, None,
|
||||
"marking a dead tenancy must not invent history: {o:?}"
|
||||
);
|
||||
assert_eq!(o.stopped_reason, Some(DownReason::Stopped), "{o:?}");
|
||||
assert_eq!(
|
||||
o.live_reason, None,
|
||||
|
||||
Reference in New Issue
Block a user