Skip to content

Commit acb660d

Browse files
committed
fix child kill
Signed-off-by: Andrei Gherghescu <[email protected]>
1 parent f83c639 commit acb660d

File tree

1 file changed

+70
-52
lines changed

1 file changed

+70
-52
lines changed

plotly_static/src/webdriver.rs

Lines changed: 70 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
use std::io::prelude::*;
1111
use std::io::BufReader;
1212
use std::path::PathBuf;
13-
use std::process::{Command, Stdio};
13+
use std::process::{Child, Command, Stdio};
1414
use std::sync::{Arc, Mutex};
1515
use std::thread;
1616
#[cfg(test)]
@@ -39,7 +39,7 @@ pub(crate) const WEBDRIVER_URL: &str = "http://localhost";
3939
struct WdInner {
4040
webdriver_port: u32,
4141
driver_path: PathBuf,
42-
webdriver_process_id: Option<u32>,
42+
webdriver_child: Option<Child>,
4343
is_external: bool, /* Marker for whether this WebDriver was spawned by us or connected to
4444
* existing */
4545
}
@@ -97,7 +97,7 @@ impl WebDriver {
9797
inner: Arc::new(Mutex::new(WdInner {
9898
webdriver_port: port,
9999
driver_path: full_path,
100-
webdriver_process_id: None,
100+
webdriver_child: None,
101101
is_external: false, // mark it as spawned by us
102102
})),
103103
})
@@ -113,51 +113,40 @@ impl WebDriver {
113113
/// using the `stop()` method.
114114
pub(crate) fn spawn_webdriver(&mut self) {
115115
info!("Spawning {WEBDRIVER_BIN}");
116-
let local_self = self.inner.clone();
116+
117+
// Spawn the process in the main thread to get the Child handle
118+
let mut command = Command::new(self.inner.lock().unwrap().driver_path.clone());
119+
command
120+
.arg(format!("--port={}", self.inner.lock().unwrap().webdriver_port))
121+
.stdin(Stdio::piped())
122+
.stdout(Stdio::piped())
123+
.stderr(Stdio::piped());
117124

118-
let _ = thread::spawn(move || {
119-
let mut inner = match local_self.lock() {
120-
Ok(inner) => inner,
121-
Err(e) => {
122-
error!("Could not acquire lock: {e}");
123-
return;
124-
}
125-
};
126-
127-
let mut command = Command::new(inner.driver_path.clone());
128-
command
129-
.arg(format!("--port={}", inner.webdriver_port))
130-
.stdin(Stdio::piped())
131-
.stdout(Stdio::piped())
132-
.stderr(Stdio::piped());
133-
134-
let mut child = match command.spawn() {
135-
Ok(c) => {
136-
inner.webdriver_process_id = Some(c.id());
137-
c
138-
}
139-
Err(e) => {
140-
error!("Failed to spawn '{WEBDRIVER_BIN}': {e}");
141-
return;
142-
}
143-
};
144-
drop(inner);
125+
let mut child = match command.spawn() {
126+
Ok(c) => c,
127+
Err(e) => {
128+
error!("Failed to spawn '{WEBDRIVER_BIN}': {e}");
129+
return;
130+
}
131+
};
145132

146-
match child.wait() {
147-
Ok(c) => {
148-
info!("Terminated with ExitStatus: {c}");
149-
let stderr = child.stderr.take().unwrap();
150-
let stderr_lines = BufReader::new(stderr).lines();
151-
for line in stderr_lines {
152-
let line = line.unwrap();
133+
// Spawn a thread to handle the process stderr output (logging only)
134+
if let Some(stderr) = child.stderr.take() {
135+
thread::spawn(move || {
136+
let stderr_lines = BufReader::new(stderr).lines();
137+
for line in stderr_lines {
138+
if let Ok(line) = line {
153139
debug!("{line}");
154140
}
155141
}
156-
Err(e) => {
157-
error!("Failed waiting on '{WEBDRIVER_BIN}': {e}");
158-
}
159-
};
160-
});
142+
});
143+
}
144+
145+
// Store the Child handle (without stderr)
146+
{
147+
let mut inner = self.inner.lock().unwrap();
148+
inner.webdriver_child = Some(child);
149+
}
161150
}
162151

163152
/// Stops the WebDriver process
@@ -172,14 +161,28 @@ impl WebDriver {
172161
/// Returns `Ok(())` on success, or an error if the process termination
173162
/// fails.
174163
pub fn stop(&mut self) -> Result<()> {
175-
let inner = self.inner.lock().unwrap();
164+
let mut inner = self.inner.lock().unwrap();
176165

177166
// Only stop the process if we spawned it (not if it's external)
178167
if !inner.is_external {
179-
if let Some(id) = inner.webdriver_process_id {
180-
info!("Stopping '{WEBDRIVER_BIN}' (PID: {id})");
181-
let mut kill = Command::new("kill").arg(id.to_string()).spawn()?;
182-
kill.wait()?;
168+
if let Some(child) = inner.webdriver_child.as_mut() {
169+
info!("Stopping '{WEBDRIVER_BIN}' (PID: {})", child.id());
170+
171+
// Use the robust Child::kill() method which works cross-platform
172+
match child.kill() {
173+
Ok(_) => {
174+
info!("Successfully sent kill signal to WebDriver process");
175+
// Wait for the process to terminate
176+
match child.wait() {
177+
Ok(status) => info!("WebDriver process terminated with status: {status}"),
178+
Err(e) => warn!("Failed to wait for WebDriver process termination: {e}"),
179+
}
180+
}
181+
Err(e) => {
182+
error!("Failed to kill WebDriver process: {e}");
183+
return Err(anyhow!("Failed to kill WebDriver process: {e}"));
184+
}
185+
}
183186
}
184187
} else {
185188
warn!(
@@ -272,7 +275,14 @@ impl WebDriver {
272275
None => {
273276
// For external connections, we don't need the actual path
274277
// Just use a placeholder since we're not spawning
275-
String::from("/usr/bin/webdriver")
278+
#[cfg(target_os = "windows")]
279+
{
280+
String::from("C:\\Windows\\System32\\webdriver.exe")
281+
}
282+
#[cfg(not(target_os = "windows"))]
283+
{
284+
String::from("/usr/bin/webdriver")
285+
}
276286
}
277287
},
278288
};
@@ -283,7 +293,7 @@ impl WebDriver {
283293
inner: Arc::new(Mutex::new(WdInner {
284294
webdriver_port: port,
285295
driver_path: full_path,
286-
webdriver_process_id: None,
296+
webdriver_child: None,
287297
is_external: true, // Mark as external since we didn't spawn it
288298
})),
289299
})
@@ -314,8 +324,16 @@ impl WebDriver {
314324
}
315325

316326
#[cfg(target_os = "windows")]
317-
fn os_binary_path(path: PathBuf) -> PathBuf {
327+
fn os_binary_path(path: PathBuf) -> Result<PathBuf> {
318328
let app = format!("{WEBDRIVER_BIN}.exe");
319-
path.join(app)
329+
let full_path = path.join(app);
330+
if full_path.exists() {
331+
Ok(full_path)
332+
} else {
333+
Err(anyhow!(
334+
"No {WEBDRIVER_BIN}.exe found at '{}'",
335+
path.display()
336+
))
337+
}
320338
}
321339
}

0 commit comments

Comments
 (0)