If an actor panics, make sure to clean up in drop if we can

This commit is contained in:
Tony Garnock-Jones 2021-12-01 11:06:29 +01:00
parent 77a3ee4a31
commit 11363c5776
1 changed files with 28 additions and 16 deletions

View File

@ -596,22 +596,7 @@ impl FacetRef {
}
drop(activation);
let exit_status = Arc::new(exit_status);
let mut t = Activation::make(&self.actor.facet_ref(state.root),
Account::new(crate::name!("shutdown")),
state);
if let Err(err) = t._terminate_facet(t.state.root, exit_status.is_ok()) {
// This can only occur as the result of an internal error in this file's code.
tracing::error!(?err, "unexpected error from terminate_facet");
panic!("Unexpected error result from terminate_facet");
}
// TODO: The linked_tasks are being cancelled above ^ when their Facets drop.
// TODO: We don't want that: we want (? do we?) exit hooks to run before linked_tasks are cancelled.
// TODO: Example: send an error message in an exit_hook that is processed and delivered by a linked_task.
for action in std::mem::take(&mut t.state.exit_hooks) {
if let Err(err) = action(&mut t, &exit_status) {
tracing::error!(?err, "error in exit hook");
}
}
state.cleanup(&self.actor, &exit_status);
*g = ActorState::Terminated {
exit_status: Arc::clone(&exit_status),
};
@ -1727,6 +1712,25 @@ impl RunningActor {
e.retract(t, handle)
}))));
}
fn cleanup(&mut self, ac_ref: &ActorRef, exit_status: &Arc<ActorResult>) {
let mut t = Activation::make(&ac_ref.facet_ref(self.root),
Account::new(crate::name!("cleanup")),
self);
if let Err(err) = t._terminate_facet(t.state.root, exit_status.is_ok()) {
// This can only occur as the result of an internal error in this file's code.
tracing::error!(?err, "unexpected error from terminate_facet");
panic!("Unexpected error result from terminate_facet");
}
// TODO: The linked_tasks are being cancelled above ^ when their Facets drop.
// TODO: We don't want that: we want (? do we?) exit hooks to run before linked_tasks are cancelled.
// TODO: Example: send an error message in an exit_hook that is processed and delivered by a linked_task.
for action in std::mem::take(&mut t.state.exit_hooks) {
if let Err(err) = action(&mut t, &exit_status) {
tracing::error!(?err, "error in exit hook");
}
}
}
}
impl<T: Any + Send> std::fmt::Debug for Field<T> {
@ -1753,6 +1757,14 @@ impl Drop for Actor {
fn drop(&mut self) {
self.rx.close();
ACTORS.write().remove(&self.ac_ref.actor_id);
let mut g = self.ac_ref.state.lock();
if let ActorState::Running(ref mut state) = *g {
tracing::warn!(actor_id = ?self.ac_ref.actor_id, "Force-terminated by Actor::drop");
let exit_status =
Arc::new(Err(error("Force-terminated by Actor::drop", AnyValue::new(false))));
state.cleanup(&self.ac_ref, &exit_status);
*g = ActorState::Terminated { exit_status };
}
tracing::trace!(actor_id = ?self.ac_ref.actor_id, "Actor::drop");
}
}