Make managed daemon reaping exception-safe
Transfer every successfully spawned daemon child to the named reaper before any connection or handshake step can fail. Read early exit status from shared reaper facts, eliminating fallible child-handle paths that could return without reaping.
This commit is contained in:
parent
c0ec8f75c2
commit
69825d0761
|
|
@ -116,8 +116,6 @@ pub enum ManagedAttachError {
|
||||||
/// Process-spawn failure.
|
/// Process-spawn failure.
|
||||||
source: io::Error,
|
source: io::Error,
|
||||||
},
|
},
|
||||||
/// The daemon process could not be queried for an early exit.
|
|
||||||
ObserveDaemon(io::Error),
|
|
||||||
/// No attachable daemon appeared before the bounded deadline.
|
/// No attachable daemon appeared before the bounded deadline.
|
||||||
StartupTimeout {
|
StartupTimeout {
|
||||||
/// Socket path that remained unreachable.
|
/// Socket path that remained unreachable.
|
||||||
|
|
@ -150,9 +148,6 @@ impl std::fmt::Display for ManagedAttachError {
|
||||||
"could not start daemon executable {}: {source}",
|
"could not start daemon executable {}: {source}",
|
||||||
executable.display()
|
executable.display()
|
||||||
),
|
),
|
||||||
Self::ObserveDaemon(source) => {
|
|
||||||
write!(f, "could not inspect the managed daemon process: {source}")
|
|
||||||
}
|
|
||||||
Self::StartupTimeout {
|
Self::StartupTimeout {
|
||||||
socket,
|
socket,
|
||||||
connect,
|
connect,
|
||||||
|
|
@ -179,7 +174,6 @@ impl std::error::Error for ManagedAttachError {
|
||||||
Self::Attach(error) => Some(error),
|
Self::Attach(error) => Some(error),
|
||||||
Self::InspectSocket { source, .. }
|
Self::InspectSocket { source, .. }
|
||||||
| Self::SpawnDaemon { source, .. }
|
| Self::SpawnDaemon { source, .. }
|
||||||
| Self::ObserveDaemon(source)
|
|
||||||
| Self::StartupTimeout {
|
| Self::StartupTimeout {
|
||||||
connect: source, ..
|
connect: source, ..
|
||||||
} => Some(source),
|
} => Some(source),
|
||||||
|
|
@ -655,12 +649,6 @@ fn start_daemon_reaper(mut child: Child, facts: ManagedDaemonFacts) {
|
||||||
.expect("spawn managed daemon reaper thread");
|
.expect("spawn managed daemon reaper thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hand_off_daemon_child(child: &mut Option<Child>, facts: &ManagedDaemonFacts) {
|
|
||||||
if let Some(child) = child.take() {
|
|
||||||
start_daemon_reaper(child, facts.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn connect_managed_inner<C, S, F>(
|
fn connect_managed_inner<C, S, F>(
|
||||||
socket_path: &Path,
|
socket_path: &Path,
|
||||||
|
|
@ -698,48 +686,22 @@ where
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
let daemon = ManagedDaemonFacts::spawned(child.id());
|
let daemon = ManagedDaemonFacts::spawned(child.id());
|
||||||
let mut child = Some(child);
|
start_daemon_reaper(child, daemon.clone());
|
||||||
let deadline = Instant::now() + timeout;
|
let deadline = Instant::now() + timeout;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match connector(socket_path) {
|
match connector(socket_path) {
|
||||||
Ok(stream) => {
|
Ok(stream) => {
|
||||||
let attached = connect_stream_with_sink(stream, sink);
|
let client = connect_stream_with_sink(stream, sink)?;
|
||||||
hand_off_daemon_child(&mut child, &daemon);
|
return Ok(ManagedAttach { client, daemon });
|
||||||
return Ok(ManagedAttach {
|
|
||||||
client: attached?,
|
|
||||||
daemon,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let retryable = match post_spawn_retryable(socket_path, &error) {
|
let retryable = post_spawn_retryable(socket_path, &error)?;
|
||||||
Ok(retryable) => retryable,
|
|
||||||
Err(classification_error) => {
|
|
||||||
hand_off_daemon_child(&mut child, &daemon);
|
|
||||||
return Err(classification_error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if !retryable {
|
if !retryable {
|
||||||
hand_off_daemon_child(&mut child, &daemon);
|
|
||||||
return Err(AttachClientError::Connect(error).into());
|
return Err(AttachClientError::Connect(error).into());
|
||||||
}
|
}
|
||||||
let wait_status = match child
|
|
||||||
.as_mut()
|
|
||||||
.expect("managed daemon child handed off only on return")
|
|
||||||
.try_wait()
|
|
||||||
{
|
|
||||||
Ok(status) => status,
|
|
||||||
Err(observe_error) => {
|
|
||||||
hand_off_daemon_child(&mut child, &daemon);
|
|
||||||
return Err(ManagedAttachError::ObserveDaemon(observe_error));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Some(status) = wait_status {
|
|
||||||
daemon.record_wait(status.to_string());
|
|
||||||
}
|
|
||||||
if Instant::now() >= deadline {
|
if Instant::now() >= deadline {
|
||||||
let daemon_status = daemon.daemon_wait_result();
|
let daemon_status = daemon.daemon_wait_result();
|
||||||
hand_off_daemon_child(&mut child, &daemon);
|
|
||||||
return Err(ManagedAttachError::StartupTimeout {
|
return Err(ManagedAttachError::StartupTimeout {
|
||||||
socket: socket_path.to_owned(),
|
socket: socket_path.to_owned(),
|
||||||
connect: error,
|
connect: error,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue