scheduler: spawn_monitor / spawn_monitor_with — monitor registered on the child's slot before publish, so the Down always carries the real reason (spawn-then-monitor could race to NoProc); tests; channel test uses it
This commit is contained in:
+47
-2
@@ -362,7 +362,7 @@ pub fn spawn_under_with<A>(
|
||||
|
||||
let pid = with_runtime(|inner| {
|
||||
let idx = inner.allocate_slot(); // panics loudly on slab exhaustion
|
||||
crate::runtime::install_actor(inner, idx, sp, stack, supervisor, closure)
|
||||
crate::runtime::install_actor(inner, idx, sp, stack, supervisor, closure, None)
|
||||
});
|
||||
|
||||
JoinHandle {
|
||||
@@ -371,6 +371,51 @@ pub fn spawn_under_with<A>(
|
||||
}
|
||||
}
|
||||
|
||||
/// [`spawn`] and [`monitor`](crate::monitor) the child in one step, with no
|
||||
/// window in which the child can die unobserved.
|
||||
///
|
||||
/// `spawn` followed by `monitor(h.pid())` races: on a multi-scheduler
|
||||
/// runtime the child can run to completion before the monitor registers, and
|
||||
/// a monitor on a dead pid delivers [`DownReason::NoProc`](crate::DownReason::NoProc)
|
||||
/// — the real reason (Exit vs Panic) is lost. Here the monitor is registered
|
||||
/// on the child's slot *before* the child is published to any run queue, so
|
||||
/// the `Down` always carries the child's actual termination reason. This is
|
||||
/// Erlang's `spawn_monitor/1`.
|
||||
pub fn spawn_monitor(f: impl FnOnce() + Send + 'static) -> (JoinHandle, crate::monitor::Monitor) {
|
||||
spawn_monitor_with(SpawnOpts::default(), f)
|
||||
}
|
||||
|
||||
/// [`spawn_monitor`] with per-actor stack shape overrides (RFC 019).
|
||||
pub fn spawn_monitor_with(
|
||||
opts: SpawnOpts,
|
||||
f: impl FnOnce() + Send + 'static,
|
||||
) -> (JoinHandle, crate::monitor::Monitor) {
|
||||
let parent = current_pid().unwrap_or_else(|| with_runtime(|_| crate::runtime::ROOT_PID));
|
||||
let (tx, rx) = crate::channel::channel::<crate::monitor::Down>();
|
||||
let stack = with_runtime(|inner| crate::runtime::acquire_stack(inner, opts));
|
||||
let sp = init_actor_stack(stack.top(), crate::actor::trampoline);
|
||||
let closure: crate::runtime::Closure = Box::new(f);
|
||||
|
||||
let (pid, id) = with_runtime(|inner| {
|
||||
let idx = inner.allocate_slot(); // panics loudly on slab exhaustion
|
||||
let id = inner.alloc_monitor_id();
|
||||
let pid = crate::runtime::install_actor(inner, idx, sp, stack, parent, closure, Some((id, tx)));
|
||||
(pid, id)
|
||||
});
|
||||
|
||||
(
|
||||
JoinHandle {
|
||||
pid,
|
||||
consumed: false,
|
||||
},
|
||||
crate::monitor::Monitor {
|
||||
id,
|
||||
target: pid,
|
||||
rx,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// [`spawn`] that reports a full actor slab instead of panicking.
|
||||
///
|
||||
/// Behaviour parity with [`spawn`] in every case except one: when the fixed
|
||||
@@ -429,7 +474,7 @@ pub fn try_spawn_under_with<A>(
|
||||
|
||||
claimed.0 = None; // install_actor takes ownership of the slot from here
|
||||
let pid = with_runtime(|inner| {
|
||||
crate::runtime::install_actor(inner, idx, sp, stack, supervisor, closure)
|
||||
crate::runtime::install_actor(inner, idx, sp, stack, supervisor, closure, None)
|
||||
});
|
||||
|
||||
Ok(JoinHandle {
|
||||
|
||||
Reference in New Issue
Block a user