Skip to content

Commit 892fed9

Browse files
committed
Add support for using a jobserver with Rayon
1 parent 350674b commit 892fed9

File tree

8 files changed

+172
-32
lines changed

8 files changed

+172
-32
lines changed

src/librustc/Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ num_cpus = "1.0"
2020
scoped-tls = "1.0"
2121
log = { version = "0.4", features = ["release_max_level_info", "std"] }
2222
polonius-engine = "0.6.2"
23-
rustc-rayon = "0.1.1"
24-
rustc-rayon-core = "0.1.1"
23+
rustc-rayon = "0.1.2"
24+
rustc-rayon-core = "0.1.2"
2525
rustc_apfloat = { path = "../librustc_apfloat" }
2626
rustc_target = { path = "../librustc_target" }
2727
rustc_data_structures = { path = "../librustc_data_structures" }

src/librustc/session/mod.rs

+3-27
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ use crate::util::profiling::SelfProfiler;
3434

3535
use rustc_target::spec::{PanicStrategy, RelroLevel, Target, TargetTriple};
3636
use rustc_data_structures::flock;
37-
use jobserver::Client;
37+
use rustc_data_structures::jobserver;
38+
use ::jobserver::Client;
3839

3940
use std;
4041
use std::cell::{self, Cell, RefCell};
@@ -1230,32 +1231,7 @@ pub fn build_session_(
12301231
optimization_fuel,
12311232
print_fuel_crate,
12321233
print_fuel,
1233-
// Note that this is unsafe because it may misinterpret file descriptors
1234-
// on Unix as jobserver file descriptors. We hopefully execute this near
1235-
// the beginning of the process though to ensure we don't get false
1236-
// positives, or in other words we try to execute this before we open
1237-
// any file descriptors ourselves.
1238-
//
1239-
// Pick a "reasonable maximum" if we don't otherwise have
1240-
// a jobserver in our environment, capping out at 32 so we
1241-
// don't take everything down by hogging the process run queue.
1242-
// The fixed number is used to have deterministic compilation
1243-
// across machines.
1244-
//
1245-
// Also note that we stick this in a global because there could be
1246-
// multiple `Session` instances in this process, and the jobserver is
1247-
// per-process.
1248-
jobserver: unsafe {
1249-
static mut GLOBAL_JOBSERVER: *mut Client = 0 as *mut _;
1250-
static INIT: std::sync::Once = std::sync::ONCE_INIT;
1251-
INIT.call_once(|| {
1252-
let client = Client::from_env().unwrap_or_else(|| {
1253-
Client::new(32).expect("failed to create jobserver")
1254-
});
1255-
GLOBAL_JOBSERVER = Box::into_raw(Box::new(client));
1256-
});
1257-
(*GLOBAL_JOBSERVER).clone()
1258-
},
1234+
jobserver: jobserver::client(),
12591235
has_global_allocator: Once::new(),
12601236
has_panic_handler: Once::new(),
12611237
driver_lint_caps,

src/librustc/ty/query/job.rs

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::{fmt, ptr};
77
use rustc_data_structures::fx::FxHashSet;
88
use rustc_data_structures::sync::{Lock, LockGuard, Lrc, Weak};
99
use rustc_data_structures::OnDrop;
10+
use rustc_data_structures::jobserver;
1011
use syntax_pos::Span;
1112

1213
use crate::ty::tls;
@@ -198,7 +199,11 @@ impl<'tcx> QueryLatch<'tcx> {
198199
// we have to be in the `wait` call. This is ensured by the deadlock handler
199200
// getting the self.info lock.
200201
rayon_core::mark_blocked();
202+
jobserver::release_thread();
201203
waiter.condvar.wait(&mut info);
204+
// Release the lock before we potentially block in `acquire_thread`
205+
mem::drop(info);
206+
jobserver::acquire_thread();
202207
}
203208
}
204209

src/librustc_data_structures/Cargo.toml

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ crate-type = ["dylib"]
1212
[dependencies]
1313
ena = "0.11"
1414
log = "0.4"
15+
jobserver_crate = { version = "0.1", package = "jobserver" }
16+
lazy_static = "1"
1517
rustc_cratesio_shim = { path = "../librustc_cratesio_shim" }
1618
serialize = { path = "../libserialize" }
1719
graphviz = { path = "../libgraphviz" }
1820
cfg-if = "0.1.2"
1921
stable_deref_trait = "1.0.0"
20-
rayon = { version = "0.1.1", package = "rustc-rayon" }
21-
rayon-core = { version = "0.1.1", package = "rustc-rayon-core" }
22+
rayon = { version = "0.1.2", package = "rustc-rayon" }
23+
rayon-core = { version = "0.1.2", package = "rustc-rayon-core" }
2224
rustc-hash = "1.0.1"
2325
smallvec = { version = "0.6.7", features = ["union", "may_dangle"] }
2426

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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+
pub 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+
pub struct ProxyData {
76+
lock: Mutex<LockedProxyData>,
77+
cond_var: Condvar,
78+
}
79+
80+
pub struct Proxy {
81+
thread: Mutex<HelperThread>,
82+
data: Arc<ProxyData>,
83+
}
84+
85+
lazy_static! {
86+
// We can only call `from_env` once per process
87+
88+
// Note that this is unsafe because it may misinterpret file descriptors
89+
// on Unix as jobserver file descriptors. We hopefully execute this near
90+
// the beginning of the process though to ensure we don't get false
91+
// positives, or in other words we try to execute this before we open
92+
// any file descriptors ourselves.
93+
//
94+
// Pick a "reasonable maximum" if we don't otherwise have
95+
// a jobserver in our environment, capping out at 32 so we
96+
// don't take everything down by hogging the process run queue.
97+
// The fixed number is used to have deterministic compilation
98+
// across machines.
99+
//
100+
// Also note that we stick this in a global because there could be
101+
// multiple rustc instances in this process, and the jobserver is
102+
// per-process.
103+
static ref GLOBAL_CLIENT: Client = unsafe {
104+
Client::from_env().unwrap_or_else(|| {
105+
Client::new(32).expect("failed to create jobserver")
106+
})
107+
};
108+
109+
static ref GLOBAL_PROXY: Proxy = {
110+
let data = Arc::new(ProxyData::default());
111+
112+
Proxy {
113+
data: data.clone(),
114+
thread: Mutex::new(client().into_helper_thread(move |token| {
115+
data.lock.lock().unwrap().new_requested_token(token.unwrap(), &data.cond_var);
116+
}).unwrap()),
117+
}
118+
};
119+
}
120+
121+
pub fn client() -> Client {
122+
GLOBAL_CLIENT.clone()
123+
}
124+
125+
pub fn acquire_thread() {
126+
GLOBAL_PROXY.acquire_token();
127+
}
128+
129+
pub fn release_thread() {
130+
GLOBAL_PROXY.release_token();
131+
}
132+
133+
impl Proxy {
134+
pub fn release_token(&self) {
135+
self.data.lock.lock().unwrap().release_token(&self.data.cond_var);
136+
}
137+
138+
pub fn acquire_token(&self) {
139+
let mut data = self.data.lock.lock().unwrap();
140+
data.waiters += 1;
141+
if data.take_token(&self.thread) {
142+
return;
143+
}
144+
// Request a token for us
145+
data.request_token(&self.thread);
146+
loop {
147+
data = self.data.cond_var.wait(data).unwrap();
148+
if data.take_token(&self.thread) {
149+
return;
150+
}
151+
}
152+
}
153+
}

src/librustc_data_structures/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ pub mod fx;
7777
pub mod graph;
7878
pub mod indexed_vec;
7979
pub mod interner;
80+
pub mod jobserver;
8081
pub mod obligation_forest;
8182
pub mod owning_ref;
8283
pub mod ptr_key;

src/librustc_driver/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ arena = { path = "../libarena" }
1313
graphviz = { path = "../libgraphviz" }
1414
log = "0.4"
1515
env_logger = { version = "0.5", default-features = false }
16-
rustc-rayon = "0.1.1"
16+
rustc-rayon = "0.1.2"
1717
scoped-tls = "1.0"
1818
rustc = { path = "../librustc" }
1919
rustc_allocator = { path = "../librustc_allocator" }

src/librustc_driver/driver.rs

+3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use rustc_allocator as allocator;
1717
use rustc_borrowck as borrowck;
1818
use rustc_codegen_utils::codegen_backend::CodegenBackend;
1919
use rustc_data_structures::sync::{self, Lock};
20+
use rustc_data_structures::jobserver;
2021
use rustc_incremental;
2122
use rustc_metadata::creader::CrateLoader;
2223
use rustc_metadata::cstore::{self, CStore};
@@ -72,6 +73,8 @@ pub fn spawn_thread_pool<F: FnOnce(config::Options) -> R + sync::Send, R: sync::
7273
let gcx_ptr = &Lock::new(0);
7374

7475
let config = ThreadPoolBuilder::new()
76+
.acquire_thread_handler(jobserver::acquire_thread)
77+
.release_thread_handler(jobserver::release_thread)
7578
.num_threads(Session::threads_from_count(opts.debugging_opts.threads))
7679
.deadlock_handler(|| unsafe { ty::query::handle_deadlock() })
7780
.stack_size(::STACK_SIZE);

0 commit comments

Comments
 (0)