Files
songbird/src/input/child.rs
Kyle Simpson aaab97511d Library: Add compatibility for legacy Tokio 0.2 (#40)
Adds support to the library for tokio 0.2 backward-compatibility. This should hopefully benefit, and prevent lavalink-rs from being blocked on this feature.

These can be reached using, e.g., `gateway-tokio-02`, `driver-tokio-02`, `serenity-rustls-tokio-02`, and `serenity-native-tokio-02` features.

Naturally, this requires some jiggering about with features and the underlying CI, which has been taken care of. Twilight can't be handled in this way, as their last tokio 0.2 version uses the deprecated Discord Gateway v6.
2021-02-04 02:34:07 +00:00

83 lines
2.1 KiB
Rust

use super::*;
use std::{
io::{BufReader, Read},
mem,
process::Child,
};
#[cfg(not(feature = "tokio-02-marker"))]
use tokio::runtime::Handle;
#[cfg(feature = "tokio-02-marker")]
use tokio_compat::runtime::Handle;
use tracing::debug;
/// 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(Vec<Child>);
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(children),
))
}
impl From<Child> for Reader {
fn from(container: Child) -> Self {
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> {
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) {
let children = mem::take(&mut self.0);
if let Ok(handle) = Handle::try_current() {
handle.spawn_blocking(move || {
cleanup_child_processes(children);
});
} else {
cleanup_child_processes(children);
}
}
}
fn cleanup_child_processes(mut children: Vec<Child>) {
let attempt = if let Some(child) = children.last_mut() {
child.kill()
} else {
return;
};
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);
}
}