Skip to content

Commit

Permalink
Minor improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
dylni committed Sep 11, 2022
1 parent 6e3dd14 commit e656140
Show file tree
Hide file tree
Showing 14 changed files with 123 additions and 143 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ authors = ["dylni"]
edition = "2021"
rust-version = "1.58.0"
description = """
Methods for ergonomically running processes with timeouts
Ergonomically run processes with limits
"""
readme = "README.md"
repository = "https://github.com/dylni/process_control"
license = "MIT OR Apache-2.0"
keywords = ["kill", "process", "terminate", "timeout", "wait"]
categories = ["concurrency", "os"]
exclude = [".*", "/rustfmt.toml", "/tests"]
exclude = [".*", "tests.rs", "/rustfmt.toml", "/src/bin", "/tests"]

[package.metadata.docs.rs]
rustc-args = ["--cfg", "process_control_docs_rs"]
Expand All @@ -26,7 +26,7 @@ crossbeam-channel = { version = "0.5", optional = true }
libc = "0.2.120"

[target.'cfg(all(unix, any(target_os = "espidf", target_os = "horizon", target_os = "openbsd", target_os = "redox", target_os = "tvos", target_os = "vxworks")))'.dependencies]
signal-hook = { version = "0.3" }
signal-hook = "0.3"

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.36", features = ["Win32_Foundation", "Win32_Security", "Win32_System_JobObjects", "Win32_System_Threading", "Win32_System_WindowsProgramming"] }
Expand Down
88 changes: 47 additions & 41 deletions src/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,11 @@ macro_rules! r#impl {
impl$(<$lifetime>)? Control for $struct$(<$lifetime>)? {
type Result = $return_type;

if_memory_limit! {
#[inline]
fn memory_limit(mut self, limit: usize) -> Self {
self.memory_limit = Some(limit);
self
}
#[cfg(any(doc, process_control_memory_limit))]
#[inline]
fn memory_limit(mut self, limit: usize) -> Self {
self.memory_limit = Some(limit);
self
}

#[inline]
Expand All @@ -92,7 +91,7 @@ macro_rules! r#impl {
let _ = self.process.stdin.take();
let mut result = $wait_fn(&mut self);

macro_rules! try_run {
macro_rules! run_if_ok {
( $get_result_fn:expr ) => {
if result.is_ok() {
if let Err(error) = $get_result_fn() {
Expand All @@ -110,10 +109,10 @@ macro_rules! r#impl {
imp::terminate_if_running(&mut self.process)
.and_then(|()| self.process.wait());
if self.strict_errors {
try_run!(|| next_result);
run_if_ok!(|| next_result);
}
}
try_run!(|| self.process.try_wait());
run_if_ok!(|| self.process.try_wait());

result
}
Expand Down Expand Up @@ -168,46 +167,53 @@ r#impl!(
Self::run_wait,
);

struct Reader(Option<JoinHandle<io::Result<Vec<u8>>>>);

impl Reader {
fn spawn<R>(source: Option<R>) -> io::Result<Self>
where
R: 'static + Read + Send,
{
source
.map(|mut source| {
thread::Builder::new().spawn(move || {
let mut buffer = Vec::new();
let _ = source.read_to_end(&mut buffer)?;
Ok(buffer)
})
})
.transpose()
.map(Self)
}

fn join(self) -> io::Result<Vec<u8>> {
self.0
.map(|x| x.join().unwrap_or_else(|x| panic::resume_unwind(x)))
.transpose()
.map(|x| x.unwrap_or_else(Vec::new))
}
}

impl OutputControl {
fn run_wait_with_output(&mut self) -> WaitResult<Output> {
let stdout_reader = spawn_reader(&mut self.process.stdout)?;
let stderr_reader = spawn_reader(&mut self.process.stderr)?;
macro_rules! reader {
( $stream:ident ) => {
Reader::spawn(self.process.$stream.take())
};
}

let stdout_reader = reader!(stdout)?;
let stderr_reader = reader!(stderr)?;

return self
.run_wait()?
self.run_wait()?
.map(|status| {
Ok(Output {
status,
stdout: join_reader(stdout_reader)?,
stderr: join_reader(stderr_reader)?,
stdout: stdout_reader.join()?,
stderr: stderr_reader.join()?,
})
})
.transpose();

type Reader = Option<JoinHandle<io::Result<Vec<u8>>>>;

fn spawn_reader<R>(source: &mut Option<R>) -> io::Result<Reader>
where
R: 'static + Read + Send,
{
source
.take()
.map(|mut source| {
thread::Builder::new().spawn(move || {
let mut buffer = Vec::new();
let _ = source.read_to_end(&mut buffer)?;
Ok(buffer)
})
})
.transpose()
}

fn join_reader(reader: Reader) -> io::Result<Vec<u8>> {
reader
.map(|x| x.join().unwrap_or_else(|x| panic::resume_unwind(x)))
.transpose()
.map(|x| x.unwrap_or_else(Vec::new))
}
.transpose()
}
}

Expand Down
55 changes: 22 additions & 33 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,6 @@ use std::process;
use std::process::Child;
use std::time::Duration;

macro_rules! if_memory_limit {
( $($item:item)+ ) => {
$(
#[cfg(process_control_memory_limit)]
$item
)+
};
}

mod control;

#[cfg_attr(unix, path = "unix/mod.rs")]
Expand Down Expand Up @@ -284,7 +275,6 @@ impl Display for ExitStatus {
impl From<process::ExitStatus> for ExitStatus {
#[inline]
fn from(value: process::ExitStatus) -> Self {
#[cfg_attr(windows, allow(clippy::useless_conversion))]
Self(value.into())
}
}
Expand Down Expand Up @@ -343,29 +333,28 @@ pub trait Control: private::Sealed {
/// [`wait`]: Self::wait
type Result;

if_memory_limit! {
/// Sets the total virtual memory limit for the process in bytes.
///
/// If the process attempts to allocate memory in excess of this limit,
/// the allocation will fail. The type of failure will depend on the
/// platform, and the process might terminate if it cannot handle it.
///
/// Small memory limits are safe, but they might prevent the operating
/// system from starting the process.
#[cfg_attr(
process_control_docs_rs,
doc(cfg(any(
target_os = "android",
all(
target_os = "linux",
any(target_env = "gnu", target_env = "musl"),
),
windows,
)))
)]
#[must_use]
fn memory_limit(self, limit: usize) -> Self;
}
/// Sets the total virtual memory limit for the process in bytes.
///
/// If the process attempts to allocate memory in excess of this limit, the
/// allocation will fail. The type of failure will depend on the platform,
/// and the process might terminate if it cannot handle it.
///
/// Small memory limits are safe, but they might prevent the operating
/// system from starting the process.
#[cfg(any(doc, process_control_memory_limit))]
#[cfg_attr(
process_control_docs_rs,
doc(cfg(any(
target_os = "android",
all(
target_os = "linux",
any(target_env = "gnu", target_env = "musl"),
),
windows,
)))
)]
#[must_use]
fn memory_limit(self, limit: usize) -> Self;

/// Sets the total time limit for the process in milliseconds.
///
Expand Down
6 changes: 4 additions & 2 deletions src/unix/exit_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ impl ExitStatus {
impl Display for ExitStatus {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.kind {
ExitStatusKind::Continued => write!(f, "continued (WIFCONTINUED)"),
ExitStatusKind::Continued => {
f.write_str("continued (WIFCONTINUED)")
}
ExitStatusKind::Dumped => {
write!(f, "signal: {} (core dumped)", self.value)
}
Expand All @@ -99,7 +101,7 @@ impl Display for ExitStatus {
write!(f, "stopped (not terminated) by signal: {}", self.value)
}
#[cfg(process_control_unix_waitid)]
ExitStatusKind::Trapped => write!(f, "trapped"),
ExitStatusKind::Trapped => f.write_str("trapped"),
ExitStatusKind::Uncategorized => {
write!(f, "uncategorized wait status: {}", self.value)
}
Expand Down
9 changes: 9 additions & 0 deletions src/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ pub(super) use exit_status::ExitStatus;

mod wait;

macro_rules! if_memory_limit {
( $($item:item)+ ) => {
$(
#[cfg(process_control_memory_limit)]
$item
)+
};
}

if_memory_limit! {
use std::convert::TryFrom;
use std::ptr;
Expand Down
10 changes: 3 additions & 7 deletions src/unix/wait/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,9 @@ use signal_hook::iterator::Signals;

use crate::WaitResult;

use super::run_with_time_limit;

use super::super::ExitStatus;
use super::super::Handle;

// https://github.com/rust-lang/rust-clippy/issues/3340
#[allow(clippy::useless_transmute)]
unsafe fn transmute_lifetime_mut<'a, T>(value: &mut T) -> &'a mut T
where
T: ?Sized,
Expand All @@ -28,8 +24,6 @@ fn run_on_drop<F>(drop_fn: F) -> impl Drop
where
F: FnOnce(),
{
return Dropper(ManuallyDrop::new(drop_fn));

struct Dropper<F>(ManuallyDrop<F>)
where
F: FnOnce();
Expand All @@ -42,6 +36,8 @@ where
(unsafe { ManuallyDrop::take(&mut self.0) })();
}
}

Dropper(ManuallyDrop::new(drop_fn))
}

pub(in super::super) fn wait(
Expand All @@ -57,7 +53,7 @@ pub(in super::super) fn wait(
});

let process = Arc::clone(&process);
run_with_time_limit(
super::run_with_time_limit(
move || {
let mut signals = Signals::new([SIGCHLD])?;
loop {
Expand Down
4 changes: 1 addition & 3 deletions src/unix/wait/waitid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,12 @@ use super::super::check_syscall;
use super::super::ExitStatus;
use super::super::Handle;

use super::run_with_time_limit;

pub(in super::super) fn wait(
handle: &mut Handle<'_>,
time_limit: Option<Duration>,
) -> WaitResult<ExitStatus> {
let pid = handle.pid.as_id();
run_with_time_limit(
super::run_with_time_limit(
move || loop {
let mut process_info = MaybeUninit::uninit();
check_result!(check_syscall(unsafe {
Expand Down
7 changes: 3 additions & 4 deletions src/windows/exit_status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@ use std::fmt::Formatter;
use std::os::windows::process::ExitStatusExt;
use std::process;

use super::DWORD;
use super::EXIT_SUCCESS;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) struct ExitStatus(DWORD);
pub(crate) struct ExitStatus(u32);

impl ExitStatus {
pub(super) const fn new(exit_code: DWORD) -> Self {
pub(super) const fn new(exit_code: u32) -> Self {
Self(exit_code)
}

pub(crate) fn success(self) -> bool {
self.0 == EXIT_SUCCESS
}

pub(crate) fn code(self) -> Option<DWORD> {
pub(crate) fn code(self) -> Option<u32> {
Some(self.0)
}
}
Expand Down
Loading

0 comments on commit e656140

Please sign in to comment.