Skip to content

Commit

Permalink
Merge pull request #38 from alexcrichton/modernize
Browse files Browse the repository at this point in the history
Bring various aspects of this crate up-to-date
  • Loading branch information
alexcrichton authored Feb 3, 2025
2 parents bda62e3 + 0e760ad commit 1583f98
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 43 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ jobs:
rust: nightly
- os: windows-latest
rust: stable
- os: macos-latest
rust: stable
steps:
- uses: actions/checkout@v2
- name: Install Rust
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ A crate to wait on a child process with a timeout specified across Unix and
Windows platforms.
"""
categories = ["os"]
edition = '2021'

[badges]
travis-ci = { repository = "alexcrichton/wait-timeout" }
Expand Down
4 changes: 2 additions & 2 deletions src/bin/reader.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::io::{stdin, Read};

#[allow(unused_must_use)]
fn main() {
let mut buffer: [u8; 32] = Default::default();
stdin().read(&mut buffer);
println!("about to block");
let _ = stdin().read(&mut buffer);
}
7 changes: 2 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@
#![deny(missing_docs, warnings)]
#![doc(html_root_url = "https://docs.rs/wait-timeout/0.1")]

#[cfg(unix)]
extern crate libc;

use std::io;
use std::process::{Child, ExitStatus};
use std::time::Duration;
Expand All @@ -45,9 +42,9 @@ mod imp;
#[path = "windows.rs"]
mod imp;

/// Extension methods for the standard `std::process::Child` type.
/// Extension methods for the standard [`std::process::Child`] type.
pub trait ChildExt {
/// Deprecated, use `wait_timeout` instead.
/// Deprecated, use [`ChildExt::wait_timeout`] instead.
#[doc(hidden)]
fn wait_timeout_ms(&mut self, ms: u32) -> io::Result<Option<ExitStatus>> {
self.wait_timeout(Duration::from_millis(ms as u64))
Expand Down
16 changes: 5 additions & 11 deletions src/unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
//! to the async nature of SIGCHLD, we use the self-pipe trick to transmit
//! data out of the signal handler to the rest of the application.
#![allow(bad_style)]

use std::cmp;
use libc::c_int;
use std::collections::HashMap;
use std::io::{self, Read, Write};
use std::mem;
Expand All @@ -27,8 +25,6 @@ use std::process::{Child, ExitStatus};
use std::sync::{Mutex, Once};
use std::time::{Duration, Instant};

use libc::{self, c_int};

static INIT: Once = Once::new();
static mut STATE: *mut State = 0 as *mut _;

Expand All @@ -47,7 +43,6 @@ pub fn wait_timeout(child: &mut Child, dur: Duration) -> io::Result<Option<ExitS
}

impl State {
#[allow(unused_assignments)]
fn init() {
unsafe {
// Create our "self pipe" and then set both ends to nonblocking
Expand Down Expand Up @@ -143,8 +138,8 @@ impl State {
.as_secs()
.checked_mul(1_000)
.and_then(|amt| amt.checked_add(timeout.subsec_nanos() as u64 / 1_000_000))
.unwrap_or(u64::max_value());
let timeout = cmp::min(<c_int>::max_value() as u64, timeout) as c_int;
.unwrap_or(u64::MAX);
let timeout = c_int::try_from(timeout).unwrap_or(c_int::MAX);
let r = unsafe { libc::poll(fds.as_mut_ptr(), 2, timeout) };
let timeout = match r {
0 => true,
Expand Down Expand Up @@ -193,13 +188,13 @@ impl State {
}

fn process_sigchlds(&self, map: &mut StateMap) {
for (&k, &mut (ref write, ref mut status)) in map {
for (k, (write, status)) in map {
// Already reaped, nothing to do here
if status.is_some() {
continue;
}

*status = unsafe { (*k).try_wait().unwrap() };
*status = unsafe { (**k).try_wait().unwrap() };
if status.is_some() {
notify(write);
}
Expand Down Expand Up @@ -247,7 +242,6 @@ fn notify(mut file: &UnixStream) {
// it. At that point we're guaranteed that there's something in the pipe
// which will wake up the other end at some point, so we just allow this
// signal to be coalesced with the pending signals on the pipe.
#[allow(unused_assignments)]
extern "C" fn sigchld_handler(signum: c_int, info: *mut libc::siginfo_t, ptr: *mut libc::c_void) {
type FnSigaction = extern "C" fn(c_int, *mut libc::siginfo_t, *mut libc::c_void);
type FnHandler = extern "C" fn(c_int);
Expand Down
52 changes: 27 additions & 25 deletions tests/smoke.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,18 @@
extern crate wait_timeout;

use std::env;
use std::io::Read;
use std::process::{Child, Command, Stdio};
use std::time::{Duration, Instant};

use wait_timeout::ChildExt;

macro_rules! t {
($e:expr) => {
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
}
};
}

fn sleeper(ms: u32) -> Child {
let mut me = env::current_exe().unwrap();
me.pop();
if me.ends_with("deps") {
me.pop();
}
me.push("sleep");
t!(Command::new(me).arg(ms.to_string()).spawn())
Command::new(me).arg(ms.to_string()).spawn().unwrap()
}

fn exit(code: u32) -> Child {
Expand All @@ -32,7 +22,7 @@ fn exit(code: u32) -> Child {
me.pop();
}
me.push("exit");
t!(Command::new(me).arg(code.to_string()).spawn())
Command::new(me).arg(code.to_string()).spawn().unwrap()
}

fn reader() -> Child {
Expand All @@ -42,24 +32,31 @@ fn reader() -> Child {
me.pop();
}
me.push("reader");
t!(Command::new(me).stdin(Stdio::piped()).spawn())
Command::new(me)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap()
}

#[test]
fn smoke_insta_timeout() {
let mut child = sleeper(1_000);
assert_eq!(t!(child.wait_timeout_ms(0)), None);
assert_eq!(child.wait_timeout_ms(0).unwrap(), None);

t!(child.kill());
let status = t!(child.wait());
child.kill().unwrap();
let status = child.wait().unwrap();
assert!(!status.success());
}

#[test]
fn smoke_success() {
let start = Instant::now();
let mut child = sleeper(0);
let status = t!(child.wait_timeout_ms(1_000)).expect("should have succeeded");
let status = child
.wait_timeout_ms(1_000)
.unwrap()
.expect("should have succeeded");
assert!(status.success());

assert!(start.elapsed() < Duration::from_millis(500));
Expand All @@ -69,37 +66,42 @@ fn smoke_success() {
fn smoke_timeout() {
let mut child = sleeper(1_000_000);
let start = Instant::now();
assert_eq!(t!(child.wait_timeout_ms(100)), None);
assert_eq!(child.wait_timeout_ms(100).unwrap(), None);
assert!(start.elapsed() > Duration::from_millis(80));

t!(child.kill());
let status = t!(child.wait());
child.kill().unwrap();
let status = child.wait().unwrap();
assert!(!status.success());
}

#[test]
fn smoke_reader() {
let mut child = reader();

// wait for the child to start and print something
let mut buf = [0; 20];
let _ = child.stdout.take().unwrap().read(&mut buf);

let dur = Duration::from_millis(100);
let status = t!(child.wait_timeout(dur)).unwrap();
let status = child.wait_timeout(dur).unwrap().unwrap();
assert!(status.success());
}

#[test]
fn exit_codes() {
let mut child = exit(0);
let status = t!(child.wait_timeout_ms(1_000)).unwrap();
let status = child.wait_timeout_ms(1_000).unwrap().unwrap();
assert_eq!(status.code(), Some(0));

let mut child = exit(1);
let status = t!(child.wait_timeout_ms(1_000)).unwrap();
let status = child.wait_timeout_ms(1_000).unwrap().unwrap();
assert_eq!(status.code(), Some(1));

// check STILL_ACTIVE on windows, on unix this ends up just getting
// truncated so don't bother with it.
if cfg!(windows) {
let mut child = exit(259);
let status = t!(child.wait_timeout_ms(1_000)).unwrap();
let status = child.wait_timeout_ms(1_000).unwrap().unwrap();
assert_eq!(status.code(), Some(259));
}
}

0 comments on commit 1583f98

Please sign in to comment.