Skip to content

Commit d1d8258

Browse files
Implement copy-on-write scheme for managing the incremental compilation cache.
1 parent a2ff0e2 commit d1d8258

File tree

14 files changed

+1161
-151
lines changed

14 files changed

+1161
-151
lines changed

src/librustc/hir/svh.rs

+15
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
1818
use std::fmt;
1919
use std::hash::{Hash, Hasher};
20+
use serialize::{Encodable, Decodable, Encoder, Decoder};
2021

2122
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
2223
pub struct Svh {
@@ -51,3 +52,17 @@ impl fmt::Display for Svh {
5152
f.pad(&self.to_string())
5253
}
5354
}
55+
56+
impl Encodable for Svh {
57+
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
58+
s.emit_u64(self.as_u64().to_le())
59+
}
60+
}
61+
62+
impl Decodable for Svh {
63+
fn decode<D: Decoder>(d: &mut D) -> Result<Svh, D::Error> {
64+
d.read_u64()
65+
.map(u64::from_le)
66+
.map(Svh::new)
67+
}
68+
}

src/librustc/session/mod.rs

+92-1
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ use syntax::feature_gate::AttributeType;
3333
use syntax_pos::{Span, MultiSpan};
3434

3535
use rustc_back::target::Target;
36+
use rustc_data_structures::flock;
3637
use llvm;
3738

3839
use std::path::{Path, PathBuf};
39-
use std::cell::{Cell, RefCell};
40+
use std::cell::{self, Cell, RefCell};
4041
use std::collections::{HashMap, HashSet};
4142
use std::env;
4243
use std::ffi::CString;
@@ -101,6 +102,8 @@ pub struct Session {
101102
/// macro name and defintion span in the source crate.
102103
pub imported_macro_spans: RefCell<HashMap<Span, (String, Span)>>,
103104

105+
incr_comp_session: RefCell<IncrCompSession>,
106+
104107
next_node_id: Cell<ast::NodeId>,
105108
}
106109

@@ -331,6 +334,70 @@ impl Session {
331334
&self.opts.search_paths,
332335
kind)
333336
}
337+
338+
pub fn init_incr_comp_session(&self,
339+
session_dir: PathBuf,
340+
lock_file: flock::Lock) {
341+
let mut incr_comp_session = self.incr_comp_session.borrow_mut();
342+
343+
if let IncrCompSession::NotInitialized = *incr_comp_session { } else {
344+
bug!("Trying to initialize IncrCompSession `{:?}`", *incr_comp_session)
345+
}
346+
347+
*incr_comp_session = IncrCompSession::Active {
348+
session_directory: session_dir,
349+
lock_file: lock_file,
350+
};
351+
}
352+
353+
pub fn finalize_incr_comp_session(&self, new_directory_path: PathBuf) {
354+
let mut incr_comp_session = self.incr_comp_session.borrow_mut();
355+
356+
if let IncrCompSession::Active { .. } = *incr_comp_session { } else {
357+
bug!("Trying to finalize IncrCompSession `{:?}`", *incr_comp_session)
358+
}
359+
360+
// Note: This will also drop the lock file, thus unlocking the directory
361+
*incr_comp_session = IncrCompSession::Finalized {
362+
session_directory: new_directory_path,
363+
};
364+
}
365+
366+
pub fn mark_incr_comp_session_as_invalid(&self) {
367+
let mut incr_comp_session = self.incr_comp_session.borrow_mut();
368+
369+
if let IncrCompSession::Active { .. } = *incr_comp_session { } else {
370+
bug!("Trying to invalidate IncrCompSession `{:?}`", *incr_comp_session)
371+
}
372+
373+
// Note: This will also drop the lock file, thus unlocking the directory
374+
*incr_comp_session = IncrCompSession::InvalidBecauseOfErrors;
375+
}
376+
377+
pub fn incr_comp_session_dir(&self) -> cell::Ref<PathBuf> {
378+
let incr_comp_session = self.incr_comp_session.borrow();
379+
cell::Ref::map(incr_comp_session, |incr_comp_session| {
380+
match *incr_comp_session {
381+
IncrCompSession::NotInitialized |
382+
IncrCompSession::InvalidBecauseOfErrors => {
383+
bug!("Trying to get session directory from IncrCompSession `{:?}`",
384+
*incr_comp_session)
385+
}
386+
IncrCompSession::Active { ref session_directory, .. } |
387+
IncrCompSession::Finalized { ref session_directory } => {
388+
session_directory
389+
}
390+
}
391+
})
392+
}
393+
394+
pub fn incr_comp_session_dir_opt(&self) -> Option<cell::Ref<PathBuf>> {
395+
if self.opts.incremental.is_some() {
396+
Some(self.incr_comp_session_dir())
397+
} else {
398+
None
399+
}
400+
}
334401
}
335402

