| use std::io; |
| use std::process::Command; |
| use std::sync::{Arc, Condvar, Mutex}; |
| use std::thread::{Builder, JoinHandle}; |
| |
| #[derive(Debug)] |
| pub struct Client { |
| inner: Arc<Inner>, |
| } |
| |
| #[derive(Debug)] |
| struct Inner { |
| count: Mutex<usize>, |
| cvar: Condvar, |
| } |
| |
| #[derive(Debug)] |
| pub struct Acquired(()); |
| |
| impl Client { |
| pub fn new(limit: usize) -> io::Result<Client> { |
| Ok(Client { |
| inner: Arc::new(Inner { |
| count: Mutex::new(limit), |
| cvar: Condvar::new(), |
| }), |
| }) |
| } |
| |
| pub unsafe fn open(_s: &str) -> Option<Client> { |
| None |
| } |
| |
| pub fn acquire(&self) -> io::Result<Acquired> { |
| let mut lock = self.inner.count.lock().unwrap_or_else(|e| e.into_inner()); |
| while *lock == 0 { |
| lock = self |
| .inner |
| .cvar |
| .wait(lock) |
| .unwrap_or_else(|e| e.into_inner()); |
| } |
| *lock -= 1; |
| Ok(Acquired(())) |
| } |
| |
| pub fn release(&self, _data: Option<&Acquired>) -> io::Result<()> { |
| let mut lock = self.inner.count.lock().unwrap_or_else(|e| e.into_inner()); |
| *lock += 1; |
| drop(lock); |
| self.inner.cvar.notify_one(); |
| Ok(()) |
| } |
| |
| pub fn string_arg(&self) -> String { |
| panic!( |
| "On this platform there is no cross process jobserver support, |
| so Client::configure is not supported." |
| ); |
| } |
| |
| pub fn configure(&self, _cmd: &mut Command) { |
| unreachable!(); |
| } |
| } |
| |
| #[derive(Debug)] |
| pub struct Helper { |
| thread: JoinHandle<()>, |
| } |
| |
| pub(crate) fn spawn_helper( |
| client: crate::Client, |
| state: Arc<super::HelperState>, |
| mut f: Box<dyn FnMut(io::Result<crate::Acquired>) + Send>, |
| ) -> io::Result<Helper> { |
| let thread = Builder::new().spawn(move || { |
| state.for_each_request(|_| f(client.acquire())); |
| })?; |
| |
| Ok(Helper { thread: thread }) |
| } |
| |
| impl Helper { |
| pub fn join(self) { |
| // TODO: this is not correct if the thread is blocked in |
| // `client.acquire()`. |
| drop(self.thread.join()); |
| } |
| } |