Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f09592a

Browse files
committedMay 14, 2014
io: Implement process wait timeouts
This implements set_timeout() for std::io::Process which will affect wait() operations on the process. This follows the same pattern as the rest of the timeouts emerging in std::io::net. The implementation was super easy for everything except libnative on unix (backwards from usual!), which required a good bit of signal handling. There's a doc comment explaining the strategy in libnative. Internally, this also required refactoring the "helper thread" implementation used by libnative to allow for an extra helper thread (not just the timer). This is a breaking change in terms of the io::Process API. It is now possible for wait() to fail, and subsequently wait_with_output(). These two functions now return IoResult<T> due to the fact that they can time out. Additionally, the wait_with_output() function has moved from taking `&mut self` to taking `self`. If a timeout occurs while waiting with output, the semantics are undesirable in almost all cases if attempting to re-wait on the process. Equivalent functionality can still be achieved by dealing with the output handles manually. [breaking-change] cc #13523
1 parent 9f7caed commit f09592a

23 files changed

+876
-326
lines changed
 

‎src/compiletest/procsrv.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,20 @@ pub fn run(lib_path: &str,
6868
input: Option<~str>) -> Option<Result> {
6969

7070
let env = env.clone().append(target_env(lib_path, prog).as_slice());
71-
let mut opt_process = Process::configure(ProcessConfig {
71+
let opt_process = Process::configure(ProcessConfig {
7272
program: prog,
7373
args: args,
7474
env: Some(env.as_slice()),
7575
.. ProcessConfig::new()
7676
});
7777

7878
match opt_process {
79-
Ok(ref mut process) => {
79+
Ok(mut process) => {
8080
for input in input.iter() {
8181
process.stdin.get_mut_ref().write(input.as_bytes()).unwrap();
8282
}
83-
let ProcessOutput { status, output, error } = process.wait_with_output();
83+
let ProcessOutput { status, output, error } =
84+
process.wait_with_output().unwrap();
8485

8586
Some(Result {
8687
status: status,

‎src/compiletest/runtest.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -502,16 +502,17 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path)
502502
let args = &[lldb_batchmode_script, test_executable_str, debugger_script_str];
503503
let env = &[("PYTHONPATH".to_owned(), config.lldb_python_dir.clone().unwrap())];
504504

505-
let mut opt_process = Process::configure(ProcessConfig {
505+
let opt_process = Process::configure(ProcessConfig {
506506
program: "python",
507507
args: args,
508508
env: Some(env),
509509
.. ProcessConfig::new()
510510
});
511511

512512
let (status, out, err) = match opt_process {
513-
Ok(ref mut process) => {
514-
let ProcessOutput { status, output, error } = process.wait_with_output();
513+
Ok(process) => {
514+
let ProcessOutput { status, output, error } =
515+
process.wait_with_output().unwrap();
515516

516517
(status,
517518
str::from_utf8(output.as_slice()).unwrap().to_owned(),

‎src/libcore/str.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,7 @@ impl<'a> StrSlice<'a> for &'a str {
17251725
#[inline]
17261726
fn is_char_boundary(&self, index: uint) -> bool {
17271727
if index == self.len() { return true; }
1728+
if index > self.len() { return false; }
17281729
let b = self[index];
17291730
return b < 128u8 || b >= 192u8;
17301731
}

‎src/liblibc/lib.rs

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ pub use funcs::bsd43::{shutdown};
173173
#[cfg(unix)] pub use consts::os::posix88::{EADDRINUSE, ENOENT, EISDIR, EAGAIN, EWOULDBLOCK};
174174
#[cfg(unix)] pub use consts::os::posix88::{ECANCELED, SIGINT, EINPROGRESS};
175175
#[cfg(unix)] pub use consts::os::posix88::{SIGTERM, SIGKILL, SIGPIPE, PROT_NONE};
176-
#[cfg(unix)] pub use consts::os::posix01::{SIG_IGN, WNOHANG};
176+
#[cfg(unix)] pub use consts::os::posix01::{SIG_IGN};
177177
#[cfg(unix)] pub use consts::os::bsd44::{AF_UNIX};
178178

179179
#[cfg(unix)] pub use types::os::common::posix01::{pthread_t, timespec, timezone};
@@ -2473,8 +2473,6 @@ pub mod consts {
24732473

24742474
pub static CLOCK_REALTIME: c_int = 0;
24752475
pub static CLOCK_MONOTONIC: c_int = 1;
2476-
2477-
pub static WNOHANG: c_int = 1;
24782476
}
24792477
pub mod posix08 {
24802478
}
@@ -2924,8 +2922,6 @@ pub mod consts {
29242922

29252923
pub static CLOCK_REALTIME: c_int = 0;
29262924
pub static CLOCK_MONOTONIC: c_int = 4;
2927-
2928-
pub static WNOHANG: c_int = 1;
29292925
}
29302926
pub mod posix08 {
29312927
}
@@ -3313,8 +3309,6 @@ pub mod consts {
33133309
pub static PTHREAD_CREATE_JOINABLE: c_int = 1;
33143310
pub static PTHREAD_CREATE_DETACHED: c_int = 2;
33153311
pub static PTHREAD_STACK_MIN: size_t = 8192;
3316-
3317-
pub static WNOHANG: c_int = 1;
33183312
}
33193313
pub mod posix08 {
33203314
}
@@ -3980,16 +3974,6 @@ pub mod funcs {
39803974
}
39813975
}
39823976

3983-
pub mod wait {
3984-
use types::os::arch::c95::{c_int};
3985-
use types::os::arch::posix88::{pid_t};
3986-
3987-
extern {
3988-
pub fn waitpid(pid: pid_t, status: *mut c_int, options: c_int)
3989-
-> pid_t;
3990-
}
3991-
}
3992-
39933977
pub mod glob {
39943978
use types::os::arch::c95::{c_char, c_int};
39953979
use types::os::common::posix01::{glob_t};

‎src/libnative/io/c_unix.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@
1010

1111
//! C definitions used by libnative that don't belong in liblibc
1212
13+
#![allow(dead_code)]
14+
1315
pub use self::select::fd_set;
16+
pub use self::signal::{sigaction, siginfo, sigset_t};
17+
pub use self::signal::{SA_ONSTACK, SA_RESTART, SA_RESETHAND, SA_NOCLDSTOP};
18+
pub use self::signal::{SA_NODEFER, SA_NOCLDWAIT, SA_SIGINFO, SIGCHLD};
1419

1520
use libc;
1621

@@ -34,6 +39,8 @@ pub static MSG_DONTWAIT: libc::c_int = 0x80;
3439
#[cfg(target_os = "android")]
3540
pub static MSG_DONTWAIT: libc::c_int = 0x40;
3641

42+
pub static WNOHANG: libc::c_int = 1;
43+
3744
extern {
3845
pub fn gettimeofday(timeval: *mut libc::timeval,
3946
tzp: *libc::c_void) -> libc::c_int;
@@ -49,6 +56,17 @@ extern {
4956
optlen: *mut libc::socklen_t) -> libc::c_int;
5057
pub fn ioctl(fd: libc::c_int, req: libc::c_ulong, ...) -> libc::c_int;
5158

59+
60+
pub fn waitpid(pid: libc::pid_t, status: *mut libc::c_int,
61+
options: libc::c_int) -> libc::pid_t;
62+
63+
pub fn sigaction(signum: libc::c_int,
64+
act: *sigaction,
65+
oldact: *mut sigaction) -> libc::c_int;
66+
67+
pub fn sigaddset(set: *mut sigset_t, signum: libc::c_int) -> libc::c_int;
68+
pub fn sigdelset(set: *mut sigset_t, signum: libc::c_int) -> libc::c_int;
69+
pub fn sigemptyset(set: *mut sigset_t) -> libc::c_int;
5270
}
5371

5472
#[cfg(target_os = "macos")]
@@ -81,3 +99,94 @@ mod select {
8199
set.fds_bits[fd / uint::BITS] |= 1 << (fd % uint::BITS);
82100
}
83101
}
102+
103+
#[cfg(target_os = "linux")]
104+
#[cfg(target_os = "android")]
105+
mod signal {
106+
use libc;
107+
108+
pub static SA_NOCLDSTOP: libc::c_ulong = 0x00000001;
109+
pub static SA_NOCLDWAIT: libc::c_ulong = 0x00000002;
110+
pub static SA_NODEFER: libc::c_ulong = 0x40000000;
111+
pub static SA_ONSTACK: libc::c_ulong = 0x08000000;
112+
pub static SA_RESETHAND: libc::c_ulong = 0x80000000;
113+
pub static SA_RESTART: libc::c_ulong = 0x10000000;
114+
pub static SA_SIGINFO: libc::c_ulong = 0x00000004;
115+
pub static SIGCHLD: libc::c_int = 17;
116+
117+
// This definition is not as accurate as it could be, {pid, uid, status} is
118+
// actually a giant union. Currently we're only interested in these fields,
119+
// however.
120+
pub struct siginfo {
121+
si_signo: libc::c_int,
122+
si_errno: libc::c_int,
123+
si_code: libc::c_int,
124+
pub pid: libc::pid_t,
125+
pub uid: libc::uid_t,
126+
pub status: libc::c_int,
127+
}
128+
129+
pub struct sigaction {
130+
pub sa_handler: extern fn(libc::c_int),
131+
pub sa_mask: sigset_t,
132+
pub sa_flags: libc::c_ulong,
133+
sa_restorer: *mut libc::c_void,
134+
}
135+
136+
#[cfg(target_word_size = "32")]
137+
pub struct sigset_t {
138+
__val: [libc::c_ulong, ..32],
139+
}
140+
#[cfg(target_word_size = "64")]
141+
pub struct sigset_t {
142+
__val: [libc::c_ulong, ..16],
143+
}
144+
}
145+
146+
#[cfg(target_os = "macos")]
147+
#[cfg(target_os = "freebsd")]
148+
mod signal {
149+
use libc;
150+
151+
pub static SA_ONSTACK: libc::c_int = 0x0001;
152+
pub static SA_RESTART: libc::c_int = 0x0002;
153+
pub static SA_RESETHAND: libc::c_int = 0x0004;
154+
pub static SA_NOCLDSTOP: libc::c_int = 0x0008;
155+
pub static SA_NODEFER: libc::c_int = 0x0010;
156+
pub static SA_NOCLDWAIT: libc::c_int = 0x0020;
157+
pub static SA_SIGINFO: libc::c_int = 0x0040;
158+
pub static SIGCHLD: libc::c_int = 20;
159+
160+
#[cfg(target_os = "macos")]
161+
pub type sigset_t = u32;
162+
#[cfg(target_os = "freebsd")]
163+
pub struct sigset_t {
164+
bits: [u32, ..4],
165+
}
166+
167+
// This structure has more fields, but we're not all that interested in
168+
// them.
169+
pub struct siginfo {
170+
pub si_signo: libc::c_int,
171+
pub si_errno: libc::c_int,
172+
pub si_code: libc::c_int,
173+
pub pid: libc::pid_t,
174+
pub uid: libc::uid_t,
175+
pub status: libc::c_int,
176+
}
177+
178+
#[cfg(target_os = "macos")]
179+
pub struct sigaction {
180+
pub sa_handler: extern fn(libc::c_int),
181+
sa_tramp: *mut libc::c_void,
182+
pub sa_mask: sigset_t,
183+
pub sa_flags: libc::c_int,
184+
}
185+
186+
#[cfg(target_os = "freebsd")]
187+
pub struct sigaction {
188+
pub sa_handler: extern fn(libc::c_int),
189+
pub sa_flags: libc::c_int,
190+
pub sa_mask: sigset_t,
191+
}
192+
}

‎src/libnative/io/helper_thread.rs

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! Implementation of the helper thread for the timer module
12+
//!
13+
//! This module contains the management necessary for the timer worker thread.
14+
//! This thread is responsible for performing the send()s on channels for timers
15+
//! that are using channels instead of a blocking call.
16+
//!
17+
//! The timer thread is lazily initialized, and it's shut down via the
18+
//! `shutdown` function provided. It must be maintained as an invariant that
19+
//! `shutdown` is only called when the entire program is finished. No new timers
20+
//! can be created in the future and there must be no active timers at that
21+
//! time.
22+
23+
#![macro_escape]
24+
25+
use std::mem;
26+
use std::rt::bookkeeping;
27+
use std::rt;
28+
use std::ty::Unsafe;
29+
use std::unstable::mutex::StaticNativeMutex;
30+
31+
use task;
32+
33+
/// A structure for management of a helper thread.
34+
///
35+
/// This is generally a static structure which tracks the lifetime of a helper
36+
/// thread.
37+
///
38+
/// The fields of this helper are all public, but they should not be used, this
39+
/// is for static initialization.
40+
pub struct Helper<M> {
41+
/// Internal lock which protects the remaining fields
42+
pub lock: StaticNativeMutex,
43+
44+
// You'll notice that the remaining fields are Unsafe<T>, and this is
45+
// because all helper thread operations are done through &self, but we need
46+
// these to be mutable (once `lock` is held).
47+
48+
/// Lazily allocated channel to send messages to the helper thread.
49+
pub chan: Unsafe<*mut Sender<M>>,
50+
51+
/// OS handle used to wake up a blocked helper thread
52+
pub signal: Unsafe<uint>,
53+
54+
/// Flag if this helper thread has booted and been initialized yet.
55+
pub initialized: Unsafe<bool>,
56+
}
57+
58+
macro_rules! helper_init( (static mut $name:ident: Helper<$m:ty>) => (
59+
static mut $name: Helper<$m> = Helper {
60+
lock: ::std::unstable::mutex::NATIVE_MUTEX_INIT,
61+
chan: ::std::ty::Unsafe {
62+
value: 0 as *mut Sender<$m>,
63+
marker1: ::std::kinds::marker::InvariantType,
64+
},
65+
signal: ::std::ty::Unsafe {
66+
value: 0,
67+
marker1: ::std::kinds::marker::InvariantType,
68+
},
69+
initialized: ::std::ty::Unsafe {
70+
value: false,
71+
marker1: ::std::kinds::marker::InvariantType,
72+
},
73+
};
74+
) )
75+
76+
impl<M: Send> Helper<M> {
77+
/// Lazily boots a helper thread, becoming a no-op if the helper has already
78+
/// been spawned.
79+
///
80+
/// This function will check to see if the thread has been initialized, and
81+
/// if it has it returns quickly. If initialization has not happened yet,
82+
/// the closure `f` will be run (inside of the initialization lock) and
83+
/// passed to the helper thread in a separate task.
84+
///
85+
/// This function is safe to be called many times.
86+
pub fn boot<T: Send>(&'static self,
87+
f: || -> T,
88+
helper: fn(imp::signal, Receiver<M>, T)) {
89+
unsafe {
90+
let _guard = self.lock.lock();
91+
if !*self.initialized.get() {
92+
let (tx, rx) = channel();
93+
*self.chan.get() = mem::transmute(box tx);
94+
let (receive, send) = imp::new();
95+
*self.signal.get() = send as uint;
96+
97+
let t = f();
98+
task::spawn(proc() {
99+
bookkeeping::decrement();
100+
helper(receive, rx, t);
101+
self.lock.lock().signal()
102+
});
103+
104+
rt::at_exit(proc() { self.shutdown() });
105+
*self.initialized.get() = true;
106+
}
107+
}
108+
}
109+
110+
/// Sends a message to a spawned worker thread.
111+
///
112+
/// This is only valid if the worker thread has previously booted
113+
pub fn send(&'static self, msg: M) {
114+
unsafe {
115+
let _guard = self.lock.lock();
116+
117+
// Must send and *then* signal to ensure that the child receives the
118+
// message. Otherwise it could wake up and go to sleep before we
119+
// send the message.
120+
assert!(!self.chan.get().is_null());
121+
(**self.chan.get()).send(msg);
122+
imp::signal(*self.signal.get() as imp::signal);
123+
}
124+
}
125+
126+
fn shutdown(&'static self) {
127+
unsafe {
128+
// Shut down, but make sure this is done inside our lock to ensure
129+
// that we'll always receive the exit signal when the thread
130+
// returns.
131+
let guard = self.lock.lock();
132+
133+
// Close the channel by destroying it
134+
let chan: Box<Sender<M>> = mem::transmute(*self.chan.get());
135+
*self.chan.get() = 0 as *mut Sender<M>;
136+
drop(chan);
137+
imp::signal(*self.signal.get() as imp::signal);
138+
139+
// Wait for the child to exit
140+
guard.wait();
141+
drop(guard);
142+
143+
// Clean up after ourselves
144+
self.lock.destroy();
145+
imp::close(*self.signal.get() as imp::signal);
146+
*self.signal.get() = 0;
147+
}
148+
}
149+
}
150+
151+
#[cfg(unix)]
152+
mod imp {
153+
use libc;
154+
use std::os;
155+
156+
use io::file::FileDesc;
157+
158+
pub type signal = libc::c_int;
159+
160+
pub fn new() -> (signal, signal) {
161+
let pipe = os::pipe();
162+
(pipe.input, pipe.out)
163+
}
164+
165+
pub fn signal(fd: libc::c_int) {
166+
FileDesc::new(fd, false).inner_write([0]).unwrap();
167+
}
168+
169+
pub fn close(fd: libc::c_int) {
170+
let _fd = FileDesc::new(fd, true);
171+
}
172+
}
173+
174+
#[cfg(windows)]
175+
mod imp {
176+
use libc::{BOOL, LPCSTR, HANDLE, LPSECURITY_ATTRIBUTES, CloseHandle};
177+
use std::ptr;
178+
use libc;
179+
180+
pub type signal = HANDLE;
181+
182+
pub fn new() -> (HANDLE, HANDLE) {
183+
unsafe {
184+
let handle = CreateEventA(ptr::mut_null(), libc::FALSE, libc::FALSE,
185+
ptr::null());
186+
(handle, handle)
187+
}
188+
}
189+
190+
pub fn signal(handle: HANDLE) {
191+
assert!(unsafe { SetEvent(handle) != 0 });
192+
}
193+
194+
pub fn close(handle: HANDLE) {
195+
assert!(unsafe { CloseHandle(handle) != 0 });
196+
}
197+
198+
extern "system" {
199+
fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
200+
bManualReset: BOOL,
201+
bInitialState: BOOL,
202+
lpName: LPCSTR) -> HANDLE;
203+
fn SetEvent(hEvent: HANDLE) -> BOOL;
204+
}
205+
}

‎src/libnative/io/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ use ai = std::io::net::addrinfo;
4040
pub use self::file::FileDesc;
4141
pub use self::process::Process;
4242

43+
mod helper_thread;
44+
4345
// Native I/O implementations
4446
pub mod addrinfo;
4547
pub mod net;
@@ -75,8 +77,6 @@ pub mod pipe;
7577
#[cfg(unix)] #[path = "c_unix.rs"] mod c;
7678
#[cfg(windows)] #[path = "c_win32.rs"] mod c;
7779

78-
mod timer_helper;
79-
8080
pub type IoResult<T> = Result<T, IoError>;
8181

8282
fn unimpl() -> IoError {

‎src/libnative/io/process.rs

Lines changed: 311 additions & 52 deletions
Large diffs are not rendered by default.

‎src/libnative/io/timer_helper.rs

Lines changed: 0 additions & 149 deletions
This file was deleted.

‎src/libnative/io/timer_unix.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,14 @@ use std::os;
5252
use std::ptr;
5353
use std::rt::rtio;
5454
use std::sync::atomics;
55+
use std::comm;
5556

5657
use io::IoResult;
5758
use io::c;
5859
use io::file::FileDesc;
59-
use io::timer_helper;
60+
use io::helper_thread::Helper;
61+
62+
helper_init!(static mut HELPER: Helper<Req>)
6063

6164
pub struct Timer {
6265
id: uint,
@@ -79,9 +82,6 @@ pub enum Req {
7982
// Remove a timer based on its id and then send it back on the channel
8083
// provided
8184
RemoveTimer(uint, Sender<Box<Inner>>),
82-
83-
// Shut down the loop and then ACK this channel once it's shut down
84-
Shutdown,
8585
}
8686

8787
// returns the current time (in milliseconds)
@@ -93,7 +93,7 @@ pub fn now() -> u64 {
9393
}
9494
}
9595

96-
fn helper(input: libc::c_int, messages: Receiver<Req>) {
96+
fn helper(input: libc::c_int, messages: Receiver<Req>, _: ()) {
9797
let mut set: c::fd_set = unsafe { mem::init() };
9898

9999
let mut fd = FileDesc::new(input, true);
@@ -163,7 +163,7 @@ fn helper(input: libc::c_int, messages: Receiver<Req>) {
163163
1 => {
164164
loop {
165165
match messages.try_recv() {
166-
Ok(Shutdown) => {
166+
Err(comm::Disconnected) => {
167167
assert!(active.len() == 0);
168168
break 'outer;
169169
}
@@ -202,7 +202,7 @@ fn helper(input: libc::c_int, messages: Receiver<Req>) {
202202

203203
impl Timer {
204204
pub fn new() -> IoResult<Timer> {
205-
timer_helper::boot(helper);
205+
unsafe { HELPER.boot(|| {}, helper); }
206206

207207
static mut ID: atomics::AtomicUint = atomics::INIT_ATOMIC_UINT;
208208
let id = unsafe { ID.fetch_add(1, atomics::Relaxed) };
@@ -235,7 +235,7 @@ impl Timer {
235235
Some(i) => i,
236236
None => {
237237
let (tx, rx) = channel();
238-
timer_helper::send(RemoveTimer(self.id, tx));
238+
unsafe { HELPER.send(RemoveTimer(self.id, tx)); }
239239
rx.recv()
240240
}
241241
}
@@ -261,7 +261,7 @@ impl rtio::RtioTimer for Timer {
261261
inner.interval = msecs;
262262
inner.target = now + msecs;
263263

264-
timer_helper::send(NewTimer(inner));
264+
unsafe { HELPER.send(NewTimer(inner)); }
265265
return rx;
266266
}
267267

@@ -275,7 +275,7 @@ impl rtio::RtioTimer for Timer {
275275
inner.interval = msecs;
276276
inner.target = now + msecs;
277277

278-
timer_helper::send(NewTimer(inner));
278+
unsafe { HELPER.send(NewTimer(inner)); }
279279
return rx;
280280
}
281281
}

‎src/libnative/io/timer_win32.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@
2323
use libc;
2424
use std::ptr;
2525
use std::rt::rtio;
26+
use std::comm;
2627

27-
use io::timer_helper;
28+
use io::helper_thread::Helper;
2829
use io::IoResult;
2930

31+
helper_init!(static mut HELPER: Helper<Req>)
32+
3033
pub struct Timer {
3134
obj: libc::HANDLE,
3235
on_worker: bool,
@@ -35,10 +38,9 @@ pub struct Timer {
3538
pub enum Req {
3639
NewTimer(libc::HANDLE, Sender<()>, bool),
3740
RemoveTimer(libc::HANDLE, Sender<()>),
38-
Shutdown,
3941
}
4042

41-
fn helper(input: libc::HANDLE, messages: Receiver<Req>) {
43+
fn helper(input: libc::HANDLE, messages: Receiver<Req>, _: ()) {
4244
let mut objs = vec![input];
4345
let mut chans = vec![];
4446

@@ -67,12 +69,12 @@ fn helper(input: libc::HANDLE, messages: Receiver<Req>) {
6769
None => {}
6870
}
6971
}
70-
Ok(Shutdown) => {
72+
Err(comm::Disconnected) => {
7173
assert_eq!(objs.len(), 1);
7274
assert_eq!(chans.len(), 0);
7375
break 'outer;
7476
}
75-
_ => break
77+
Err(..) => break
7678
}
7779
}
7880
} else {
@@ -102,7 +104,7 @@ pub fn now() -> u64 {
102104

103105
impl Timer {
104106
pub fn new() -> IoResult<Timer> {
105-
timer_helper::boot(helper);
107+
unsafe { HELPER.boot(|| {}, helper) }
106108

107109
let obj = unsafe {
108110
imp::CreateWaitableTimerA(ptr::mut_null(), 0, ptr::null())
@@ -124,7 +126,7 @@ impl Timer {
124126
if !self.on_worker { return }
125127

126128
let (tx, rx) = channel();
127-
timer_helper::send(RemoveTimer(self.obj, tx));
129+
unsafe { HELPER.send(RemoveTimer(self.obj, tx)) }
128130
rx.recv();
129131

130132
self.on_worker = false;
@@ -157,7 +159,7 @@ impl rtio::RtioTimer for Timer {
157159
ptr::mut_null(), 0)
158160
}, 1);
159161

160-
timer_helper::send(NewTimer(self.obj, tx, true));
162+
unsafe { HELPER.send(NewTimer(self.obj, tx, true)) }
161163
self.on_worker = true;
162164
return rx;
163165
}
@@ -173,7 +175,7 @@ impl rtio::RtioTimer for Timer {
173175
ptr::null(), ptr::mut_null(), 0)
174176
}, 1);
175177

176-
timer_helper::send(NewTimer(self.obj, tx, false));
178+
unsafe { HELPER.send(NewTimer(self.obj, tx, false)) }
177179
self.on_worker = true;
178180

179181
return rx;

‎src/libnative/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
// NB this crate explicitly does *not* allow glob imports, please seriously
5656
// consider whether they're needed before adding that feature here (the
5757
// answer is that you don't need them)
58+
#![feature(macro_rules)]
5859

5960
extern crate libc;
6061

‎src/librustc/back/archive.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ fn run_ar(sess: &Session, args: &str, cwd: Option<&Path>,
5454
cwd: cwd.map(|a| &*a),
5555
.. ProcessConfig::new()
5656
}) {
57-
Ok(mut prog) => {
58-
let o = prog.wait_with_output();
57+
Ok(prog) => {
58+
let o = prog.wait_with_output().unwrap();
5959
if !o.status.success() {
6060
sess.err(format!("{} {} failed with: {}", ar, args.connect(" "),
6161
o.status));

‎src/librustuv/process.rs

Lines changed: 72 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ use std::rt::task::BlockedTask;
1919
use homing::{HomingIO, HomeHandle};
2020
use pipe::PipeWatcher;
2121
use super::{UvHandle, UvError, uv_error_to_io_error,
22-
wait_until_woken_after, wakeup};
22+
wait_until_woken_after, wakeup, Loop};
23+
use timer::TimerWatcher;
2324
use uvio::UvIoFactory;
2425
use uvll;
2526

@@ -32,6 +33,16 @@ pub struct Process {
3233

3334
/// Collected from the exit_cb
3435
exit_status: Option<process::ProcessExit>,
36+
37+
/// Lazily initialized timeout timer
38+
timer: Option<Box<TimerWatcher>>,
39+
timeout_state: TimeoutState,
40+
}
41+
42+
enum TimeoutState {
43+
NoTimeout,
44+
TimeoutPending,
45+
TimeoutElapsed,
3546
}
3647

3748
impl Process {
@@ -92,6 +103,8 @@ impl Process {
92103
home: io_loop.make_handle(),
93104
to_wake: None,
94105
exit_status: None,
106+
timer: None,
107+
timeout_state: NoTimeout,
95108
};
96109
match unsafe {
97110
uvll::uv_spawn(io_loop.uv_loop(), handle, &options)
@@ -223,21 +236,71 @@ impl RtioProcess for Process {
223236
}
224237
}
225238

226-
fn wait(&mut self) -> process::ProcessExit {
239+
fn wait(&mut self) -> Result<process::ProcessExit, IoError> {
227240
// Make sure (on the home scheduler) that we have an exit status listed
228241
let _m = self.fire_homing_missile();
229242
match self.exit_status {
230-
Some(..) => {}
231-
None => {
232-
// If there's no exit code previously listed, then the
233-
// process's exit callback has yet to be invoked. We just
234-
// need to deschedule ourselves and wait to be reawoken.
243+
Some(status) => return Ok(status),
244+
None => {}
245+
}
246+
247+
// If there's no exit code previously listed, then the process's exit
248+
// callback has yet to be invoked. We just need to deschedule ourselves
249+
// and wait to be reawoken.
250+
match self.timeout_state {
251+
NoTimeout | TimeoutPending => {
235252
wait_until_woken_after(&mut self.to_wake, &self.uv_loop(), || {});
236-
assert!(self.exit_status.is_some());
237253
}
254+
TimeoutElapsed => {}
255+
}
256+
257+
// If there's still no exit status listed, then we timed out, and we
258+
// need to return.
259+
match self.exit_status {
260+
Some(status) => Ok(status),
261+
None => Err(uv_error_to_io_error(UvError(uvll::ECANCELED)))
262+
}
263+
}
264+
265+
fn set_timeout(&mut self, timeout: Option<u64>) {
266+
let _m = self.fire_homing_missile();
267+
self.timeout_state = NoTimeout;
268+
let ms = match timeout {
269+
Some(ms) => ms,
270+
None => {
271+
match self.timer {
272+
Some(ref mut timer) => timer.stop(),
273+
None => {}
274+
}
275+
return
276+
}
277+
};
278+
if self.timer.is_none() {
279+
let loop_ = Loop::wrap(unsafe {
280+
uvll::get_loop_for_uv_handle(self.uv_handle())
281+
});
282+
let mut timer = box TimerWatcher::new_home(&loop_, self.home().clone());
283+
unsafe {
284+
timer.set_data(self as *mut _ as *Process);
285+
}
286+
self.timer = Some(timer);
238287
}
239288

240-
self.exit_status.unwrap()
289+
let timer = self.timer.get_mut_ref();
290+
timer.stop();
291+
timer.start(timer_cb, ms, 0);
292+
self.timeout_state = TimeoutPending;
293+
294+
extern fn timer_cb(timer: *uvll::uv_timer_t) {
295+
let p: &mut Process = unsafe {
296+
&mut *(uvll::get_data_for_uv_handle(timer) as *mut Process)
297+
};
298+
p.timeout_state = TimeoutElapsed;
299+
match p.to_wake.take() {
300+
Some(task) => { let _t = task.wake().map(|t| t.reawaken()); }
301+
None => {}
302+
}
303+
}
241304
}
242305
}
243306

‎src/libstd/io/process.rs

Lines changed: 131 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
//! Bindings for executing child processes
1212
13+
#![allow(experimental)]
14+
1315
use prelude::*;
1416

1517
use fmt;
@@ -50,7 +52,7 @@ use rt::rtio::{RtioProcess, IoFactory, LocalIo};
5052
/// };
5153
///
5254
/// let contents = child.stdout.get_mut_ref().read_to_end();
53-
/// assert!(child.wait().success());
55+
/// assert!(child.wait().unwrap().success());
5456
/// ```
5557
pub struct Process {
5658
handle: Box<RtioProcess:Send>,
@@ -284,7 +286,7 @@ impl Process {
284286
/// println!("stderr: {}", str::from_utf8_lossy(output.error.as_slice()));
285287
/// ```
286288
pub fn output(prog: &str, args: &[~str]) -> IoResult<ProcessOutput> {
287-
Process::new(prog, args).map(|mut p| p.wait_with_output())
289+
Process::new(prog, args).and_then(|p| p.wait_with_output())
288290
}
289291

290292
/// Executes a child process and collects its exit status. This will block
@@ -303,7 +305,7 @@ impl Process {
303305
/// println!("process exited with: {}", status);
304306
/// ```
305307
pub fn status(prog: &str, args: &[~str]) -> IoResult<ProcessExit> {
306-
Process::new(prog, args).map(|mut p| p.wait())
308+
Process::new(prog, args).and_then(|mut p| p.wait())
307309
}
308310

309311
/// Creates a new process with the specified configuration.
@@ -378,17 +380,72 @@ impl Process {
378380
/// after it has been called at least once.
379381
///
380382
/// The stdin handle to the child process will be closed before waiting.
381-
pub fn wait(&mut self) -> ProcessExit {
383+
///
384+
/// # Errors
385+
///
386+
/// This function can fail if a timeout was previously specified via
387+
/// `set_timeout` and the timeout expires before the child exits.
388+
pub fn wait(&mut self) -> IoResult<ProcessExit> {
382389
drop(self.stdin.take());
383390
self.handle.wait()
384391
}
385392

393+
/// Sets a timeout, in milliseconds, for future calls to wait().
394+
///
395+
/// The argument specified is a relative distance into the future, in
396+
/// milliseconds, after which any call to wait() will return immediately
397+
/// with a timeout error, and all future calls to wait() will not block.
398+
///
399+
/// A value of `None` will clear any previous timeout, and a value of `Some`
400+
/// will override any previously set timeout.
401+
///
402+
/// # Example
403+
///
404+
/// ```no_run
405+
/// # #![allow(experimental)]
406+
/// use std::io::process::{Process, ProcessExit};
407+
/// use std::io::IoResult;
408+
///
409+
/// fn run_gracefully(prog: &str) -> IoResult<ProcessExit> {
410+
/// let mut p = try!(Process::new("long-running-process", []));
411+
///
412+
/// // give the process 10 seconds to finish completely
413+
/// p.set_timeout(Some(10_000));
414+
/// match p.wait() {
415+
/// Ok(status) => return Ok(status),
416+
/// Err(..) => {}
417+
/// }
418+
///
419+
/// // Attempt to exit gracefully, but don't wait for it too long
420+
/// try!(p.signal_exit());
421+
/// p.set_timeout(Some(1_000));
422+
/// match p.wait() {
423+
/// Ok(status) => return Ok(status),
424+
/// Err(..) => {}
425+
/// }
426+
///
427+
/// // Well, we did our best, forcefully kill the process
428+
/// try!(p.signal_kill());
429+
/// p.set_timeout(None);
430+
/// p.wait()
431+
/// }
432+
/// ```
433+
#[experimental = "the type of the timeout is likely to change"]
434+
pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
435+
self.handle.set_timeout(timeout_ms)
436+
}
437+
386438
/// Simultaneously wait for the child to exit and collect all remaining
387439
/// output on the stdout/stderr handles, returning a `ProcessOutput`
388440
/// instance.
389441
///
390442
/// The stdin handle to the child is closed before waiting.
391-
pub fn wait_with_output(&mut self) -> ProcessOutput {
443+
///
444+
/// # Errors
445+
///
446+
/// This function can fail for any of the same reasons that `wait()` can
447+
/// fail.
448+
pub fn wait_with_output(mut self) -> IoResult<ProcessOutput> {
392449
drop(self.stdin.take());
393450
fn read(stream: Option<io::PipeStream>) -> Receiver<IoResult<Vec<u8>>> {
394451
let (tx, rx) = channel();
@@ -404,11 +461,13 @@ impl Process {
404461
let stdout = read(self.stdout.take());
405462
let stderr = read(self.stderr.take());
406463

407-
let status = self.wait();
464+
let status = try!(self.wait());
408465

409-
ProcessOutput { status: status,
410-
output: stdout.recv().ok().unwrap_or(Vec::new()),
411-
error: stderr.recv().ok().unwrap_or(Vec::new()) }
466+
Ok(ProcessOutput {
467+
status: status,
468+
output: stdout.recv().ok().unwrap_or(Vec::new()),
469+
error: stderr.recv().ok().unwrap_or(Vec::new()),
470+
})
412471
}
413472
}
414473

@@ -421,7 +480,8 @@ impl Drop for Process {
421480
drop(self.stderr.take());
422481
drop(mem::replace(&mut self.extra_io, Vec::new()));
423482

424-
self.wait();
483+
self.set_timeout(None);
484+
let _ = self.wait().unwrap();
425485
}
426486
}
427487

@@ -441,7 +501,7 @@ mod tests {
441501
let p = Process::configure(args);
442502
assert!(p.is_ok());
443503
let mut p = p.unwrap();
444-
assert!(p.wait().success());
504+
assert!(p.wait().unwrap().success());
445505
})
446506

447507
#[cfg(not(target_os="android"))]
@@ -465,7 +525,7 @@ mod tests {
465525
let p = Process::configure(args);
466526
assert!(p.is_ok());
467527
let mut p = p.unwrap();
468-
assert!(p.wait().matches_exit_status(1));
528+
assert!(p.wait().unwrap().matches_exit_status(1));
469529
drop(p.wait().clone());
470530
})
471531

@@ -479,7 +539,7 @@ mod tests {
479539
let p = Process::configure(args);
480540
assert!(p.is_ok());
481541
let mut p = p.unwrap();
482-
match p.wait() {
542+
match p.wait().unwrap() {
483543
process::ExitSignal(1) => {},
484544
result => fail!("not terminated by signal 1 (instead, {})", result),
485545
}
@@ -495,7 +555,7 @@ mod tests {
495555
let mut p = p.unwrap();
496556
assert!(p.stdout.is_some());
497557
let ret = read_all(p.stdout.get_mut_ref() as &mut Reader);
498-
assert!(p.wait().success());
558+
assert!(p.wait().unwrap().success());
499559
return ret;
500560
}
501561

@@ -536,7 +596,7 @@ mod tests {
536596
p.stdin.get_mut_ref().write("foobar".as_bytes()).unwrap();
537597
drop(p.stdin.take());
538598
let out = read_all(p.stdout.get_mut_ref() as &mut Reader);
539-
assert!(p.wait().success());
599+
assert!(p.wait().unwrap().success());
540600
assert_eq!(out, "foobar\n".to_owned());
541601
})
542602

@@ -548,7 +608,7 @@ mod tests {
548608
.. ProcessConfig::new()
549609
};
550610
let mut p = Process::configure(args).unwrap();
551-
assert!(p.wait().success());
611+
assert!(p.wait().unwrap().success());
552612
})
553613

554614
#[cfg(windows)]
@@ -572,7 +632,7 @@ mod tests {
572632
.. ProcessConfig::new()
573633
};
574634
let mut p = Process::configure(args).unwrap();
575-
assert!(p.wait().success());
635+
assert!(p.wait().unwrap().success());
576636
})
577637

578638
#[cfg(unix, not(target_os="android"))]
@@ -635,21 +695,21 @@ mod tests {
635695
#[cfg(not(target_os="android"))]
636696
iotest!(fn test_finish_once() {
637697
let mut prog = Process::new("false", []).unwrap();
638-
assert!(prog.wait().matches_exit_status(1));
698+
assert!(prog.wait().unwrap().matches_exit_status(1));
639699
})
640700

641701
#[cfg(not(target_os="android"))]
642702
iotest!(fn test_finish_twice() {
643703
let mut prog = Process::new("false", []).unwrap();
644-
assert!(prog.wait().matches_exit_status(1));
645-
assert!(prog.wait().matches_exit_status(1));
704+
assert!(prog.wait().unwrap().matches_exit_status(1));
705+
assert!(prog.wait().unwrap().matches_exit_status(1));
646706
})
647707

648708
#[cfg(not(target_os="android"))]
649709
iotest!(fn test_wait_with_output_once() {
650710

651-
let mut prog = Process::new("echo", ["hello".to_owned()]).unwrap();
652-
let ProcessOutput {status, output, error} = prog.wait_with_output();
711+
let prog = Process::new("echo", ["hello".to_owned()]).unwrap();
712+
let ProcessOutput {status, output, error} = prog.wait_with_output().unwrap();
653713
let output_str = str::from_utf8(output.as_slice()).unwrap();
654714

655715
assert!(status.success());
@@ -660,30 +720,6 @@ mod tests {
660720
}
661721
})
662722

663-
#[cfg(not(target_os="android"))]
664-
iotest!(fn test_wait_with_output_twice() {
665-
let mut prog = Process::new("echo", ["hello".to_owned()]).unwrap();
666-
let ProcessOutput {status, output, error} = prog.wait_with_output();
667-
668-
let output_str = str::from_utf8(output.as_slice()).unwrap();
669-
670-
assert!(status.success());
671-
assert_eq!(output_str.trim().to_owned(), "hello".to_owned());
672-
// FIXME #7224
673-
if !running_on_valgrind() {
674-
assert_eq!(error, Vec::new());
675-
}
676-
677-
let ProcessOutput {status, output, error} = prog.wait_with_output();
678-
679-
assert!(status.success());
680-
assert_eq!(output, Vec::new());
681-
// FIXME #7224
682-
if !running_on_valgrind() {
683-
assert_eq!(error, Vec::new());
684-
}
685-
})
686-
687723
#[cfg(unix,not(target_os="android"))]
688724
pub fn run_pwd(dir: Option<&Path>) -> Process {
689725
Process::configure(ProcessConfig {
@@ -714,9 +750,10 @@ mod tests {
714750

715751
iotest!(fn test_keep_current_working_dir() {
716752
use os;
717-
let mut prog = run_pwd(None);
753+
let prog = run_pwd(None);
718754

719-
let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned();
755+
let output = str::from_utf8(prog.wait_with_output().unwrap()
756+
.output.as_slice()).unwrap().to_owned();
720757
let parent_dir = os::getcwd();
721758
let child_dir = Path::new(output.trim());
722759

@@ -732,9 +769,10 @@ mod tests {
732769
// test changing to the parent of os::getcwd() because we know
733770
// the path exists (and os::getcwd() is not expected to be root)
734771
let parent_dir = os::getcwd().dir_path();
735-
let mut prog = run_pwd(Some(&parent_dir));
772+
let prog = run_pwd(Some(&parent_dir));
736773

737-
let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned();
774+
let output = str::from_utf8(prog.wait_with_output().unwrap()
775+
.output.as_slice()).unwrap().to_owned();
738776
let child_dir = Path::new(output.trim());
739777

740778
let parent_stat = parent_dir.stat().unwrap();
@@ -777,8 +815,9 @@ mod tests {
777815
use os;
778816
if running_on_valgrind() { return; }
779817

780-
let mut prog = run_env(None);
781-
let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned();
818+
let prog = run_env(None);
819+
let output = str::from_utf8(prog.wait_with_output().unwrap()
820+
.output.as_slice()).unwrap().to_owned();
782821

783822
let r = os::env();
784823
for &(ref k, ref v) in r.iter() {
@@ -791,8 +830,10 @@ mod tests {
791830
use os;
792831
if running_on_valgrind() { return; }
793832

794-
let mut prog = run_env(None);
795-
let output = str::from_utf8(prog.wait_with_output().output.as_slice()).unwrap().to_owned();
833+
let prog = run_env(None);
834+
let output = str::from_utf8(prog.wait_with_output()
835+
.unwrap().output.as_slice())
836+
.unwrap().to_owned();
796837

797838
let r = os::env();
798839
for &(ref k, ref v) in r.iter() {
@@ -807,8 +848,8 @@ mod tests {
807848
iotest!(fn test_add_to_env() {
808849
let new_env = box [("RUN_TEST_NEW_ENV".to_owned(), "123".to_owned())];
809850

810-
let mut prog = run_env(Some(new_env));
811-
let result = prog.wait_with_output();
851+
let prog = run_env(Some(new_env));
852+
let result = prog.wait_with_output().unwrap();
812853
let output = str::from_utf8_lossy(result.output.as_slice()).into_owned();
813854

814855
assert!(output.contains("RUN_TEST_NEW_ENV=123"),
@@ -830,26 +871,57 @@ mod tests {
830871
iotest!(fn test_kill() {
831872
let mut p = sleeper();
832873
Process::kill(p.id(), PleaseExitSignal).unwrap();
833-
assert!(!p.wait().success());
874+
assert!(!p.wait().unwrap().success());
834875
})
835876

836877
iotest!(fn test_exists() {
837878
let mut p = sleeper();
838879
assert!(Process::kill(p.id(), 0).is_ok());
839880
p.signal_kill().unwrap();
840-
assert!(!p.wait().success());
881+
assert!(!p.wait().unwrap().success());
841882
})
842883

843884
iotest!(fn test_zero() {
844885
let mut p = sleeper();
845886
p.signal_kill().unwrap();
846887
for _ in range(0, 20) {
847888
if p.signal(0).is_err() {
848-
assert!(!p.wait().success());
889+
assert!(!p.wait().unwrap().success());
849890
return
850891
}
851892
timer::sleep(100);
852893
}
853894
fail!("never saw the child go away");
854895
})
896+
897+
iotest!(fn wait_timeout() {
898+
let mut p = sleeper();
899+
p.set_timeout(Some(10));
900+
assert_eq!(p.wait().err().unwrap().kind, TimedOut);
901+
assert_eq!(p.wait().err().unwrap().kind, TimedOut);
902+
p.signal_kill().unwrap();
903+
p.set_timeout(None);
904+
assert!(p.wait().is_ok());
905+
})
906+
907+
iotest!(fn wait_timeout2() {
908+
let (tx, rx) = channel();
909+
let tx2 = tx.clone();
910+
spawn(proc() {
911+
let mut p = sleeper();
912+
p.set_timeout(Some(10));
913+
assert_eq!(p.wait().err().unwrap().kind, TimedOut);
914+
p.signal_kill().unwrap();
915+
tx.send(());
916+
});
917+
spawn(proc() {
918+
let mut p = sleeper();
919+
p.set_timeout(Some(10));
920+
assert_eq!(p.wait().err().unwrap().kind, TimedOut);
921+
p.signal_kill().unwrap();
922+
tx2.send(());
923+
});
924+
rx.recv();
925+
rx.recv();
926+
})
855927
}

‎src/libstd/rt/rtio.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,8 @@ pub trait RtioFileStream {
275275
pub trait RtioProcess {
276276
fn id(&self) -> libc::pid_t;
277277
fn kill(&mut self, signal: int) -> IoResult<()>;
278-
fn wait(&mut self) -> ProcessExit;
278+
fn wait(&mut self) -> IoResult<ProcessExit>;
279+
fn set_timeout(&mut self, timeout: Option<u64>);
279280
}
280281

281282
pub trait RtioPipe {

‎src/test/run-pass/backtrace.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ fn runtest(me: &str) {
5050
env: Some(env.as_slice()),
5151
.. ProcessConfig::new()
5252
}).unwrap();
53-
let out = p.wait_with_output();
53+
let out = p.wait_with_output().unwrap();
5454
assert!(!out.status.success());
5555
let s = str::from_utf8(out.error.as_slice()).unwrap();
5656
assert!(s.contains("stack backtrace") && s.contains("foo::h"),
@@ -62,7 +62,7 @@ fn runtest(me: &str) {
6262
args: ["fail".to_owned()],
6363
.. ProcessConfig::new()
6464
}).unwrap();
65-
let out = p.wait_with_output();
65+
let out = p.wait_with_output().unwrap();
6666
assert!(!out.status.success());
6767
let s = str::from_utf8(out.error.as_slice()).unwrap();
6868
assert!(!s.contains("stack backtrace") && !s.contains("foo::h"),
@@ -74,7 +74,7 @@ fn runtest(me: &str) {
7474
args: ["double-fail".to_owned()],
7575
.. ProcessConfig::new()
7676
}).unwrap();
77-
let out = p.wait_with_output();
77+
let out = p.wait_with_output().unwrap();
7878
assert!(!out.status.success());
7979
let s = str::from_utf8(out.error.as_slice()).unwrap();
8080
assert!(s.contains("stack backtrace") && s.contains("double::h"),
@@ -87,7 +87,7 @@ fn runtest(me: &str) {
8787
env: Some(env.as_slice()),
8888
.. ProcessConfig::new()
8989
}).unwrap();
90-
let out = p.wait_with_output();
90+
let out = p.wait_with_output().unwrap();
9191
assert!(!out.status.success());
9292
let s = str::from_utf8(out.error.as_slice()).unwrap();
9393
let mut i = 0;

‎src/test/run-pass/core-run-destroy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ pub fn test_destroy_actually_kills(force: bool) {
120120
() = rx1.recv() => {}
121121
}
122122
});
123-
match p.wait() {
123+
match p.wait().unwrap() {
124124
ExitStatus(..) => fail!("expected a signal"),
125125
ExitSignal(..) => tx.send(()),
126126
}

‎src/test/run-pass/issue-13304.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ fn parent(flavor: ~str) {
5252
let args = args.as_slice();
5353
let mut p = io::Process::new(args[0].as_slice(), ["child".to_owned(), flavor]).unwrap();
5454
p.stdin.get_mut_ref().write_str("test1\ntest2\ntest3").unwrap();
55-
let out = p.wait_with_output();
55+
let out = p.wait_with_output().unwrap();
5656
assert!(out.status.success());
5757
let s = str::from_utf8(out.output.as_slice()).unwrap();
5858
assert_eq!(s, "test1\n\ntest2\n\ntest3\n");

‎src/test/run-pass/logging-separate-lines.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ fn main() {
3636
env: Some(env.as_slice()),
3737
..ProcessConfig::new()
3838
};
39-
let p = Process::configure(config).unwrap().wait_with_output();
39+
let p = Process::configure(config).unwrap().wait_with_output().unwrap();
4040
assert!(p.status.success());
4141
let mut lines = str::from_utf8(p.error.as_slice()).unwrap().lines();
4242
assert!(lines.next().unwrap().contains("foo"));

‎src/test/run-pass/process-detach.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ fn main() {
5454
// Wait for the child process to die (terminate it's stdin and the read
5555
// should fail).
5656
drop(p.stdin.take());
57-
match p.wait() {
57+
match p.wait().unwrap() {
5858
process::ExitStatus(..) => {}
5959
process::ExitSignal(..) => fail!()
6060
}

‎src/test/run-pass/process-spawn-with-unicode-params.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ fn main() {
6262
cwd: Some(&cwd),
6363
env: Some(my_env.append_one(env).as_slice()),
6464
.. ProcessConfig::new()
65-
}).unwrap().wait_with_output();
65+
}).unwrap().wait_with_output().unwrap();
6666

6767
// display the output
6868
assert!(io::stdout().write(p.output.as_slice()).is_ok());

‎src/test/run-pass/sigpipe-should-be-ignored.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,5 @@ fn main() {
3131
}
3232

3333
let mut p = Process::new(args[0], ["test".to_owned()]).unwrap();
34-
assert!(p.wait().success());
34+
assert!(p.wait().unwrap().success());
3535
}

9 commit comments

Comments
 (9)

bors commented on May 14, 2014

@bors
Collaborator

saw approval from alexcrichton
at alexcrichton@f09592a

bors commented on May 14, 2014

@bors
Collaborator

merging alexcrichton/rust/rollup = f09592a into auto

bors commented on May 14, 2014

@bors
Collaborator

alexcrichton/rust/rollup = f09592a merged ok, testing candidate = f87ff617

bors commented on May 14, 2014

@bors
Collaborator

saw approval from alexcrichton
at alexcrichton@f09592a

bors commented on May 14, 2014

@bors
Collaborator

merging alexcrichton/rust/rollup = f09592a into auto

bors commented on May 14, 2014

@bors
Collaborator

alexcrichton/rust/rollup = f09592a merged ok, testing candidate = b2b383c

bors commented on May 14, 2014

@bors
Collaborator

fast-forwarding master to auto = b2b383c

Please sign in to comment.