Desktop: ask before quitting on top of a running install (#7550)
* Desktop: ask before quitting on top of a running install This is the trigger neither #7492 nor #7490 addresses -- both start from a venv that is already broken. Confirmed: neither PR touches cleanup_child_processes. Quitting runs cleanup_child_processes -> install::stop_install, which SIGTERMs the installer's process group. In the reported session that landed at "5/10 studio deps", so the venv kept the CLI's dependencies and lost the server stack, and the next launch died on `import structlog`. Three minutes of installing, destroyed with no warning and no way back. So ask. Only from the tray Quit item -- a deliberate action with a UI present. The RunEvent::Exit path (OS shutdown, SIGTERM) is left alone: it must never block on a dialog nobody can answer. The call already runs off the menu callback thread, which is also what blocking_show requires. Closing the window was already safe (it hides to tray); this closes the remaining way to lose an install by accident. * Tighten comments in desktop quit-during-install guard * Condense comments in quit-during-install guard --------- Co-authored-by: danielhanchen <unslothai@gmail.com>
This commit is contained in:
parent
df63522369
commit
4f0cbf0d81
2 changed files with 38 additions and 0 deletions
|
|
@ -783,6 +783,14 @@ pub fn record_install_intentional_stop(state: &InstallState, diagnostics: &Diagn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// True while an installer runs; quitting now would leave a broken venv.
|
||||||
|
pub fn is_install_running(state: &InstallState) -> bool {
|
||||||
|
state
|
||||||
|
.lock()
|
||||||
|
.map(|install| install.child.is_some())
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
/// Stop a running install process gracefully.
|
/// Stop a running install process gracefully.
|
||||||
/// Unix: SIGTERM to process group -> wait up to 5s -> SIGKILL
|
/// Unix: SIGTERM to process group -> wait up to 5s -> SIGKILL
|
||||||
/// Windows: hidden taskkill /T /F to terminate the installer tree
|
/// Windows: hidden taskkill /T /F to terminate the installer tree
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,33 @@ fn setup_custom_titlebar(app: &tauri::App) -> Result<(), Box<dyn std::error::Err
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ask before quitting mid-install (true to proceed): `cleanup_child_processes` SIGTERMs the
|
||||||
|
/// installer, leaving a venv that looks healthy but cannot start. Tray Quit only, since
|
||||||
|
/// RunEvent::Exit must never block on a dialog nobody can answer.
|
||||||
|
fn confirm_quit_during_install(app: &tauri::AppHandle) -> bool {
|
||||||
|
use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogKind};
|
||||||
|
|
||||||
|
let Some(install_state) = app.try_state::<install::InstallState>() else {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if !install::is_install_running(&install_state) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
app.dialog()
|
||||||
|
.message(
|
||||||
|
"Unsloth Studio is still installing. Quitting now stops it part-way and \
|
||||||
|
leaves the installation incomplete, so it will need to be repaired before \
|
||||||
|
it can start.",
|
||||||
|
)
|
||||||
|
.kind(MessageDialogKind::Warning)
|
||||||
|
.title("Installation in progress")
|
||||||
|
.buttons(MessageDialogButtons::OkCancelCustom(
|
||||||
|
"Quit anyway".to_string(),
|
||||||
|
"Keep installing".to_string(),
|
||||||
|
))
|
||||||
|
.blocking_show()
|
||||||
|
}
|
||||||
|
|
||||||
fn cleanup_child_processes(app: &tauri::AppHandle) {
|
fn cleanup_child_processes(app: &tauri::AppHandle) {
|
||||||
let diagnostics_state = app
|
let diagnostics_state = app
|
||||||
.try_state::<diagnostics::DiagnosticsState>()
|
.try_state::<diagnostics::DiagnosticsState>()
|
||||||
|
|
@ -138,6 +165,9 @@ fn setup_tray(app: &tauri::App) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// leaving the backend orphaned.
|
// leaving the backend orphaned.
|
||||||
let app_handle = app.clone();
|
let app_handle = app.clone();
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
|
if !confirm_quit_during_install(&app_handle) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
cleanup_child_processes(&app_handle);
|
cleanup_child_processes(&app_handle);
|
||||||
app_handle.exit(0);
|
app_handle.exit(0);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue