@@ -1258,15 +1258,16 @@ impl Build {
1258
1258
1259
1259
#[ cfg( feature = "parallel" ) ]
1260
1260
fn compile_objects ( & self , objs : & [ Object ] , print : & PrintThread ) -> Result < ( ) , Error > {
1261
- use std:: sync:: Once ;
1261
+ use std:: sync:: { mpsc :: channel , Once } ;
1262
1262
1263
1263
// Limit our parallelism globally with a jobserver. Start off by
1264
1264
// releasing our own token for this process so we can have a bit of an
1265
1265
// easier to write loop below. If this fails, though, then we're likely
1266
1266
// on Windows with the main implicit token, so we just have a bit extra
1267
1267
// parallelism for a bit and don't reacquire later.
1268
1268
let server = jobserver ( ) ;
1269
- let reacquire = server. release_raw ( ) . is_ok ( ) ;
1269
+ // Reacquire our process's token on drop
1270
+ let _reacquire = server. release_raw ( ) . ok ( ) . map ( |_| JobserverToken ( server) ) ;
1270
1271
1271
1272
// When compiling objects in parallel we do a few dirty tricks to speed
1272
1273
// things up:
@@ -1287,29 +1288,31 @@ impl Build {
1287
1288
// acquire the appropriate tokens, Once all objects have been compiled
1288
1289
// we wait on all the processes and propagate the results of compilation.
1289
1290
1290
- let children = objs
1291
- . iter ( )
1292
- . map ( |obj| {
1293
- let ( mut cmd, program) = self . create_compile_object_cmd ( obj) ?;
1294
- let token = server. acquire ( ) ?;
1291
+ let ( tx, rx) = channel :: < ( _ , String , KillOnDrop , _ ) > ( ) ;
1295
1292
1296
- let child = spawn ( & mut cmd, & program, print. pipe_writer_cloned ( ) ?. unwrap ( ) ) ?;
1293
+ // Since jobserver::Client::acquire can block, waiting
1294
+ // must be done in parallel so that acquire won't block forever.
1295
+ let wait_thread = thread:: Builder :: new ( ) . spawn ( move || {
1296
+ for ( cmd, program, mut child, _token) in rx {
1297
+ wait_on_child ( & cmd, & program, & mut child. 0 ) ?;
1298
+ }
1297
1299
1298
- Ok ( ( cmd, program, KillOnDrop ( child) , token) )
1299
- } )
1300
- . collect :: < Result < Vec < _ > , Error > > ( ) ?;
1300
+ Ok ( ( ) )
1301
+ } ) ?;
1301
1302
1302
- for ( cmd , program , mut child , _token ) in children {
1303
- wait_on_child ( & cmd, & program, & mut child . 0 ) ?;
1304
- }
1303
+ for obj in objs {
1304
+ let ( mut cmd, program) = self . create_compile_object_cmd ( obj ) ?;
1305
+ let token = server . acquire ( ) ? ;
1305
1306
1306
- // Reacquire our process's token before we proceed, which we released
1307
- // before entering the loop above.
1308
- if reacquire {
1309
- server. acquire_raw ( ) ?;
1307
+ let child = spawn ( & mut cmd, & program, print. pipe_writer_cloned ( ) ?. unwrap ( ) ) ?;
1308
+
1309
+ if tx. send ( ( cmd, program, KillOnDrop ( child) , token) ) . is_err ( ) {
1310
+ break ;
1311
+ }
1310
1312
}
1313
+ drop ( tx) ;
1311
1314
1312
- return Ok ( ( ) ) ;
1315
+ return wait_thread . join ( ) . expect ( "wait_thread panics" ) ;
1313
1316
1314
1317
/// Returns a suitable `jobserver::Client` used to coordinate
1315
1318
/// parallelism between build scripts.
@@ -1365,6 +1368,13 @@ impl Build {
1365
1368
child. kill ( ) . ok ( ) ;
1366
1369
}
1367
1370
}
1371
+
1372
+ struct JobserverToken ( & ' static jobserver:: Client ) ;
1373
+ impl Drop for JobserverToken {
1374
+ fn drop ( & mut self ) {
1375
+ let _ = self . 0 . acquire_raw ( ) ;
1376
+ }
1377
+ }
1368
1378
}
1369
1379
1370
1380
#[ cfg( not( feature = "parallel" ) ) ]
0 commit comments