]> git.lizzy.rs Git - rust.git/blob - src/librustc_data_structures/jobserver.rs
Auto merge of #58423 - nox:relax-bounds-buf-reader, r=dtolnay
[rust.git] / src / librustc_data_structures / jobserver.rs
1 use jobserver_crate::{Client, HelperThread, Acquired};
2 use lazy_static::lazy_static;
3 use std::sync::{Condvar, Arc, Mutex};
4 use std::mem;
5
6 #[derive(Default)]
7 struct LockedProxyData {
8     /// The number of free thread tokens, this may include the implicit token given to the process
9     free: usize,
10
11     /// The number of threads waiting for a token
12     waiters: usize,
13
14     /// The number of tokens we requested from the server
15     requested: usize,
16
17     /// Stored tokens which will be dropped when we no longer need them
18     tokens: Vec<Acquired>,
19 }
20
21 impl LockedProxyData {
22     fn request_token(&mut self, thread: &Mutex<HelperThread>) {
23         self.requested += 1;
24         thread.lock().unwrap().request_token();
25     }
26
27     fn release_token(&mut self, cond_var: &Condvar) {
28         if self.waiters > 0 {
29             self.free += 1;
30             cond_var.notify_one();
31         } else {
32             if self.tokens.is_empty() {
33                 // We are returning the implicit token
34                 self.free += 1;
35             } else {
36                 // Return a real token to the server
37                 self.tokens.pop().unwrap();
38             }
39         }
40     }
41
42     fn take_token(&mut self, thread: &Mutex<HelperThread>) -> bool {
43         if self.free > 0 {
44             self.free -= 1;
45             self.waiters -= 1;
46
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);
51             }
52
53             true
54         } else {
55             false
56         }
57     }
58
59     fn new_requested_token(&mut self, token: Acquired, cond_var: &Condvar) {
60         self.requested -= 1;
61
62         // Does anything need this token?
63         if self.waiters > 0 {
64             self.free += 1;
65             self.tokens.push(token);
66             cond_var.notify_one();
67         } else {
68             // Otherwise we'll just drop it
69             mem::drop(token);
70         }
71     }
72 }
73
74 #[derive(Default)]
75 struct ProxyData {
76     lock: Mutex<LockedProxyData>,
77     cond_var: Condvar,
78 }
79
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.
83 struct Proxy {
84     thread: Mutex<HelperThread>,
85     data: Arc<ProxyData>,
86 }
87
88 lazy_static! {
89     // We can only call `from_env` once per process
90
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.
96     //
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
101     // across machines.
102     //
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
105     // per-process.
106     static ref GLOBAL_CLIENT: Client = unsafe {
107         Client::from_env().unwrap_or_else(|| {
108             Client::new(32).expect("failed to create jobserver")
109         })
110     };
111
112     static ref GLOBAL_PROXY: Proxy = {
113         let data = Arc::new(ProxyData::default());
114
115         Proxy {
116             data: data.clone(),
117             thread: Mutex::new(client().into_helper_thread(move |token| {
118                 data.lock.lock().unwrap().new_requested_token(token.unwrap(), &data.cond_var);
119             }).unwrap()),
120         }
121     };
122 }
123
124 pub fn client() -> Client {
125     GLOBAL_CLIENT.clone()
126 }
127
128 pub fn acquire_thread() {
129     GLOBAL_PROXY.acquire_token();
130 }
131
132 pub fn release_thread() {
133     GLOBAL_PROXY.release_token();
134 }
135
136 impl Proxy {
137     fn release_token(&self) {
138         self.data.lock.lock().unwrap().release_token(&self.data.cond_var);
139     }
140
141     fn acquire_token(&self) {
142         let mut data = self.data.lock.lock().unwrap();
143         data.waiters += 1;
144         if data.take_token(&self.thread) {
145             return;
146         }
147         // Request a token for us
148         data.request_token(&self.thread);
149         loop {
150             data = self.data.cond_var.wait(data).unwrap();
151             if data.take_token(&self.thread) {
152                 return;
153             }
154         }
155     }
156 }