336403
pub fn build_session(sopts: config::Options,
@@ -446,13 +513,37 @@ pub fn build_session_(sopts: config::Options,
446513
injected_panic_runtime: Cell::new(None),
447514
available_macros: RefCell::new(HashSet::new()),
448515
imported_macro_spans: RefCell::new(HashMap::new()),
516+
incr_comp_session: RefCell::new(IncrCompSession::NotInitialized),
449517
};
450518

451519
init_llvm(&sess);
452520

453521
sess
454522
}
455523

524+
/// Holds data on the current incremental compilation session, if there is one.
525+
#[derive(Debug)]
526+
pub enum IncrCompSession {
527+
// This is the state the session will be in until the incr. comp. dir is
528+
// needed.
529+
NotInitialized,
530+
// This is the state during which the session directory is private and can
531+
// be modified.
532+
Active {
533+
session_directory: PathBuf,
534+
lock_file: flock::Lock,
535+
},
536+
// This is the state after the session directory has been finalized. In this
537+
// state, the contents of the directory must not be modified any more.
538+
Finalized {
539+
session_directory: PathBuf,
540+
},
541+
// This is an error state that is reached when some compilation error has
542+
// occurred. It indicates that the contents of the session directory must
543+
// not be used, since they might be invalid.
544+
InvalidBecauseOfErrors,
545+
}
546+
456547
fn init_llvm(sess: &Session) {
457548
unsafe {
458549
// Before we touch LLVM, make sure that multithreading is enabled.

src/librustc/util/fs.rs

+38-3
Original file line numberDiff line numberDiff line change
@@ -56,14 +56,49 @@ pub fn fix_windows_verbatim_for_gcc(p: &Path) -> PathBuf {
5656
}
5757
}
5858

59+
pub enum LinkOrCopy {
60+
Link,
61+
Copy
62+
}
63+
5964
/// Copy `p` into `q`, preferring to use hard-linking if possible. If
6065
/// `q` already exists, it is removed first.
61-
pub fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Q) -> io::Result<()> {
66+
/// The result indicates which of the two operations has been performed.
67+
pub fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Q) -> io::Result<LinkOrCopy> {
6268
let p = p.as_ref();
6369
let q = q.as_ref();
6470
if q.exists() {
6571
try!(fs::remove_file(&q));
6672
}
67-
fs::hard_link(p, q)
68-
.or_else(|_| fs::copy(p, q).map(|_| ()))
73+
74+
match fs::hard_link(p, q) {
75+
Ok(()) => Ok(LinkOrCopy::Link),
76+
Err(_) => {
77+
match fs::copy(p, q) {
78+
Ok(_) => Ok(LinkOrCopy::Copy),
79+
Err(e) => Err(e)
80+
}
81+
}
82+
}
83+
}
84+
85+
// Like std::fs::create_dir_all, except handles concurrent calls among multiple
86+
// threads or processes.
87+
pub fn create_dir_racy(path: &Path) -> io::Result<()> {
88+
match fs::create_dir(path) {
89+
Ok(()) => return Ok(()),
90+
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => return Ok(()),
91+
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
92+
Err(e) => return Err(e),
93+
}
94+
match path.parent() {
95+
Some(p) => try!(create_dir_racy(p)),
96+
None => return Err(io::Error::new(io::ErrorKind::Other,
97+
"failed to create whole tree")),
98+
}
99+
match fs::create_dir(path) {
100+
Ok(()) => Ok(()),
101+
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()),
102+
Err(e) => Err(e),
103+
}
69104
}

