14
14
//! communication with the jobserver (otherwise it's too hard to work with the
15
15
//! jobserver API). This is non-ideal, and it would be good to avoid, but
16
16
//! currently that cost is pretty much required for correct functionality, as we
17
- //! must be able to atomically wait on both a Condvar (for other threads
18
- //! releasing the implicit token) and the jobserver itself. That's not possible
19
- //! with the jobserver API today unless we spawn up an additional thread.
17
+ //! must be able to atomically wait on both other threads releasing the implicit
18
+ //! token and the jobserver itself. That's not possible with the jobserver API
19
+ //! today unless we spawn up an additional thread.
20
20
//!
21
21
//! There are 3 primary APIs this crate exposes:
22
- //! * acquire()
23
- //! * release()
24
- //! * acquire_from_request()
25
- //! * request_token()
22
+ //! * `acquire_thread()`
23
+ //! * `release_thread()`
24
+ //! * `request_token(impl FnOnce(Acquired))`
26
25
//!
27
- //! The first two, acquire and release, are blocking functions which acquire
28
- //! and release a jobserver token .
26
+ //! `acquire_thread` blocks on obtaining a token, `release_thread` releases a
27
+ //! token without blocking .
29
28
//!
30
- //! The latter two help manage async requesting of tokens: specifically,
31
- //! acquire_from_request() will block on acquiring token but will not request it
32
- //! from the jobserver itself, whereas the last one just requests a token (and
33
- //! should return pretty quickly, i.e., it does not block on some event).
29
+ //! `request_token` queues up the called function without blocking.
34
30
//!
35
31
//! -------------------------------------
36
32
//!
@@ -62,8 +58,9 @@ use jobserver::Client;
62
58
use lazy_static:: lazy_static;
63
59
use rustc_serialize:: json:: as_json;
64
60
use std:: collections:: VecDeque ;
61
+ use std:: mem;
65
62
use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
66
- use std:: sync:: { Arc , Condvar , Mutex } ;
63
+ use std:: sync:: { Arc , Condvar , Mutex , MutexGuard } ;
67
64
68
65
lazy_static ! {
69
66
// We can only call `from_env` once per process
@@ -91,7 +88,7 @@ lazy_static! {
91
88
} )
92
89
} ;
93
90
94
- static ref HELPER : Mutex < Helper > = Mutex :: new ( Helper :: new( ) ) ;
91
+ static ref HELPER : Helper = Helper :: new( ) ;
95
92
}
96
93
97
94
// These are the values for TOKEN_REQUESTS, which is an enum between these
@@ -139,22 +136,38 @@ pub fn initialize(token_requests: bool) {
139
136
}
140
137
}
141
138
142
- pub struct Helper {
139
+ struct Helper {
143
140
helper : jobserver:: HelperThread ,
141
+
142
+ token_requests : Arc < Mutex < TokenRequests > > ,
143
+ }
144
+
145
+ struct TokenRequests {
144
146
tokens : usize ,
145
- requests : Arc < Mutex < VecDeque < Box < dyn FnOnce ( Acquired ) + Send > > > > ,
147
+ requests : VecDeque < Box < dyn FnOnce ( Acquired ) + Send > > ,
148
+ }
149
+
150
+ impl TokenRequests {
151
+ fn new ( ) -> Self {
152
+ Self { tokens : 1 , requests : VecDeque :: new ( ) }
153
+ }
154
+
155
+ fn push_request ( & mut self , request : impl FnOnce ( Acquired ) + Send + ' static ) {
156
+ self . requests . push_back ( Box :: new ( request) ) ;
157
+ }
146
158
}
147
159
148
160
impl Helper {
149
161
fn new ( ) -> Self {
150
- let requests: Arc < Mutex < VecDeque < Box < dyn FnOnce ( Acquired ) + Send > > > > =
151
- Arc :: new ( Mutex :: new ( VecDeque :: new ( ) ) ) ;
152
- let requests2 = requests. clone ( ) ;
162
+ let requests = Arc :: new ( Mutex :: new ( TokenRequests :: new ( ) ) ) ;
163
+ let helper_thread_requests = requests. clone ( ) ;
153
164
let helper = GLOBAL_CLIENT
154
165
. clone ( )
155
166
. into_helper_thread ( move |token| {
156
167
log:: trace!( "Helper thread token sending into channel" ) ;
157
- if let Some ( sender) = requests2. lock ( ) . unwrap ( ) . pop_front ( ) {
168
+ let mut helper_thread_requests = helper_thread_requests. lock ( ) . unwrap ( ) ;
169
+ let sender = helper_thread_requests. requests . pop_front ( ) ;
170
+ if let Some ( sender) = sender {
158
171
// We've acquired a token, but we need to not use it as we have our own
159
172
// custom release-on-drop struct since we'll want different logic than
160
173
// just normally releasing the token in this case.
@@ -163,49 +176,47 @@ impl Helper {
163
176
// was in the pipe (i.e., we just write back the same byte all the time)
164
177
// but that's not expected to be a problem.
165
178
token. expect ( "acquire token" ) . drop_without_releasing ( ) ;
166
- sender ( Acquired :: new ( ) ) ;
179
+ sender ( Acquired :: new ( helper_thread_requests ) ) ;
167
180
}
168
181
169
182
// If we didn't manage to send the token off, just drop it on
170
183
// the ground; it'll get released automatically.
171
184
} )
172
185
. expect ( "spawned helper" ) ;
173
- Helper { helper, tokens : 1 , requests }
186
+ Helper { helper, token_requests : requests }
174
187
}
175
188
176
- // This blocks on acquiring a token ( that must have been previously
177
- // requested) .
178
- fn acquire_token_from_prior_request ( & mut self ) -> Acquired {
179
- if self . tokens == 0 {
180
- self . tokens += 1 ;
181
- return Acquired :: new ( ) ;
189
+ // This blocks on acquiring a token that was requested from the
190
+ // HelperThread, i.e., through `Helper::request_token` .
191
+ fn acquire_token_from_prior_request ( & self ) -> Acquired {
192
+ let mut token_requests = self . token_requests . lock ( ) . unwrap ( ) ;
193
+ if token_requests . tokens == 0 {
194
+ return Acquired :: new ( token_requests ) ;
182
195
}
183
196
184
197
let receiver = Arc :: new ( ( Mutex :: new ( None ) , Condvar :: new ( ) ) ) ;
185
198
let receiver2 = receiver. clone ( ) ;
186
-
187
- self . requests . lock ( ) . unwrap ( ) . push_back ( Box :: new ( move |token| {
199
+ token_requests. push_request ( move |token| {
188
200
let mut slot = receiver. 0 . lock ( ) . unwrap ( ) ;
189
201
* slot = Some ( token) ;
190
202
receiver. 1 . notify_one ( ) ;
191
- } ) ) ;
203
+ } ) ;
192
204
193
- let ( lock , cvar ) = & * receiver2 ;
194
- let mut guard = cvar . wait_while ( lock . lock ( ) . unwrap ( ) , |slot| slot . is_none ( ) ) . unwrap ( ) ;
205
+ // Release tokens guard after registering our callback
206
+ mem :: drop ( token_requests ) ;
195
207
196
- self . tokens += 1 ;
208
+ let ( lock, cvar) = & * receiver2;
209
+ let mut guard = cvar. wait_while ( lock. lock ( ) . unwrap ( ) , |s| s. is_none ( ) ) . unwrap ( ) ;
197
210
guard. take ( ) . unwrap ( )
198
211
}
199
212
200
- fn release_token ( & mut self ) {
201
- let mut requests = self . requests . lock ( ) . unwrap ( ) ;
202
-
203
- self . tokens -= 1 ;
204
-
205
- if self . tokens == 0 {
213
+ fn release_token ( & self ) {
214
+ let mut token_requests = self . token_requests . lock ( ) . unwrap ( ) ;
215
+ token_requests. tokens -= 1 ;
216
+ if token_requests. tokens == 0 {
206
217
// If there is a sender, then it needs to be given this token.
207
- if let Some ( sender) = requests. pop_front ( ) {
208
- sender ( Acquired :: new ( ) ) ;
218
+ if let Some ( sender) = token_requests . requests . pop_front ( ) {
219
+ sender ( Acquired :: new ( token_requests ) ) ;
209
220
return ;
210
221
}
211
222
@@ -219,7 +230,7 @@ impl Helper {
219
230
}
220
231
}
221
232
222
- pub fn request_token ( & self ) {
233
+ fn request_token ( & self ) {
223
234
log:: trace!( "{:?} requesting token" , std:: thread:: current( ) . id( ) ) ;
224
235
// Just notify, don't actually acquire here.
225
236
notify_acquiring_token ( ) ;
@@ -241,7 +252,9 @@ impl Drop for Acquired {
241
252
}
242
253
243
254
impl Acquired {
244
- fn new ( ) -> Self {
255
+ fn new ( mut requests : MutexGuard < ' _ , TokenRequests > ) -> Self {
256
+ // When we create a token, bump up the acquired token counter
257
+ requests. tokens += 1 ;
245
258
Self { armed : true }
246
259
}
247
260
@@ -267,21 +280,19 @@ fn notify_acquiring_token() {
267
280
}
268
281
}
269
282
283
+ /// This does not block the current thread, but schedules the passed callback to
284
+ /// be called at some point in the future when a token is acquired.
270
285
pub fn request_token ( f : impl FnOnce ( Acquired ) + Send + ' static ) {
271
- HELPER . lock ( ) . unwrap ( ) . requests . lock ( ) . unwrap ( ) . push_back ( Box :: new ( move |token| {
272
- f ( token) ;
273
- } ) ) ;
274
- }
275
-
276
- pub fn acquire_from_request ( ) -> Acquired {
277
- HELPER . lock ( ) . unwrap ( ) . acquire_token_from_prior_request ( )
286
+ HELPER . token_requests . lock ( ) . unwrap ( ) . push_request ( f) ;
287
+ HELPER . request_token ( ) ;
278
288
}
279
289
290
+ /// This blocks the current thread until a token is acquired.
280
291
pub fn acquire_thread ( ) {
281
- HELPER . lock ( ) . unwrap ( ) . request_token ( ) ;
282
- HELPER . lock ( ) . unwrap ( ) . acquire_token_from_prior_request ( ) . disarm ( ) ;
292
+ HELPER . request_token ( ) ;
293
+ HELPER . acquire_token_from_prior_request ( ) . disarm ( ) ;
283
294
}
284
295
285
296
pub fn release_thread ( ) {
286
- HELPER . lock ( ) . unwrap ( ) . release_token ( ) ;
297
+ HELPER . release_token ( ) ;
287
298
}
0 commit comments