use crate::distribution::control_link::LinkControlDelivery;
use crate::process::{ExitReason, ProcessStatus, RemotePid};
use crate::scheduler::process_slot::PendingExitSource;
use crate::supervision::link;
use super::execution::{cleanup_exited_process, wake_process};
use super::supervision_integration::{
SchedulerDistributionSendFacility, establish_remote_link, remove_remote_link,
shared_exit_tombstone,
};
use super::{ProcessSlot, ScheduledProcess, SharedState, dist_control_out, lock_or_recover};
use crate::atom::Atom;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum RemoteExitKind {
LinkExit,
Direct,
}
pub(crate) fn process_remote_exit_signal(
shared: &SharedState,
source_pid: RemotePid,
target_pid: u64,
reason: ExitReason,
kind: RemoteExitKind,
) {
let Some(entry) = shared.process_bodies.get(&target_pid) else {
return;
};
let mut slot = lock_or_recover(&entry);
match &mut *slot {
ProcessSlot::Present(ScheduledProcess(target)) => {
if matches!(target.status(), ProcessStatus::Exited(_)) {
return;
}
if kind == RemoteExitKind::LinkExit && !target.remove_remote_link(source_pid) {
return;
}
let should_die =
reason == ExitReason::Kill || (reason != ExitReason::Normal && !target.trap_exit());
if should_die {
let propagated_reason = link::terminal_reason(reason);
target.terminate(propagated_reason);
drop(slot);
drop(entry);
cleanup_exited_process(shared, target_pid, propagated_reason);
} else if target.trap_exit() {
link::enqueue_remote_exit_message_pub(target, source_pid, reason);
drop(slot);
drop(entry);
wake_process(shared, target_pid);
}
}
ProcessSlot::Executing(metadata) => {
if kind == RemoteExitKind::LinkExit && !metadata.remove_remote_link(source_pid) {
return;
}
let should_die =
reason == ExitReason::Kill || (reason != ExitReason::Normal && !metadata.trap_exit);
if should_die {
if reason == ExitReason::Kill {
metadata.trap_exit = false;
}
shared_exit_tombstone(shared, target_pid, link::terminal_reason(reason));
} else if metadata.trap_exit {
metadata
.pending_exit_messages
.push((PendingExitSource::Remote(source_pid), reason));
drop(slot);
drop(entry);
wake_process(shared, target_pid);
}
}
ProcessSlot::Absent => {}
}
}
pub(crate) fn connection_down(shared: &SharedState, node: Atom) {
let affected: Vec<(u64, RemotePid)> = shared
.process_bodies
.iter()
.flat_map(|entry| {
let pid = *entry.key();
let slot = lock_or_recover(entry.value());
match &*slot {
ProcessSlot::Present(ScheduledProcess(process)) => process
.remote_links()
.iter()
.copied()
.filter(|remote| remote.node == node)
.map(|remote| (pid, remote))
.collect::<Vec<_>>(),
ProcessSlot::Executing(metadata) => metadata
.remote_links
.iter()
.copied()
.filter(|remote| remote.node == node)
.map(|remote| (pid, remote))
.collect::<Vec<_>>(),
ProcessSlot::Absent => Vec::new(),
}
})
.collect();
for (local_pid, remote_pid) in affected {
process_remote_exit_signal(
shared,
remote_pid,
local_pid,
ExitReason::NoConnection,
RemoteExitKind::LinkExit,
);
}
}
pub(super) fn apply_inbound_link(shared: &SharedState, from: RemotePid, to_pid: u64) {
if !establish_remote_link(shared, to_pid, from) {
let reason = shared
.exit_tombstones
.get(&to_pid)
.map(link::terminal_reason)
.unwrap_or(ExitReason::NoProc);
dist_control_out::send_exit_linked(shared, to_pid, from, reason);
return;
}
let connection_live = shared
.distribution()
.and_then(|dist| dist.connections().get_connection(from.node))
.is_some_and(|connection| !connection.is_down());
if !connection_live {
process_remote_exit_signal(
shared,
from,
to_pid,
ExitReason::NoConnection,
RemoteExitKind::LinkExit,
);
}
}
impl LinkControlDelivery for SchedulerDistributionSendFacility {
fn apply_link(&self, from: RemotePid, to_pid: u64) {
apply_inbound_link(&self.shared, from, to_pid);
}
fn apply_unlink(&self, from: RemotePid, to_pid: u64) {
let _ = remove_remote_link(&self.shared, to_pid, from);
}
fn apply_link_exit(&self, from: RemotePid, to_pid: u64, reason: ExitReason) {
super::supervision_integration::process_remote_exit_signal(
&self.shared,
from,
to_pid,
reason,
RemoteExitKind::LinkExit,
);
}
fn apply_exit2(&self, from: RemotePid, to_pid: u64, reason: ExitReason) {
super::supervision_integration::process_remote_exit_signal(
&self.shared,
from,
to_pid,
reason,
RemoteExitKind::Direct,
);
}
}