Skip to content

Commit eaa6e07

Browse files
authored
Rollup merge of rust-lang#58059 - RalfJung:before_exec, r=alexcrichton
deprecate before_exec in favor of unsafe pre_exec Fixes rust-lang#39575 As per the [lang team decision](rust-lang#39575 (comment)): > The language team agreed that before_exec should be unsafe, and leaves the details of a transition plan to the libs team. Cc @alexcrichton @rust-lang/libs how would you like to proceed?
2 parents 9f7f3af + e023403 commit eaa6e07

File tree

6 files changed

+171
-95
lines changed

6 files changed

+171
-95
lines changed

src/libstd/sys/redox/ext/process.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub trait CommandExt {
3636
/// will be called and the spawn operation will immediately return with a
3737
/// failure.
3838
///
39-
/// # Notes
39+
/// # Notes and Safety
4040
///
4141
/// This closure will be run in the context of the child process after a
4242
/// `fork`. This primarily means that any modifications made to memory on
@@ -45,12 +45,32 @@ pub trait CommandExt {
4545
/// like `malloc` or acquiring a mutex are not guaranteed to work (due to
4646
/// other threads perhaps still running when the `fork` was run).
4747
///
48+
/// This also means that all resources such as file descriptors and
49+
/// memory-mapped regions got duplicated. It is your responsibility to make
50+
/// sure that the closure does not violate library invariants by making
51+
/// invalid use of these duplicates.
52+
///
4853
/// When this closure is run, aspects such as the stdio file descriptors and
4954
/// working directory have successfully been changed, so output to these
5055
/// locations may not appear where intended.
56+
#[stable(feature = "process_pre_exec", since = "1.34.0")]
57+
unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
58+
where F: FnMut() -> io::Result<()> + Send + Sync + 'static;
59+
60+
/// Schedules a closure to be run just before the `exec` function is
61+
/// invoked.
62+
///
63+
/// This method is stable and usable, but it should be unsafe. To fix
64+
/// that, it got deprecated in favor of the unsafe [`pre_exec`].
65+
///
66+
/// [`pre_exec`]: #tymethod.pre_exec
5167
#[stable(feature = "process_exec", since = "1.15.0")]
68+
#[rustc_deprecated(since = "1.37.0", reason = "should be unsafe, use `pre_exec` instead")]
5269
fn before_exec<F>(&mut self, f: F) -> &mut process::Command
53-
where F: FnMut() -> io::Result<()> + Send + Sync + 'static;
70+
where F: FnMut() -> io::Result<()> + Send + Sync + 'static
71+
{
72+
unsafe { self.pre_exec(f) }
73+
}
5474

5575
/// Performs all the required setup by this `Command`, followed by calling
5676
/// the `execvp` syscall.
@@ -87,10 +107,10 @@ impl CommandExt for process::Command {
87107
self
88108
}
89109

90-
fn before_exec<F>(&mut self, f: F) -> &mut process::Command
110+
unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
91111
where F: FnMut() -> io::Result<()> + Send + Sync + 'static
92112
{
93-
self.as_inner_mut().before_exec(Box::new(f));
113+
self.as_inner_mut().pre_exec(Box::new(f));
94114
self
95115
}
96116

src/libstd/sys/redox/process.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,10 @@ impl Command {
116116
self.gid = Some(id);
117117
}
118118

119-
pub fn before_exec(&mut self,
120-
f: Box<dyn FnMut() -> io::Result<()> + Send + Sync>) {
119+
pub unsafe fn pre_exec(
120+
&mut self,
121+
f: Box<dyn FnMut() -> io::Result<()> + Send + Sync>,
122+
) {
121123
self.closures.push(f);
122124
}
123125

src/libstd/sys/unix/ext/process.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub trait CommandExt {
3636
/// will be called and the spawn operation will immediately return with a
3737
/// failure.
3838
///
39-
/// # Notes
39+
/// # Notes and Safety
4040
///
4141
/// This closure will be run in the context of the child process after a
4242
/// `fork`. This primarily means that any modifications made to memory on
@@ -45,12 +45,32 @@ pub trait CommandExt {
4545
/// like `malloc` or acquiring a mutex are not guaranteed to work (due to
4646
/// other threads perhaps still running when the `fork` was run).
4747
///
48+
/// This also means that all resources such as file descriptors and
49+
/// memory-mapped regions got duplicated. It is your responsibility to make
50+
/// sure that the closure does not violate library invariants by making
51+
/// invalid use of these duplicates.
52+
///
4853
/// When this closure is run, aspects such as the stdio file descriptors and
4954
/// working directory have successfully been changed, so output to these
5055
/// locations may not appear where intended.
56+
#[stable(feature = "process_pre_exec", since = "1.34.0")]
57+
unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
58+
where F: FnMut() -> io::Result<()> + Send + Sync + 'static;
59+
60+
/// Schedules a closure to be run just before the `exec` function is
61+
/// invoked.
62+
///
63+
/// This method is stable and usable, but it should be unsafe. To fix
64+
/// that, it got deprecated in favor of the unsafe [`pre_exec`].
65+
///
66+
/// [`pre_exec`]: #tymethod.pre_exec
5167
#[stable(feature = "process_exec", since = "1.15.0")]
68+
#[rustc_deprecated(since = "1.37.0", reason = "should be unsafe, use `pre_exec` instead")]
5269
fn before_exec<F>(&mut self, f: F) -> &mut process::Command
53-
where F: FnMut() -> io::Result<()> + Send + Sync + 'static;
70+
where F: FnMut() -> io::Result<()> + Send + Sync + 'static
71+
{
72+
unsafe { self.pre_exec(f) }
73+
}
5474

5575
/// Performs all the required setup by this `Command`, followed by calling
5676
/// the `execvp` syscall.
@@ -97,10 +117,10 @@ impl CommandExt for process::Command {
97117
self
98118
}
99119

100-
fn before_exec<F>(&mut self, f: F) -> &mut process::Command
120+
unsafe fn pre_exec<F>(&mut self, f: F) -> &mut process::Command
101121
where F: FnMut() -> io::Result<()> + Send + Sync + 'static
102122
{
103-
self.as_inner_mut().before_exec(Box::new(f));
123+
self.as_inner_mut().pre_exec(Box::new(f));
104124
self
105125
}
106126

src/libstd/sys/unix/process/process_common.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,10 @@ impl Command {
149149
&mut self.closures
150150
}
151151

152-
pub fn before_exec(&mut self,
153-
f: Box<dyn FnMut() -> io::Result<()> + Send + Sync>) {
152+
pub unsafe fn pre_exec(
153+
&mut self,
154+
f: Box<dyn FnMut() -> io::Result<()> + Send + Sync>,
155+
) {
154156
self.closures.push(f);
155157
}
156158

src/test/run-pass/command-before-exec.rs

-83
This file was deleted.

src/test/run-pass/command-pre-exec.rs

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#![allow(stable_features)]
2+
// ignore-windows - this is a unix-specific test
3+
// ignore-cloudabi no processes
4+
// ignore-emscripten no processes
5+
#![feature(process_exec, rustc_private)]
6+
7+
extern crate libc;
8+
9+
use std::env;
10+
use std::io::Error;
11+
use std::os::unix::process::CommandExt;
12+
use std::process::Command;
13+
use std::sync::atomic::{AtomicUsize, Ordering};
14+
use std::sync::Arc;
15+
16+
fn main() {
17+
if let Some(arg) = env::args().nth(1) {
18+
match &arg[..] {
19+
"test1" => println!("hello2"),
20+
"test2" => assert_eq!(env::var("FOO").unwrap(), "BAR"),
21+
"test3" => assert_eq!(env::current_dir().unwrap().to_str().unwrap(), "/"),
22+
"empty" => {}
23+
_ => panic!("unknown argument: {}", arg),
24+
}
25+
return;
26+
}
27+
28+
let me = env::current_exe().unwrap();
29+
30+
let output = unsafe {
31+
Command::new(&me)
32+
.arg("test1")
33+
.pre_exec(|| {
34+
println!("hello");
35+
Ok(())
36+
})
37+
.output()
38+
.unwrap()
39+
};
40+
assert!(output.status.success());
41+
assert!(output.stderr.is_empty());
42+
assert_eq!(output.stdout, b"hello\nhello2\n");
43+
44+
let output = unsafe {
45+
Command::new(&me)
46+
.arg("test2")
47+
.pre_exec(|| {
48+
env::set_var("FOO", "BAR");
49+
Ok(())
50+
})
51+
.output()
52+
.unwrap()
53+
};
54+
assert!(output.status.success());
55+
assert!(output.stderr.is_empty());
56+
assert!(output.stdout.is_empty());
57+
58+
let output = unsafe {
59+
Command::new(&me)
60+
.arg("test3")
61+
.pre_exec(|| {
62+
env::set_current_dir("/").unwrap();
63+
Ok(())
64+
})
65+
.output()
66+
.unwrap()
67+
};
68+
assert!(output.status.success());
69+
assert!(output.stderr.is_empty());
70+
assert!(output.stdout.is_empty());
71+
72+
let output = unsafe {
73+
Command::new(&me)
74+
.arg("bad")
75+
.pre_exec(|| Err(Error::from_raw_os_error(102)))
76+
.output()
77+
.unwrap_err()
78+
};
79+
assert_eq!(output.raw_os_error(), Some(102));
80+
81+
let pid = unsafe { libc::getpid() };
82+
assert!(pid >= 0);
83+
let output = unsafe {
84+
Command::new(&me)
85+
.arg("empty")
86+
.pre_exec(move || {
87+
let child = libc::getpid();
88+
assert!(child >= 0);
89+
assert!(pid != child);
90+
Ok(())
91+
})
92+
.output()
93+
.unwrap()
94+
};
95+
assert!(output.status.success());
96+
assert!(output.stderr.is_empty());
97+
assert!(output.stdout.is_empty());
98+
99+
let mem = Arc::new(AtomicUsize::new(0));
100+
let mem2 = mem.clone();
101+
let output = unsafe {
102+
Command::new(&me)
103+
.arg("empty")
104+
.pre_exec(move || {
105+
assert_eq!(mem2.fetch_add(1, Ordering::SeqCst), 0);
106+
Ok(())
107+
})
108+
.output()
109+
.unwrap()
110+
};
111+
assert!(output.status.success());
112+
assert!(output.stderr.is_empty());
113+
assert!(output.stdout.is_empty());
114+
assert_eq!(mem.load(Ordering::SeqCst), 0);
115+
}

0 commit comments

Comments
 (0)