Skip to content

Commit 3951e34

Browse files
committed
Keep file descriptors open by default
1 parent d357534 commit 3951e34

File tree

7 files changed

+58
-128
lines changed

7 files changed

+58
-128
lines changed

src/lib.rs

+15-47
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
//!
5353
//! let client = Client::new(4).expect("failed to create jobserver");
5454
//! let mut cmd = Command::new("make");
55-
//! client.configure(&mut cmd);
55+
//! // client.configure(&mut cmd);
5656
//! ```
5757
//!
5858
//! ## Caveats
@@ -188,10 +188,9 @@ impl Client {
188188
/// allow at most `limit` tokens to be acquired from it in parallel. More
189189
/// calls to `acquire` will cause the calling thread to block.
190190
///
191-
/// Note that the created `Client` is not automatically inherited into
192-
/// spawned child processes from this program. Manual usage of the
193-
/// `configure` function is required for a child process to have access to a
194-
/// job server.
191+
/// Note that the created `Client` is automatically inherited into
192+
/// spawned child processes from this program. `disable_inheritance`
193+
/// function can be used to change behavior.
195194
///
196195
/// # Examples
197196
///
@@ -219,10 +218,8 @@ impl Client {
219218
/// it's passing down. This function will attempt to look for these details
220219
/// and connect to the jobserver.
221220
///
222-
/// Note that the created `Client` is not automatically inherited into
223-
/// spawned child processes from this program. Manual usage of the
224-
/// `configure` function is required for a child process to have access to a
225-
/// job server.
221+
/// Note that the created `Client` is automatically inherited into
222+
/// spawned child processes from this program.
226223
///
227224
/// # Return value
228225
///
@@ -238,9 +235,9 @@ impl Client {
238235
/// descriptors for this process and will close the file descriptors after
239236
/// this value is dropped.
240237
///
241-
/// Additionally on Unix this function will configure the file descriptors
242-
/// with `CLOEXEC` so they're not automatically inherited by spawned
243-
/// children.
238+
/// // Additionally on Unix this function will configure the file descriptors
239+
/// // with `CLOEXEC` false to ensure they're automatically inherited by spawned
240+
/// // children.
244241
///
245242
/// On unix if `check_pipe` enabled this function will check if provided
246243
/// files are actually pipes.
@@ -357,42 +354,13 @@ impl Client {
357354
/// two file descriptors for this client to be inherited to the child.
358355
///
359356
/// On platforms other than Unix and Windows this panics.
360-
pub fn configure(&self, cmd: &mut Command) {
361-
cmd.env("CARGO_MAKEFLAGS", &self.mflags_env());
362-
self.inner.configure(cmd);
363-
}
364-
365-
/// Configures a child process to have access to this client's jobserver as
366-
/// well.
367-
///
368-
/// This function is required to be called to ensure that a jobserver is
369-
/// properly inherited to a child process. If this function is *not* called
370-
/// then this `Client` will not be accessible in the child process. In other
371-
/// words, if not called, then `Client::from_env` will return `None` in the
372-
/// child process (or the equivalent of `Child::from_env` that `make` uses).
373-
///
374-
/// ## Platform-specific behavior
357+
pub fn _configure(&self, _cmd: &mut Command) {}
375358
///
376-
/// On Unix and Windows this will clobber the `CARGO_MAKEFLAGS`,
377-
/// `MAKEFLAGS` and `MFLAGS` environment variables for the child process,
378-
/// and on Unix this will also allow the two file descriptors for
379-
/// this client to be inherited to the child.
380-
///
381-
/// On platforms other than Unix and Windows this panics.
382-
pub fn configure_make(&self, cmd: &mut Command) {
383-
let value = self.mflags_env();
384-
cmd.env("CARGO_MAKEFLAGS", &value);
385-
cmd.env("MAKEFLAGS", &value);
386-
cmd.env("MFLAGS", &value);
387-
self.inner.configure(cmd);
388-
}
389-
390-
fn mflags_env(&self) -> String {
391-
let arg = self.inner.string_arg();
392-
// Older implementations of make use `--jobserver-fds` and newer
393-
// implementations use `--jobserver-auth`, pass both to try to catch
394-
// both implementations.
395-
format!("-j --jobserver-fds={0} --jobserver-auth={0}", arg)
359+
pub fn disable_inheritance(&self, cmd: &mut Command) {
360+
cmd.env_remove("CARGO_MAKEFLAGS");
361+
cmd.env_remove("MAKEFLAGS");
362+
cmd.env_remove("MFLAGS");
363+
self.inner.disable_inheritance(cmd);
396364
}
397365

398366
/// Converts this `Client` into a helper thread to deal with a blocking

src/unix.rs

+29-57
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ pub struct Acquired {
2828

2929
impl Client {
3030
pub fn new(mut limit: usize) -> io::Result<Client> {
31-
let client = unsafe { Client::mk()? };
31+
let (client, string_arg) = unsafe { Client::mk()? };
3232

3333
// I don't think the character written here matters, but I could be
3434
// wrong!
@@ -47,39 +47,21 @@ impl Client {
4747

4848
set_nonblocking(write.as_raw_fd(), false)?;
4949

50+
// TODO: what if this var has already been set?
51+
std::env::set_var("CARGO_MAKEFLAGS", string_arg);
52+
5053
Ok(client)
5154
}
5255

53-
unsafe fn mk() -> io::Result<Client> {
56+
unsafe fn mk() -> io::Result<(Client, String)> {
5457
let mut pipes = [0; 2];
55-
56-
// Attempt atomically-create-with-cloexec if we can on Linux,
57-
// detected by using the `syscall` function in `libc` to try to work
58-
// with as many kernels/glibc implementations as possible.
59-
#[cfg(target_os = "linux")]
60-
{
61-
use std::sync::atomic::{AtomicBool, Ordering};
62-
63-
static PIPE2_AVAILABLE: AtomicBool = AtomicBool::new(true);
64-
if PIPE2_AVAILABLE.load(Ordering::SeqCst) {
65-
match libc::syscall(libc::SYS_pipe2, pipes.as_mut_ptr(), libc::O_CLOEXEC) {
66-
-1 => {
67-
let err = io::Error::last_os_error();
68-
if err.raw_os_error() == Some(libc::ENOSYS) {
69-
PIPE2_AVAILABLE.store(false, Ordering::SeqCst);
70-
} else {
71-
return Err(err);
72-
}
73-
}
74-
_ => return Ok(Client::from_fds(pipes[0], pipes[1])),
75-
}
76-
}
77-
}
78-
7958
cvt(libc::pipe(pipes.as_mut_ptr()))?;
80-
drop(set_cloexec(pipes[0], true));
81-
drop(set_cloexec(pipes[1], true));
82-
Ok(Client::from_fds(pipes[0], pipes[1]))
59+
let string_arg = format!(
60+
"--jobserver-fds={},{}",
61+
pipes[0].as_raw_fd(),
62+
pipes[1].as_raw_fd()
63+
);
64+
Ok((Client::from_fds(pipes[0], pipes[1]), string_arg))
8365
}
8466

8567
pub(crate) unsafe fn open(s: &str, check_pipe: bool) -> Result<Client, FromEnvErrorInner> {
@@ -149,8 +131,10 @@ impl Client {
149131
}
150132
}
151133

152-
drop(set_cloexec(read, true));
153-
drop(set_cloexec(write, true));
134+
// // Unless env var was explicitly removed, file descriptors should be passed.
135+
// drop(set_cloexec(read, false));
136+
// drop(set_cloexec(write, false));
137+
154138
Ok(Some(Client::from_fds(read, write)))
155139
}
156140

@@ -263,38 +247,26 @@ impl Client {
263247
}
264248
}
265249

266-
pub fn string_arg(&self) -> String {
267-
match self {
268-
Client::Pipe { read, write } => format!("{},{}", read.as_raw_fd(), write.as_raw_fd()),
269-
Client::Fifo { path, .. } => format!("fifo:{}", path.to_str().unwrap()),
270-
}
271-
}
272-
273250
pub fn available(&self) -> io::Result<usize> {
274251
let mut len = MaybeUninit::<c_int>::uninit();
275252
cvt(unsafe { libc::ioctl(self.read().as_raw_fd(), libc::FIONREAD, len.as_mut_ptr()) })?;
276253
Ok(unsafe { len.assume_init() } as usize)
277254
}
278255

279-
pub fn configure(&self, cmd: &mut Command) {
280-
match self {
281-
// We `File::open`ed it when inheriting from environment,
282-
// so no need to set cloexec for fifo.
283-
Client::Fifo { .. } => return,
284-
Client::Pipe { .. } => {}
285-
};
286-
// Here we basically just want to say that in the child process
287-
// we'll configure the read/write file descriptors to *not* be
288-
// cloexec, so they're inherited across the exec and specified as
289-
// integers through `string_arg` above.
290-
let read = self.read().as_raw_fd();
291-
let write = self.write().as_raw_fd();
292-
unsafe {
293-
cmd.pre_exec(move || {
294-
set_cloexec(read, false)?;
295-
set_cloexec(write, false)?;
296-
Ok(())
297-
});
256+
pub fn disable_inheritance(&self, cmd: &mut Command) {
257+
if let Client::Pipe { .. } = self {
258+
// Here we basically just want to say that in the child process
259+
// we'll configure the read/write file descriptors to be cloexec,
260+
// so they aren't inherited across the exec.
261+
let read = self.read().as_raw_fd();
262+
let write = self.write().as_raw_fd();
263+
unsafe {
264+
cmd.pre_exec(move || {
265+
set_cloexec(read, true)?;
266+
set_cloexec(write, true)?;
267+
Ok(())
268+
});
269+
}
298270
}
299271
}
300272
}

src/wasm.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -53,19 +53,12 @@ impl Client {
5353
Ok(())
5454
}
5555

56-
pub fn string_arg(&self) -> String {
57-
panic!(
58-
"On this platform there is no cross process jobserver support,
59-
so Client::configure is not supported."
60-
);
61-
}
62-
6356
pub fn available(&self) -> io::Result<usize> {
6457
let lock = self.inner.count.lock().unwrap_or_else(|e| e.into_inner());
6558
Ok(*lock)
6659
}
6760

68-
pub fn configure(&self, _cmd: &mut Command) {
61+
pub fn disable_inheritance(&self, _cmd: &mut Command) {
6962
unreachable!();
7063
}
7164
}

src/windows.rs

+5-9
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ impl Client {
9696
unsafe {
9797
let create_limit = if limit == 0 { 1 } else { limit };
9898
let r = CreateSemaphoreA(
99-
ptr::null_mut(),
99+
ptr::null_mut(), // SECURITY_ATTRIBUTES::bInheritHandle.
100100
create_limit as LONG,
101101
create_limit as LONG,
102102
name.as_ptr() as *const _,
@@ -118,6 +118,9 @@ impl Client {
118118
if create_limit != limit {
119119
client.acquire()?;
120120
}
121+
122+
let string_arg = format!("--jobserver-auth={}", client.name);
123+
std::env::set_var("CARGO_MAKEFLAGS", string_arg);
121124
return Ok(client);
122125
}
123126
}
@@ -170,10 +173,6 @@ impl Client {
170173
}
171174
}
172175

173-
pub fn string_arg(&self) -> String {
174-
self.name.clone()
175-
}
176-
177176
pub fn available(&self) -> io::Result<usize> {
178177
// Can't read value of a semaphore on Windows, so
179178
// try to acquire without sleeping, since we can find out the
@@ -194,10 +193,7 @@ impl Client {
194193
}
195194
}
196195

197-
pub fn configure(&self, _cmd: &mut Command) {
198-
// nothing to do here, we gave the name of our semaphore to the
199-
// child above
200-
}
196+
pub fn disable_inheritance(&self, _cmd: &mut Command) {}
201197
}
202198

203199
#[derive(Debug)]

tests/client-of-myself.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ fn server() {
2929
let client = t!(Client::new(1));
3030
let mut cmd = Command::new(me);
3131
cmd.env("I_AM_THE_CLIENT", "1").stdout(Stdio::piped());
32-
client.configure(&mut cmd);
32+
// client.configure(&mut cmd);
3333
let acq = client.acquire().unwrap();
3434
let mut child = t!(cmd.spawn());
3535
let stdout = child.stdout.take().unwrap();
@@ -52,7 +52,8 @@ fn server() {
5252
}
5353

5454
fn client() {
55-
let client = unsafe { Client::from_env().unwrap() };
55+
let client = unsafe { Client::from_env_ext(true) }.client.unwrap();
56+
// let client = unsafe { Client::from_env().unwrap() };
5657
let acq = client.acquire().unwrap();
5758
println!("hello!");
5859
drop(acq);

tests/make-as-a-client.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ fn main() {
3535
return;
3636
}
3737

38-
let c = t!(Client::new(1));
38+
let _c = t!(Client::new(1));
3939
let td = tempfile::tempdir().unwrap();
4040

4141
let prog = env::var("MAKE").unwrap_or_else(|_| "make".to_string());
@@ -64,7 +64,7 @@ bar:
6464

6565
// We're leaking one extra token to `make` sort of violating the makefile
6666
// jobserver protocol. It has the desired effect though.
67-
c.configure(&mut cmd);
67+
// c.configure(&mut cmd);
6868

6969
let listener = t!(TcpListener::bind("127.0.0.1:0"));
7070
let addr = t!(listener.local_addr());

tests/server.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ bar:
9999
// token", so we acquire our one token to drain the jobserver, and this
100100
// should mean that `make` itself never has a second token available to it.
101101
let _a = c.acquire();
102-
c.configure(&mut cmd);
102+
// c.configure(&mut cmd);
103103
let output = t!(cmd.output());
104104
println!(
105105
"\n\t=== stderr\n\t\t{}",
@@ -132,7 +132,7 @@ foo
132132

133133
#[test]
134134
fn make_as_a_multi_thread_client() {
135-
let c = t!(Client::new(1));
135+
let _c = t!(Client::new(1));
136136
let td = tempfile::tempdir().unwrap();
137137

138138
let prog = env::var("MAKE").unwrap_or_else(|_| "make".to_string());
@@ -151,7 +151,7 @@ bar:
151151

152152
// We're leaking one extra token to `make` sort of violating the makefile
153153
// jobserver protocol. It has the desired effect though.
154-
c.configure(&mut cmd);
154+
// c.configure(&mut cmd);
155155
let output = t!(cmd.output());
156156
println!(
157157
"\n\t=== stderr\n\t\t{}",

0 commit comments

Comments
 (0)