Detect and surface plugin runtime crashes (#502)

This commit is contained in:
Gregory Schier
2026-07-09 09:05:34 -07:00
committed by GitHub
parent 2fee5ba413
commit 155260521e
3 changed files with 87 additions and 20 deletions
+25 -10
View File
@@ -1,5 +1,5 @@
use crate::error::Result;
use log::{info, warn};
use log::{error, info, warn};
use std::net::SocketAddr;
use std::path::Path;
use std::process::Stdio;
@@ -15,12 +15,16 @@ use yaak_common::command::new_xplatform_command;
/// * `plugin_runtime_main` - Path to the plugin runtime index.cjs
/// * `addr` - Socket address for the plugin runtime to connect to
/// * `kill_rx` - Channel to signal shutdown
/// * `killed_tx` - Notified once the runtime is killed after a shutdown signal
/// * `unexpected_exit_tx` - Set to the exit status if the runtime exits
/// without being asked to
pub async fn start_nodejs_plugin_runtime(
node_bin_path: &Path,
plugin_runtime_main: &Path,
addr: SocketAddr,
kill_rx: &Receiver<bool>,
killed_tx: oneshot::Sender<()>,
unexpected_exit_tx: tokio::sync::watch::Sender<Option<String>>,
) -> Result<()> {
// HACK: Remove UNC prefix for Windows paths to pass to sidecar
let plugin_runtime_main_str =
@@ -65,18 +69,29 @@ pub async fn start_nodejs_plugin_runtime(
});
}
// Handle kill signal
// Wait for either an explicit kill signal or the child exiting on its own.
// An unexpected exit is reported to the caller, which decides whether to
// abort startup or surface a user-facing error.
let mut kill_rx = kill_rx.clone();
tokio::spawn(async move {
if kill_rx.wait_for(|b| *b == true).await.is_err() {
warn!("Kill channel closed before explicit shutdown; terminating plugin runtime");
tokio::select! {
status = child.wait() => {
let status = status.map(|s| s.to_string()).unwrap_or_else(|e| e.to_string());
error!("Plugin runtime exited unexpectedly ({status})");
let _ = unexpected_exit_tx.send(Some(status));
}
closed = async { kill_rx.wait_for(|b| *b == true).await.is_err() } => {
if closed {
warn!("Kill channel closed before explicit shutdown; terminating plugin runtime");
}
info!("Killing plugin runtime");
if let Err(e) = child.kill().await {
warn!("Failed to kill plugin runtime: {e}");
}
info!("Killed plugin runtime");
let _ = killed_tx.send(());
}
}
info!("Killing plugin runtime");
if let Err(e) = child.kill().await {
warn!("Failed to kill plugin runtime: {e}");
}
info!("Killed plugin runtime");
let _ = killed_tx.send(());
});
Ok(())