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:
+23
-19
@@ -472,24 +472,28 @@ pub(crate) struct SlotCold {
|
||||
/// epoch-matched unpark.
|
||||
pub(crate) waiters: Vec<(Pid, u32)>,
|
||||
pub(crate) outcome: Option<Outcome>,
|
||||
/// The slot's most recent *named-tenancy* death: `(generation, reason)`,
|
||||
/// stamped by `finalize_actor` — but only for a tenancy that ever
|
||||
/// registered a name (`ever_named`) — and deliberately never cleared: a
|
||||
/// new tenant's install leaves it standing (it describes the previous
|
||||
/// tenancy), and only the next *named* death overwrites it. Anonymous
|
||||
/// green-thread churn must not evict it: the free list is LIFO, so the
|
||||
/// just-freed slot is the first recycled, and an unconditional stamp
|
||||
/// made a watchable tenancy's record the shortest-lived data in the
|
||||
/// runtime. Read generation-matched via
|
||||
/// The slot's most recent *watchable-tenancy* death: `(generation,
|
||||
/// reason)`, stamped by `finalize_actor` — but only for a tenancy whose
|
||||
/// `watchable` bit was set — and deliberately never cleared: a new
|
||||
/// tenant's install leaves it standing (it describes the previous
|
||||
/// tenancy), and only the next *watchable* death overwrites it.
|
||||
/// Anonymous green-thread churn must not evict it: the free list is
|
||||
/// LIFO, so the just-freed slot is the first recycled, and an
|
||||
/// unconditional stamp made a watchable tenancy's record the
|
||||
/// shortest-lived data in the runtime. Read generation-matched via
|
||||
/// [`terminal_reason`](crate::monitor::terminal_reason), so a watch that
|
||||
/// raced its target's death can recover the real down reason instead of
|
||||
/// a blanket `NoProc` (bridge soak signature 4).
|
||||
/// a blanket `NoProc` (bridge soak signatures 4 and 5).
|
||||
pub(crate) terminal: Option<(u32, DownReason)>,
|
||||
/// This tenancy registered a name at least once — the stamp-eligibility
|
||||
/// bit for `terminal` above. Set by `register_with` *before* the binding
|
||||
/// lands (so no successfully-registered actor can die unflagged; a
|
||||
/// failed register's overshoot is harmless), reset at reclaim.
|
||||
pub(crate) ever_named: bool,
|
||||
/// Stamp eligibility for `terminal` above: someone could plausibly hold
|
||||
/// a watch on this tenancy. Two set-sites, both while the tenancy is
|
||||
/// live: `register_with` *before* the binding lands (no successfully
|
||||
/// registered actor can die unflagged; a failed register's overshoot is
|
||||
/// harmless), and [`mark_watchable`](crate::monitor::mark_watchable) —
|
||||
/// the bridge calls it wherever a pid is encoded across the boundary,
|
||||
/// because BEAM can only watch pids it holds and can only hold pids
|
||||
/// that crossed. Reset at reclaim.
|
||||
pub(crate) watchable: bool,
|
||||
pub(crate) supervisor_channel: Option<Sender<Signal>>,
|
||||
/// Watchers registered via `monitor()`, each tagged with its
|
||||
/// `MonitorId` so `demonitor` can remove exactly one. Each receives one
|
||||
@@ -645,7 +649,7 @@ impl Slot {
|
||||
waiters: Vec::new(),
|
||||
outcome: None,
|
||||
terminal: None,
|
||||
ever_named: false,
|
||||
watchable: false,
|
||||
supervisor_channel: None,
|
||||
monitors: Vec::new(),
|
||||
links: Vec::new(),
|
||||
@@ -1648,7 +1652,7 @@ pub(crate) fn reclaim_slot(inner: &RuntimeInner, pid: Pid) {
|
||||
cold.waiters.clear();
|
||||
cold.monitors.clear();
|
||||
cold.links.clear();
|
||||
cold.ever_named = false;
|
||||
cold.watchable = false;
|
||||
slot.reset_counters();
|
||||
slot.stop_ptr.store(std::ptr::null_mut(), Ordering::Release);
|
||||
// The generation bump IS the reclaim: every stale pid is dead from
|
||||
@@ -1696,8 +1700,8 @@ fn finalize_actor(inner: &Arc<RuntimeInner>, pid: Pid, outcome: Outcome) {
|
||||
// thread's exit stamped too, the churn behind any real workload
|
||||
// would evict a watchable tenancy's record in well under the race
|
||||
// window this exists to cover. Overwritten only by the slot's next
|
||||
// *named* death.
|
||||
if cold.ever_named {
|
||||
// *watchable* death.
|
||||
if cold.watchable {
|
||||
cold.terminal = Some((pid.generation(), down_reason));
|
||||
}
|
||||
slot.stop_ptr.store(std::ptr::null_mut(), Ordering::Release);
|
||||
|
||||
Reference in New Issue
Block a user