fix(process_manager): await child exit and reap process handles (v0.1.16) #77

Merged
gronod merged 1 commits from fix/issue-63-process-manager-reap into development 2026-08-25 11:08:25 +01:00
4 changed files with 36 additions and 23 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "iccery", "name": "iccery",
"private": true, "private": true,
"version": "0.1.15", "version": "0.1.16",
"type": "module", "type": "module",
"scripts": { "scripts": {
"tauri": "tauri" "tauri": "tauri"
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "iccery" name = "iccery"
version = "0.1.15" version = "0.1.16"
description = "Modern Printer Profiling UI frontend for ArgyllCMS" description = "Modern Printer Profiling UI frontend for ArgyllCMS"
authors = ["Gordon"] authors = ["Gordon"]
edition = "2021" edition = "2021"
+29 -16
View File
@@ -1,29 +1,32 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::process::Stdio; use std::process::Stdio;
use std::sync::Arc;
use tauri::AppHandle;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader}; use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::process::{Child, Command}; use tokio::process::{Child, Command};
use tokio::sync::Mutex; use tokio::sync::Mutex;
use tauri::AppHandle;
#[cfg(windows)]
use std::os::windows::process::CommandExt;
use crate::events::{emit_error, emit_stderr, emit_stdout}; use crate::events::{emit_error, emit_stderr, emit_stdout};
use std::sync::Arc;
pub struct ProcessManager { pub struct ProcessManager {
processes: Mutex<HashMap<String, Arc<Mutex<Child>>>>, processes: Arc<Mutex<HashMap<String, Arc<Mutex<Child>>>>>,
} }
impl ProcessManager { impl ProcessManager {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
processes: Mutex::new(HashMap::new()), processes: Arc::new(Mutex::new(HashMap::new())),
} }
} }
pub async fn spawn(&self, app: AppHandle, id: String, binary: String, args: Vec<String>, cwd: Option<String>) -> Result<(), String> { pub async fn spawn(
&self,
app: AppHandle,
id: String,
binary: String,
args: Vec<String>,
cwd: Option<String>,
) -> Result<(), String> {
let mut command = Command::new(&binary); let mut command = Command::new(&binary);
command.args(args); command.args(args);
if let Some(dir) = cwd { if let Some(dir) = cwd {
@@ -69,20 +72,30 @@ impl ProcessManager {
}); });
let child_arc = Arc::new(Mutex::new(child)); let child_arc = Arc::new(Mutex::new(child));
let mut processes = self.processes.lock().await; {
processes.insert(id.clone(), child_arc.clone()); let mut processes = self.processes.lock().await;
processes.insert(id.clone(), child_arc.clone());
}
let id_clone_exit = id.clone(); let id_clone_exit = id.clone();
let app_clone_exit = app.clone(); let app_clone_exit = app.clone();
let processes_clone = self.processes.clone();
tokio::spawn(async move { tokio::spawn(async move {
loop { let exit_code = {
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
let mut c = child_arc.lock().await; let mut c = child_arc.lock().await;
if let Ok(Some(status)) = c.try_wait() { match c.wait().await {
crate::events::emit_exit(&app_clone_exit, &id_clone_exit, status.code().unwrap_or(1)); Ok(status) => status.code().unwrap_or(0),
break; Err(_) => 1,
} }
};
// Reap child from process manager map upon exit
{
let mut processes = processes_clone.lock().await;
processes.remove(&id_clone_exit);
} }
crate::events::emit_exit(&app_clone_exit, &id_clone_exit, exit_code);
}); });
Ok(()) Ok(())
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "ICCery", "productName": "ICCery",
"version": "0.1.15", "version": "0.1.16",
"identifier": "com.gronod.iccery", "identifier": "com.gronod.iccery",
"build": { "build": {
"frontendDist": "../src" "frontendDist": "../src"