impl JobTokenServer { /// This function returns a static reference to the jobserver because /// - creating a jobserver from env is a bit fd-unsafe (e.g. the fd might /// be closed by other jobserver users in the process) and better do it /// at the start of the program. /// - in case a jobserver cannot be created from env (e.g. it's not /// present), we will create a global in-process only jobserver /// that has to be static so that it will be shared by all cc /// compilation. fn new() -> &'static Self { static INIT: Once = Once::new(); staticmut JOBSERVER: MaybeUninit<JobTokenServer> = MaybeUninit::uninit();
unsafe {
INIT.call_once(|| { let server = inherited_jobserver::JobServer::from_env()
.map(Self::Inherited)
.unwrap_or_else(|| Self::InProcess(inprocess_jobserver::JobServer::new()));
JOBSERVER = MaybeUninit::new(server);
}); // TODO: Poor man's assume_init_ref, as that'd require a MSRV of 1.55.
&*JOBSERVER.as_ptr()
}
}
}
use std::{
io, mem,
sync::{mpsc, Mutex, MutexGuard, PoisonError},
};
pub(super) struct JobServer { /// Implicit token for this process which is obtained and will be /// released in parent. Since JobTokens only give back what they got, /// there should be at most one global implicit token in the wild. /// /// Since Rust does not execute any `Drop` for global variables, /// we can't just put it back to jobserver and then re-acquire it at /// the end of the process. /// /// Use `Mutex` to avoid race between acquire and release. /// If an `AtomicBool` is used, then it's possible for: /// - `release_token_raw`: Tries to set `global_implicit_token` to true, but it is already /// set to `true`, continue to release it to jobserver /// - `acquire` takes the global implicit token, set `global_implicit_token` to false /// - `release_token_raw` now writes the token back into the jobserver, while /// `global_implicit_token` is `false` /// /// If the program exits here, then cc effectively increases parallelism by one, which is /// incorrect, hence we use a `Mutex` here.
global_implicit_token: Mutex<bool>,
inner: jobserver::Client,
}
/// All tokens except for the global implicit token will be put back into the jobserver /// immediately and they cannot be cached, since Rust does not call `Drop::drop` on /// global variables. pub(super) fn release_token_raw(&self) { letmut global_implicit_token = self.get_global_implicit_token();
if *global_implicit_token { // There's already a global implicit token, so this token must // be released back into jobserver. // // `release_raw` should not block let _ = self.inner.release_raw();
} else {
*global_implicit_token = true;
}
}
pub(crate) struct ActiveJobServer<'a> {
jobserver: &'a JobServer,
helper_thread: jobserver::HelperThread, /// When rx is dropped, all the token stored within it will be dropped.
rx: mpsc::Receiver<io::Result<jobserver::Acquired>>,
}
loop { // Fast path if mem::replace(&mut *self.jobserver.get_global_implicit_token(), false) { break Ok(JobToken::new());
}
// Cold path, no global implicit token, obtain one matchself.rx.try_recv() {
Ok(res) => { let acquired = res?;
acquired.drop_without_releasing(); break Ok(JobToken::new());
}
Err(mpsc::TryRecvError::Disconnected) => { break Err(Error::new(
ErrorKind::JobserverHelpThreadError, "jobserver help thread has returned before ActiveJobServer is dropped",
))
}
Err(mpsc::TryRecvError::Empty) => { if !has_requested_token { self.helper_thread.request_token();
has_requested_token = true;
}
YieldOnce::default().await
}
}
}
}
}
}
mod inprocess_jobserver { usesuper::JobToken;
usecrate::parallel::async_executor::YieldOnce;
use std::{
env::var,
sync::atomic::{
AtomicU32,
Ordering::{AcqRel, Acquire},
},
};
pub(crate) struct JobServer(AtomicU32);
impl JobServer { pub(super) fn new() -> Self { // Use `NUM_JOBS` if set (it's configured by Cargo) and otherwise // just fall back to a semi-reasonable number. // // Note that we could use `num_cpus` here but it's an extra // dependency that will almost never be used, so // it's generally not too worth it. letmut parallelism = 4; // TODO: Use std::thread::available_parallelism as an upper bound // when MSRV is bumped. iflet Ok(amt) = var("NUM_JOBS") { iflet Ok(amt) = amt.parse() {
parallelism = amt;
}
}
Self(AtomicU32::new(parallelism))
}
pub(super) asyncfn acquire(&self) -> JobToken { loop { let res = self
.0
.fetch_update(AcqRel, Acquire, |tokens| tokens.checked_sub(1));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.