use std::io;
use std::process::Command;
use std::sync::Arc;
use futures::StreamExt;
use futures::channel::mpsc;
use futures::channel::oneshot;
use crate::errors::*;
#[cfg(not(windows))]
pub unsafe fn discard_inherited_jobserver() {
if let Some(value) = ["CARGO_MAKEFLAGS", "MAKEFLAGS", "MFLAGS"]
.into_iter()
.find_map(|env| std::env::var(env).ok())
{
if let Some(auth) = value.rsplit(' ').find_map(|arg| {
arg.strip_prefix("--jobserver-auth=")
.or_else(|| arg.strip_prefix("--jobserver-fds="))
}) {
if !auth.starts_with("fifo:") {
let mut parts = auth.splitn(2, ',');
let read = parts.next().unwrap();
let write = match parts.next() {
Some(w) => w,
None => return,
};
let read = read.parse().unwrap();
let write = write.parse().unwrap();
if read < 0 || write < 0 {
return;
}
unsafe {
if libc::fcntl(read, libc::F_GETFD) == -1 {
return;
}
if libc::fcntl(write, libc::F_GETFD) == -1 {
return;
}
libc::close(read);
libc::close(write);
}
}
}
}
}
#[derive(Clone)]
pub struct Client {
helper: Option<Arc<jobserver::HelperThread>>,
tx: Option<mpsc::UnboundedSender<oneshot::Sender<io::Result<jobserver::Acquired>>>>,
inner: jobserver::Client,
}
pub struct Acquired {
_token: Option<jobserver::Acquired>,
}
impl Client {
pub fn new() -> Client {
Client::new_num(crate::util::num_cpus())
}
pub fn new_num(num: usize) -> Client {
let inner = jobserver::Client::new(num).expect("failed to create jobserver");
Client::_new(inner, false)
}
fn _new(inner: jobserver::Client, inherited: bool) -> Client {
let (helper, tx) = if inherited {
(None, None)
} else {
let (tx, mut rx) = mpsc::unbounded::<oneshot::Sender<_>>();
let helper = inner
.clone()
.into_helper_thread(move |token| {
let rt = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
rt.block_on(async {
if let Some(sender) = rx.next().await {
drop(sender.send(token));
}
});
})
.expect("failed to spawn helper thread");
(Some(Arc::new(helper)), Some(tx))
};
Client { inner, helper, tx }
}
pub fn configure(&self, cmd: &mut Command) {
self.inner.configure(cmd);
}
pub async fn acquire(&self) -> Result<Acquired> {
let (helper, tx) = match (self.helper.as_ref(), self.tx.as_ref()) {
(Some(a), Some(b)) => (a, b),
_ => return Ok(Acquired { _token: None }),
};
let (mytx, myrx) = oneshot::channel();
helper.request_token();
tx.unbounded_send(mytx).unwrap();
let acquired = myrx
.await
.context("jobserver helper panicked")?
.context("failed to acquire jobserver token")?;
Ok(Acquired {
_token: Some(acquired),
})
}
}