fix: window state preservation by soft-killing the process (#3579)
This commit is contained in:
1 parent
e22ea0c2ff
commit
a729968ee4
3 files changed
+57
-10
No files matched your search
@@ -46,7 +46,7 @@ impl RunArgs {
|
|||||||
tracing::info!("[{platform}]: {msg}")
|
tracing::info!("[{platform}]: {msg}")
|
||||||
}
|
}
|
||||||
ServeUpdate::ProcessExited { platform, status } => {
|
ServeUpdate::ProcessExited { platform, status } => {
|
||||||
runner.kill(platform);
|
runner.kill(platform).await;
|
||||||
tracing::info!("[{platform}]: process exited with status: {status:?}");
|
tracing::info!("[{platform}]: process exited with status: {status:?}");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ pub(crate) async fn serve_all(mut args: ServeArgs) -> Result<()> {
|
|||||||
|
|
||||||
// Kill any running executables on Windows
|
// Kill any running executables on Windows
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
runner.kill_all();
|
runner.kill_all().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We're going to kick off a new build, interrupting the current build if it's ongoing
|
// We're going to kick off a new build, interrupting the current build if it's ongoing
|
||||||
@@ -200,7 +200,7 @@ pub(crate) async fn serve_all(mut args: ServeArgs) -> Result<()> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
runner.kill(platform);
|
runner.kill(platform).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServeUpdate::StdoutReceived { platform, msg } => {
|
ServeUpdate::StdoutReceived { platform, msg } => {
|
||||||
@@ -223,7 +223,7 @@ pub(crate) async fn serve_all(mut args: ServeArgs) -> Result<()> {
|
|||||||
|
|
||||||
// Kill any running executables on Windows
|
// Kill any running executables on Windows
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
runner.kill_all();
|
runner.kill_all().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.rebuild(args.build_arguments.clone());
|
builder.rebuild(args.build_arguments.clone());
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::{
|
|||||||
use dioxus_core::internal::TemplateGlobalKey;
|
use dioxus_core::internal::TemplateGlobalKey;
|
||||||
use dioxus_devtools_types::HotReloadMsg;
|
use dioxus_devtools_types::HotReloadMsg;
|
||||||
use dioxus_html::HtmlCtx;
|
use dioxus_html::HtmlCtx;
|
||||||
use futures_util::{future::OptionFuture, stream::FuturesUnordered};
|
use futures_util::{future::OptionFuture, stream::FuturesUnordered, FutureExt};
|
||||||
use ignore::gitignore::Gitignore;
|
use ignore::gitignore::Gitignore;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
@@ -105,7 +105,7 @@ impl AppRunner {
|
|||||||
// Drop the old handle
|
// Drop the old handle
|
||||||
// todo(jon): we should instead be sending the kill signal rather than dropping the process
|
// todo(jon): we should instead be sending the kill signal rather than dropping the process
|
||||||
// This would allow a more graceful shutdown and fix bugs like desktop not retaining its size
|
// This would allow a more graceful shutdown and fix bugs like desktop not retaining its size
|
||||||
self.kill(platform);
|
self.kill(platform).await;
|
||||||
|
|
||||||
// wait a tiny sec for the processes to die so we don't have fullstack servers on top of each other
|
// wait a tiny sec for the processes to die so we don't have fullstack servers on top of each other
|
||||||
// todo(jon): we should allow rebinding to the same port in fullstack itself
|
// todo(jon): we should allow rebinding to the same port in fullstack itself
|
||||||
@@ -137,12 +137,59 @@ impl AppRunner {
|
|||||||
Ok(self.running.get(&platform).unwrap())
|
Ok(self.running.get(&platform).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn kill(&mut self, platform: Platform) {
|
/// Gracefully kill the process and all of its children
|
||||||
self.running.remove(&platform);
|
///
|
||||||
|
/// Uses the `SIGTERM` signal on unix and `taskkill` on windows.
|
||||||
|
/// This complex logic is necessary for things like window state preservation to work properly.
|
||||||
|
pub(crate) async fn kill(&mut self, platform: Platform) {
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
let Some(mut process) = self.running.remove(&platform) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let server_process = process.server_child.take();
|
||||||
|
let client_process = process.app_child.take();
|
||||||
|
let processes = [server_process, client_process]
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for mut process in processes {
|
||||||
|
let Some(pid) = process.id() else {
|
||||||
|
_ = process.kill().await;
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
// on unix, we can send a signal to the process to shut down
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
_ = Command::new("kill")
|
||||||
|
.args(["-s", "TERM", &pid.to_string()])
|
||||||
|
.spawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// on windows, use the `taskkill` command
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
_ = Command::new("taskkill")
|
||||||
|
.args(["/F", "/PID", &pid.to_string()])
|
||||||
|
.spawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// join the wait with a 100ms timeout
|
||||||
|
futures_util::select! {
|
||||||
|
_ = process.wait().fuse() => {}
|
||||||
|
_ = tokio::time::sleep(std::time::Duration::from_millis(1000)).fuse() => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn kill_all(&mut self) {
|
pub(crate) async fn kill_all(&mut self) {
|
||||||
self.running.clear();
|
let keys = self.running.keys().cloned().collect::<Vec<_>>();
|
||||||
|
for platform in keys {
|
||||||
|
self.kill(platform).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open an existing app bundle, if it exists
|
/// Open an existing app bundle, if it exists
|
||||||
|
|||||||
Reference in new issue
Block a user