1 use jobserver_crate::{Client, HelperThread, Acquired};
2 use lazy_static::lazy_static;
3 use std::sync::{Condvar, Arc, Mutex};
7 struct LockedProxyData {
8 /// The number of free thread tokens, this may include the implicit token given to the process
11 /// The number of threads waiting for a token
14 /// The number of tokens we requested from the server
17 /// Stored tokens which will be dropped when we no longer need them
18 tokens: Vec<Acquired>,
21 impl LockedProxyData {
22 fn request_token(&mut self, thread: &Mutex<HelperThread>) {
24 thread.lock().unwrap().request_token();
27 fn release_token(&mut self, cond_var: &Condvar) {
30 cond_var.notify_one();
32 if self.tokens.is_empty() {
33 // We are returning the implicit token
36 // Return a real token to the server
37 self.tokens.pop().unwrap();
42 fn take_token(&mut self, thread: &Mutex<HelperThread>) -> bool {
47 // We stole some token reqested by someone else
48 // Request another one
49 if self.requested + self.free < self.waiters {
50 self.request_token(thread);
59 fn new_requested_token(&mut self, token: Acquired, cond_var: &Condvar) {
62 // Does anything need this token?
65 self.tokens.push(token);
66 cond_var.notify_one();
68 // Otherwise we'll just drop it
76 lock: Mutex<LockedProxyData>,
80 /// A helper type which makes managing jobserver tokens easier.
81 /// It also allows you to treat the implicit token given to the process
82 /// in the same manner as requested tokens.
84 thread: Mutex<HelperThread>,
89 // We can only call `from_env` once per process
91 // Note that this is unsafe because it may misinterpret file descriptors
92 // on Unix as jobserver file descriptors. We hopefully execute this near
93 // the beginning of the process though to ensure we don't get false
94 // positives, or in other words we try to execute this before we open
95 // any file descriptors ourselves.
97 // Pick a "reasonable maximum" if we don't otherwise have
98 // a jobserver in our environment, capping out at 32 so we
99 // don't take everything down by hogging the process run queue.
100 // The fixed number is used to have deterministic compilation
103 // Also note that we stick this in a global because there could be
104 // multiple rustc instances in this process, and the jobserver is
106 static ref GLOBAL_CLIENT: Client = unsafe {
107 Client::from_env().unwrap_or_else(|| {
108 Client::new(32).expect("failed to create jobserver")
112 static ref GLOBAL_PROXY: Proxy = {
113 let data = Arc::new(ProxyData::default());
117 thread: Mutex::new(client().into_helper_thread(move |token| {
118 data.lock.lock().unwrap().new_requested_token(token.unwrap(), &data.cond_var);
124 pub fn client() -> Client {
125 GLOBAL_CLIENT.clone()
128 pub fn acquire_thread() {
129 GLOBAL_PROXY.acquire_token();
132 pub fn release_thread() {
133 GLOBAL_PROXY.release_token();
137 fn release_token(&self) {
138 self.data.lock.lock().unwrap().release_token(&self.data.cond_var);
141 fn acquire_token(&self) {
142 let mut data = self.data.lock.lock().unwrap();
144 if data.take_token(&self.thread) {
147 // Request a token for us
148 data.request_token(&self.thread);
150 data = self.data.cond_var.wait(data).unwrap();
151 if data.take_token(&self.thread) {