use std::{ os::fd::{AsFd, AsRawFd, OwnedFd}, slice, }; use nix::{errno::Errno, fcntl::OFlag, poll, unistd}; use common::error::*; use super::util::unix; #[derive(Debug)] pub struct Jobserver { tokens: usize, r: OwnedFd, w: OwnedFd, } impl Jobserver { pub fn new(tokens: usize) -> Result { let (r, w) = unistd::pipe2(OFlag::O_CLOEXEC | OFlag::O_NONBLOCK).context("pipe()")?; for _ in 0..tokens { if unistd::write(w.as_fd(), b"+").is_err() { break; } } unix::set_blocking(&w, true)?; Ok(Jobserver { tokens, r, w }) } pub fn wait(&mut self) -> u8 { loop { poll::poll( &mut [poll::PollFd::new(self.r.as_fd(), poll::PollFlags::POLLIN)], poll::PollTimeout::NONE, ) .expect("poll()"); let mut token = 0; match unistd::read(self.r.as_raw_fd(), slice::from_mut(&mut token)) { Ok(n) => { assert!(n == 1); return token; } Err(Errno::EAGAIN) => { // Token was sniped by another task continue; } error @ Err(_) => { error.expect("read()"); } } } } pub fn post(&mut self, token: u8) { let n = unistd::write(self.w.as_fd(), slice::from_ref(&token)).expect("write()"); assert!(n == 1); } pub fn set_cloexec(&mut self, cloexec: bool) -> Result<()> { unix::set_cloexec(&self.r, cloexec)?; unix::set_cloexec(&self.w, cloexec)?; Ok(()) } pub fn to_makeflags(&self) -> String { format!( " -j{} --jobserver-auth={},{}", self.tokens, self.r.as_raw_fd(), self.w.as_raw_fd(), ) } } // FIXME Log lost tokens on drop