-
Notifications
You must be signed in to change notification settings - Fork 120
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix: hand off process killing to blocking thread, await all children.
This should make dropping `ChildContainer`s and their parent `Input`s safer in async contexts. It seems like SIGINT is insufficient to make wait terminate, but SIGKILL suffices. This introduced a new problem, namely that we have to remember and wait on *every* pid we create. This should, hopefully, put the issue of zombie processes to bed for good.
- Loading branch information
1 parent
7d4891d
commit b245309
Showing
4 changed files
with
53 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,79 @@ | ||
use super::*; | ||
use std::{ | ||
io::{BufReader, Read}, | ||
mem, | ||
process::Child, | ||
}; | ||
use tokio::runtime::Handle; | ||
use tracing::debug; | ||
|
||
#[cfg(unix)] | ||
use nix::{ | ||
sys::signal::{self, Signal}, | ||
unistd::Pid, | ||
}; | ||
|
||
/// Handle for a child process which ensures that any subprocesses are properly closed | ||
/// on drop. | ||
/// | ||
/// # Warning | ||
/// To allow proper cleanup of child processes, if you create a process chain you must | ||
/// make sure to use `From<Vec<Child>>`. Here, the *last* process in the `Vec` will be | ||
/// used as the audio byte source. | ||
#[derive(Debug)] | ||
pub struct ChildContainer(Child); | ||
pub struct ChildContainer(Vec<Child>); | ||
|
||
pub(crate) fn child_to_reader<T>(child: Child) -> Reader { | ||
pub(crate) fn children_to_reader<T>(children: Vec<Child>) -> Reader { | ||
Reader::Pipe(BufReader::with_capacity( | ||
STEREO_FRAME_SIZE * mem::size_of::<T>() * CHILD_BUFFER_LEN, | ||
ChildContainer(child), | ||
ChildContainer(children), | ||
)) | ||
} | ||
|
||
impl From<Child> for Reader { | ||
fn from(container: Child) -> Self { | ||
child_to_reader::<f32>(container) | ||
children_to_reader::<f32>(vec![container]) | ||
} | ||
} | ||
|
||
impl From<Vec<Child>> for Reader { | ||
fn from(container: Vec<Child>) -> Self { | ||
children_to_reader::<f32>(container) | ||
} | ||
} | ||
|
||
impl Read for ChildContainer { | ||
fn read(&mut self, buffer: &mut [u8]) -> IoResult<usize> { | ||
self.0.stdout.as_mut().unwrap().read(buffer) | ||
match self.0.last_mut() { | ||
Some(ref mut child) => child.stdout.as_mut().unwrap().read(buffer), | ||
None => Ok(0), | ||
} | ||
} | ||
} | ||
|
||
impl Drop for ChildContainer { | ||
fn drop(&mut self) { | ||
#[cfg(not(unix))] | ||
let attempt = self.0.kill(); | ||
let children = mem::take(&mut self.0); | ||
|
||
#[cfg(unix)] | ||
let attempt = { | ||
let pid = Pid::from_raw(self.0.id() as i32); | ||
let _ = signal::kill(pid, Signal::SIGINT); | ||
if let Ok(handle) = Handle::try_current() { | ||
handle.spawn_blocking(move || { | ||
cleanup_child_processes(children); | ||
}); | ||
} else { | ||
cleanup_child_processes(children); | ||
} | ||
} | ||
} | ||
|
||
self.0.wait() | ||
}; | ||
fn cleanup_child_processes(mut children: Vec<Child>) { | ||
let attempt = if let Some(child) = children.last_mut() { | ||
child.kill() | ||
} else { | ||
return; | ||
}; | ||
|
||
if let Err(e) = attempt { | ||
debug!("Error awaiting child process: {:?}", e); | ||
} | ||
let attempt = attempt.and_then(|_| { | ||
children | ||
.iter_mut() | ||
.rev() | ||
.try_for_each(|child| child.wait().map(|_| ())) | ||
}); | ||
|
||
if let Err(e) = attempt { | ||
debug!("Error awaiting child process: {:?}", e); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters