use std::sync::{Weak, mpsc};
use beamr::native::native_process::{NativeContext, NativeHandler, NativeOutcome};
use beamr::process::ExitReason;
use super::beam::exit_source;
use super::{ActorCore, SupervisorInner};
use crate::conversation::types::ParticipantPid;
pub(super) struct ActorExitWatcher {
core: Weak<ActorCore>,
supervisor: Weak<SupervisorInner>,
actor: ParticipantPid,
armed: Option<mpsc::SyncSender<()>>,
}
impl ActorExitWatcher {
pub(super) const fn new(
core: Weak<ActorCore>,
supervisor: Weak<SupervisorInner>,
actor: ParticipantPid,
armed: mpsc::SyncSender<()>,
) -> Self {
Self {
core,
supervisor,
actor,
armed: Some(armed),
}
}
fn cleanup(&self) {
if let Some(core) = self.core.upgrade() {
core.finalize_after_actor_exit(self.actor);
return;
}
if let Some(supervisor) = self.supervisor.upgrade() {
supervisor.runtime.deregister_dead(self.actor);
supervisor
.participant_runtime
.reap_orphans(&supervisor.scheduler);
}
}
fn actor_process_gone(&self) -> bool {
self.supervisor.upgrade().is_none_or(|supervisor| {
supervisor
.scheduler
.process_table()
.get(self.actor.get())
.is_none()
})
}
}
impl std::fmt::Debug for ActorExitWatcher {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("ActorExitWatcher")
.field("actor", &self.actor)
.finish_non_exhaustive()
}
}
impl NativeHandler for ActorExitWatcher {
fn handle(&mut self, ctx: &mut NativeContext<'_>) -> NativeOutcome {
if let Some(armed) = self.armed.take() {
ctx.set_trap_exit(true);
let _ = armed.try_send(());
}
let mut actor_exited = false;
while let Some(message) = ctx.recv() {
if let Some((source, _reason)) = exit_source(message) {
if source == self.actor {
actor_exited = true;
}
}
}
if actor_exited || self.actor_process_gone() {
self.cleanup();
return NativeOutcome::Stop(ExitReason::Normal);
}
NativeOutcome::Wait
}
}