summaryrefslogtreecommitdiffstats
path: root/vendor/jobserver/src
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/jobserver/src')
-rw-r--r--vendor/jobserver/src/lib.rs54
-rw-r--r--vendor/jobserver/src/unix.rs122
-rw-r--r--vendor/jobserver/src/wasm.rs5
-rw-r--r--vendor/jobserver/src/windows.rs20
4 files changed, 181 insertions, 20 deletions
diff --git a/vendor/jobserver/src/lib.rs b/vendor/jobserver/src/lib.rs
index 72c02c120..cd0cdd749 100644
--- a/vendor/jobserver/src/lib.rs
+++ b/vendor/jobserver/src/lib.rs
@@ -11,7 +11,10 @@
//! The jobserver implementation can be found in [detail online][docs] but
//! basically boils down to a cross-process semaphore. On Unix this is
//! implemented with the `pipe` syscall and read/write ends of a pipe and on
-//! Windows this is implemented literally with IPC semaphores.
+//! Windows this is implemented literally with IPC semaphores. Starting from
+//! GNU `make` version 4.4, named pipe becomes the default way in communication
+//! on Unix. This crate also supports that feature in the sense of inheriting
+//! and forwarding the correct environment.
//!
//! The jobserver protocol in `make` also dictates when tokens are acquired to
//! run child work, and clients using this crate should take care to implement
@@ -208,7 +211,7 @@ impl Client {
/// with `CLOEXEC` so they're not automatically inherited by spawned
/// children.
///
- /// # Unsafety
+ /// # Safety
///
/// This function is `unsafe` to call on Unix specifically as it
/// transitively requires usage of the `from_raw_fd` function, which is
@@ -273,6 +276,19 @@ impl Client {
})
}
+ /// Returns amount of tokens in the read-side pipe.
+ ///
+ /// # Return value
+ ///
+ /// Number of bytes available to be read from the jobserver pipe
+ ///
+ /// # Errors
+ ///
+ /// Underlying errors from the ioctl will be passed up.
+ pub fn available(&self) -> io::Result<usize> {
+ self.inner.available()
+ }
+
/// Configures a child process to have access to this client's jobserver as
/// well.
///
@@ -290,13 +306,41 @@ impl Client {
///
/// On platforms other than Unix and Windows this panics.
pub fn configure(&self, cmd: &mut Command) {
+ cmd.env("CARGO_MAKEFLAGS", &self.mflags_env());
+ self.inner.configure(cmd);
+ }
+
+ /// Configures a child process to have access to this client's jobserver as
+ /// well.
+ ///
+ /// This function is required to be called to ensure that a jobserver is
+ /// properly inherited to a child process. If this function is *not* called
+ /// then this `Client` will not be accessible in the child process. In other
+ /// words, if not called, then `Client::from_env` will return `None` in the
+ /// child process (or the equivalent of `Child::from_env` that `make` uses).
+ ///
+ /// ## Platform-specific behavior
+ ///
+ /// On Unix and Windows this will clobber the `CARGO_MAKEFLAGS`,
+ /// `MAKEFLAGS` and `MFLAGS` environment variables for the child process,
+ /// and on Unix this will also allow the two file descriptors for
+ /// this client to be inherited to the child.
+ ///
+ /// On platforms other than Unix and Windows this panics.
+ pub fn configure_make(&self, cmd: &mut Command) {
+ let value = self.mflags_env();
+ cmd.env("CARGO_MAKEFLAGS", &value);
+ cmd.env("MAKEFLAGS", &value);
+ cmd.env("MFLAGS", &value);
+ self.inner.configure(cmd);
+ }
+
+ fn mflags_env(&self) -> String {
let arg = self.inner.string_arg();
// Older implementations of make use `--jobserver-fds` and newer
// implementations use `--jobserver-auth`, pass both to try to catch
// both implementations.
- let value = format!("-j --jobserver-fds={0} --jobserver-auth={0}", arg);
- cmd.env("CARGO_MAKEFLAGS", &value);
- self.inner.configure(cmd);
+ format!("-j --jobserver-fds={0} --jobserver-auth={0}", arg)
}
/// Converts this `Client` into a helper thread to deal with a blocking
diff --git a/vendor/jobserver/src/unix.rs b/vendor/jobserver/src/unix.rs
index d69ae88e3..e4b143505 100644
--- a/vendor/jobserver/src/unix.rs
+++ b/vendor/jobserver/src/unix.rs
@@ -1,8 +1,11 @@
use libc::c_int;
-use std::fs::File;
+
+use std::fs::{File, OpenOptions};
use std::io::{self, Read, Write};
use std::mem;
+use std::mem::MaybeUninit;
use std::os::unix::prelude::*;
+use std::path::{Path, PathBuf};
use std::process::Command;
use std::ptr;
use std::sync::{Arc, Once};
@@ -10,9 +13,11 @@ use std::thread::{self, Builder, JoinHandle};
use std::time::Duration;
#[derive(Debug)]
-pub struct Client {
- read: File,
- write: File,
+pub enum Client {
+ /// `--jobserver-auth=R,W`
+ Pipe { read: File, write: File },
+ /// `--jobserver-auth=fifo:PATH`
+ Fifo { file: File, path: PathBuf },
}
#[derive(Debug)]
@@ -21,13 +26,26 @@ pub struct Acquired {
}
impl Client {
- pub fn new(limit: usize) -> io::Result<Client> {
+ pub fn new(mut limit: usize) -> io::Result<Client> {
let client = unsafe { Client::mk()? };
+
// I don't think the character written here matters, but I could be
// wrong!
- for _ in 0..limit {
- (&client.write).write_all(&[b'|'])?;
+ const BUFFER: [u8; 128] = [b'|'; 128];
+
+ let mut write = client.write();
+
+ set_nonblocking(write.as_raw_fd(), true)?;
+
+ while limit > 0 {
+ let n = limit.min(BUFFER.len());
+
+ write.write_all(&BUFFER[..n])?;
+ limit -= n;
}
+
+ set_nonblocking(write.as_raw_fd(), false)?;
+
Ok(client)
}
@@ -64,6 +82,31 @@ impl Client {
}
pub unsafe fn open(s: &str) -> Option<Client> {
+ Client::from_fifo(s).or_else(|| Client::from_pipe(s))
+ }
+
+ /// `--jobserver-auth=fifo:PATH`
+ fn from_fifo(s: &str) -> Option<Client> {
+ let mut parts = s.splitn(2, ':');
+ if parts.next().unwrap() != "fifo" {
+ return None;
+ }
+ let path = match parts.next() {
+ Some(p) => Path::new(p),
+ None => return None,
+ };
+ let file = match OpenOptions::new().read(true).write(true).open(path) {
+ Ok(f) => f,
+ Err(_) => return None,
+ };
+ Some(Client::Fifo {
+ file,
+ path: path.into(),
+ })
+ }
+
+ /// `--jobserver-auth=R,W`
+ unsafe fn from_pipe(s: &str) -> Option<Client> {
let mut parts = s.splitn(2, ',');
let read = parts.next().unwrap();
let write = match parts.next() {
@@ -97,12 +140,28 @@ impl Client {
}
unsafe fn from_fds(read: c_int, write: c_int) -> Client {
- Client {
+ Client::Pipe {
read: File::from_raw_fd(read),
write: File::from_raw_fd(write),
}
}
+ /// Gets the read end of our jobserver client.
+ fn read(&self) -> &File {
+ match self {
+ Client::Pipe { read, .. } => read,
+ Client::Fifo { file, .. } => file,
+ }
+ }
+
+ /// Gets the write end of our jobserver client.
+ fn write(&self) -> &File {
+ match self {
+ Client::Pipe { write, .. } => write,
+ Client::Fifo { file, .. } => file,
+ }
+ }
+
pub fn acquire(&self) -> io::Result<Acquired> {
// Ignore interrupts and keep trying if that happens
loop {
@@ -137,11 +196,12 @@ impl Client {
// to shut us down, so we otherwise punt all errors upwards.
unsafe {
let mut fd: libc::pollfd = mem::zeroed();
- fd.fd = self.read.as_raw_fd();
+ let mut read = self.read();
+ fd.fd = read.as_raw_fd();
fd.events = libc::POLLIN;
loop {
let mut buf = [0];
- match (&self.read).read(&mut buf) {
+ match read.read(&mut buf) {
Ok(1) => return Ok(Some(Acquired { byte: buf[0] })),
Ok(_) => {
return Err(io::Error::new(
@@ -179,7 +239,7 @@ impl Client {
// always quickly release a token). If that turns out to not be the
// case we'll get an error anyway!
let byte = data.map(|d| d.byte).unwrap_or(b'+');
- match (&self.write).write(&[byte])? {
+ match self.write().write(&[byte])? {
1 => Ok(()),
_ => Err(io::Error::new(
io::ErrorKind::Other,
@@ -189,16 +249,31 @@ impl Client {
}
pub fn string_arg(&self) -> String {
- format!("{},{}", self.read.as_raw_fd(), self.write.as_raw_fd())
+ match self {
+ Client::Pipe { read, write } => format!("{},{}", read.as_raw_fd(), write.as_raw_fd()),
+ Client::Fifo { path, .. } => format!("fifo:{}", path.to_str().unwrap()),
+ }
+ }
+
+ pub fn available(&self) -> io::Result<usize> {
+ let mut len = MaybeUninit::<c_int>::uninit();
+ cvt(unsafe { libc::ioctl(self.read().as_raw_fd(), libc::FIONREAD, len.as_mut_ptr()) })?;
+ Ok(unsafe { len.assume_init() } as usize)
}
pub fn configure(&self, cmd: &mut Command) {
+ match self {
+ // We `File::open`ed it when inheriting from environment,
+ // so no need to set cloexec for fifo.
+ Client::Fifo { .. } => return,
+ Client::Pipe { .. } => {}
+ };
// Here we basically just want to say that in the child process
// we'll configure the read/write file descriptors to *not* be
// cloexec, so they're inherited across the exec and specified as
// integers through `string_arg` above.
- let read = self.read.as_raw_fd();
- let write = self.write.as_raw_fd();
+ let read = self.read().as_raw_fd();
+ let write = self.write().as_raw_fd();
unsafe {
cmd.pre_exec(move || {
set_cloexec(read, false)?;
@@ -224,7 +299,14 @@ pub(crate) fn spawn_helper(
let mut err = None;
USR1_INIT.call_once(|| unsafe {
let mut new: libc::sigaction = mem::zeroed();
- new.sa_sigaction = sigusr1_handler as usize;
+ #[cfg(target_os = "aix")]
+ {
+ new.sa_union.__su_sigaction = sigusr1_handler;
+ }
+ #[cfg(not(target_os = "aix"))]
+ {
+ new.sa_sigaction = sigusr1_handler as usize;
+ }
new.sa_flags = libc::SA_SIGINFO as _;
if libc::sigaction(libc::SIGUSR1, &new, ptr::null_mut()) != 0 {
err = Some(io::Error::last_os_error());
@@ -322,6 +404,16 @@ fn set_cloexec(fd: c_int, set: bool) -> io::Result<()> {
}
}
+fn set_nonblocking(fd: c_int, set: bool) -> io::Result<()> {
+ let status_flag = if set { libc::O_NONBLOCK } else { 0 };
+
+ unsafe {
+ cvt(libc::fcntl(fd, libc::F_SETFL, status_flag))?;
+ }
+
+ Ok(())
+}
+
fn cvt(t: c_int) -> io::Result<c_int> {
if t == -1 {
Err(io::Error::last_os_error())
diff --git a/vendor/jobserver/src/wasm.rs b/vendor/jobserver/src/wasm.rs
index b88a9d952..3793bd67c 100644
--- a/vendor/jobserver/src/wasm.rs
+++ b/vendor/jobserver/src/wasm.rs
@@ -59,6 +59,11 @@ impl Client {
);
}
+ pub fn available(&self) -> io::Result<usize> {
+ let lock = self.inner.count.lock().unwrap_or_else(|e| e.into_inner());
+ Ok(*lock)
+ }
+
pub fn configure(&self, _cmd: &mut Command) {
unreachable!();
}
diff --git a/vendor/jobserver/src/windows.rs b/vendor/jobserver/src/windows.rs
index d795c1cee..6791efea4 100644
--- a/vendor/jobserver/src/windows.rs
+++ b/vendor/jobserver/src/windows.rs
@@ -170,6 +170,26 @@ impl Client {
self.name.clone()
}
+ pub fn available(&self) -> io::Result<usize> {
+ // Can't read value of a semaphore on Windows, so
+ // try to acquire without sleeping, since we can find out the
+ // old value on release. If acquisiton fails, then available is 0.
+ unsafe {
+ let r = WaitForSingleObject(self.sem.0, 0);
+ if r != WAIT_OBJECT_0 {
+ Ok(0)
+ } else {
+ let mut prev: LONG = 0;
+ let r = ReleaseSemaphore(self.sem.0, 1, &mut prev);
+ if r != 0 {
+ Ok(prev as usize + 1)
+ } else {
+ Err(io::Error::last_os_error())
+ }
+ }
+ }
+ }
+
pub fn configure(&self, _cmd: &mut Command) {
// nothing to do here, we gave the name of our semaphore to the
// child above