src/librustc_data_structures/flock.rs

+2
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ mod imp {
136136
pub const F_SETLKW: libc::c_int = 7;
137137
}
138138

139+
#[derive(Debug)]
139140
pub struct Lock {
140141
fd: libc::c_int,
141142
}
@@ -251,6 +252,7 @@ mod imp {
251252
lpOverlapped: LPOVERLAPPED) -> BOOL;
252253
}
253254

255+
#[derive(Debug)]
254256
pub struct Lock {
255257
_file: File,
256258
}

src/librustc_driver/driver.rs

+11-8
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ pub fn compile_input(sess: &Session,
8888
// We need nested scopes here, because the intermediate results can keep
8989
// large chunks of memory alive and we want to free them as soon as
9090
// possible to keep the peak memory usage low
91-
let (outputs, trans, crate_name) = {
91+
let (outputs, trans) = {
9292
let krate = match phase_1_parse_input(sess, cfg, input) {
9393
Ok(krate) => krate,
9494
Err(mut parse_error) => {
@@ -212,11 +212,11 @@ pub fn compile_input(sess: &Session,
212212
// Discard interned strings as they are no longer required.
213213
token::clear_ident_interner();
214214

215-
Ok((outputs, trans, crate_name.clone()))
215+
Ok((outputs, trans))
216216
})??
217217
};
218218

219-
let phase5_result = phase_5_run_llvm_passes(sess, &crate_name, &trans, &outputs);
219+
let phase5_result = phase_5_run_llvm_passes(sess, &trans, &outputs);
220220

221221
controller_entry_point!(after_llvm,
222222
sess,
@@ -228,6 +228,10 @@ pub fn compile_input(sess: &Session,
228228

229229
phase_6_link_output(sess, &trans, &outputs);
230230

231+
// Now that we won't touch anything in the incremental compilation directory
232+
// any more, we can finalize it (which involves renaming it)
233+
rustc_incremental::finalize_session_directory(sess, trans.link.crate_hash);
234+
231235
controller_entry_point!(compilation_done,
232236
sess,
233237
CompileState::state_when_compilation_done(input, sess, outdir, output),
@@ -1011,19 +1015,18 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
10111015

10121016
time(time_passes,
10131017
"assert dep graph",
1014-
move || rustc_incremental::assert_dep_graph(tcx));
1018+
|| rustc_incremental::assert_dep_graph(tcx));
10151019

10161020
time(time_passes,
10171021
"serialize dep graph",
1018-
move || rustc_incremental::save_dep_graph(tcx));
1019-
1022+
|| rustc_incremental::save_dep_graph(tcx,
1023+
translation.link.crate_hash));
10201024
translation
10211025
}
10221026

10231027
/// Run LLVM itself, producing a bitcode file, assembly file or object file
10241028
/// as a side effect.
10251029
pub fn phase_5_run_llvm_passes(sess: &Session,
1026-
crate_name: &str,
10271030
trans: &trans::CrateTranslation,
10281031
outputs: &OutputFilenames) -> CompileResult {
10291032
if sess.opts.cg.no_integrated_as {
@@ -1046,7 +1049,7 @@ pub fn phase_5_run_llvm_passes(sess: &Session,
10461049

10471050
time(sess.time_passes(),
10481051
"serialize work products",
1049-
move || rustc_incremental::save_work_products(sess, crate_name));
1052+
move || rustc_incremental::save_work_products(sess));
10501053

10511054
if sess.err_count() > 0 {
10521055
Err(sess.err_count())

src/librustc_incremental/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#![feature(question_mark)]
2323
#![feature(rustc_private)]
2424
#![feature(staged_api)]
25+
#![feature(rand)]
2526

2627
extern crate graphviz;
2728
extern crate rbml;
@@ -44,3 +45,4 @@ pub use persist::save_dep_graph;
4445
pub use persist::save_trans_partition;
4546
pub use persist::save_work_products;
4647
pub use persist::in_incr_comp_dir;
48+
pub use persist::finalize_session_directory;

0 commit comments

Comments
 (0)