Skip to content

Commit

Permalink
Rollup merge of rust-lang#35718 - michaelwoerister:incr-comp-dir-lock…
Browse files Browse the repository at this point in the history
…ing, r=nikomatsakis

Implement synchronization scheme for incr. comp. directory

This PR implements a copy-on-write-based synchronization scheme for the incremental compilation cache directory. For technical details, see the documentation at the beginning of `rustc_incremental/persist/fs.rs`.

The PR contains unit tests for some functions but for testing whether the scheme properly handles races, a more elaborate test setup would be needed. It would probably involve a small tool that allows to manipulate the incremental compilation directory in a controlled way and then letting a compiler instance run against directories in different states. I don't know if it's worth the trouble of adding another test category to `compiletest`, but I'd be happy to do so.

Fixes rust-lang#32754
Fixes rust-lang#34957
  • Loading branch information
eddyb authored Aug 23, 2016
2 parents cf9cf3f + 95d3cf3 commit d86838e
Show file tree
Hide file tree
Showing 18 changed files with 1,390 additions and 177 deletions.
7 changes: 3 additions & 4 deletions mk/crates.mk
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ DEPS_rustc := syntax fmt_macros flate arena serialize getopts rbml \
rustc_const_math syntax_pos rustc_errors
DEPS_rustc_back := std syntax flate log libc
DEPS_rustc_borrowck := rustc log graphviz syntax syntax_pos rustc_errors rustc_mir
DEPS_rustc_data_structures := std log serialize
DEPS_rustc_data_structures := std log serialize libc
DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \
rustc_typeck rustc_mir rustc_resolve log syntax serialize rustc_llvm \
rustc_trans rustc_privacy rustc_lint rustc_plugin \
Expand All @@ -137,9 +137,8 @@ DEPS_rustc_save_analysis := rustc log syntax syntax_pos serialize
DEPS_rustc_typeck := rustc syntax syntax_pos rustc_platform_intrinsics rustc_const_math \
rustc_const_eval rustc_errors

DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \
test rustc_lint rustc_const_eval syntax_pos

DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts test \
rustc_lint rustc_const_eval syntax_pos rustc_data_structures

TOOL_DEPS_compiletest := test getopts log serialize
TOOL_DEPS_rustdoc := rustdoc
Expand Down
15 changes: 15 additions & 0 deletions src/librustc/hir/svh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

use std::fmt;
use std::hash::{Hash, Hasher};
use serialize::{Encodable, Decodable, Encoder, Decoder};

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct Svh {
Expand Down Expand Up @@ -51,3 +52,17 @@ impl fmt::Display for Svh {
f.pad(&self.to_string())
}
}

impl Encodable for Svh {
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
s.emit_u64(self.as_u64().to_le())
}
}

impl Decodable for Svh {
fn decode<D: Decoder>(d: &mut D) -> Result<Svh, D::Error> {
d.read_u64()
.map(u64::from_le)
.map(Svh::new)
}
}
101 changes: 100 additions & 1 deletion src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ use syntax::feature_gate::AttributeType;
use syntax_pos::{Span, MultiSpan};

use rustc_back::target::Target;
use rustc_data_structures::flock;
use llvm;

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

incr_comp_session: RefCell<IncrCompSession>,

next_node_id: Cell<ast::NodeId>,
}

Expand Down Expand Up @@ -331,6 +334,76 @@ impl Session {
&self.opts.search_paths,
kind)
}

pub fn init_incr_comp_session(&self,
session_dir: PathBuf,
lock_file: flock::Lock) {
let mut incr_comp_session = self.incr_comp_session.borrow_mut();

if let IncrCompSession::NotInitialized = *incr_comp_session { } else {
bug!("Trying to initialize IncrCompSession `{:?}`", *incr_comp_session)
}

*incr_comp_session = IncrCompSession::Active {
session_directory: session_dir,
lock_file: lock_file,
};
}

pub fn finalize_incr_comp_session(&self, new_directory_path: PathBuf) {
let mut incr_comp_session = self.incr_comp_session.borrow_mut();

if let IncrCompSession::Active { .. } = *incr_comp_session { } else {
bug!("Trying to finalize IncrCompSession `{:?}`", *incr_comp_session)
}

// Note: This will also drop the lock file, thus unlocking the directory
*incr_comp_session = IncrCompSession::Finalized {
session_directory: new_directory_path,
};
}

pub fn mark_incr_comp_session_as_invalid(&self) {
let mut incr_comp_session = self.incr_comp_session.borrow_mut();

let session_directory = match *incr_comp_session {
IncrCompSession::Active { ref session_directory, .. } => {
session_directory.clone()
}
_ => bug!("Trying to invalidate IncrCompSession `{:?}`",
*incr_comp_session),
};

// Note: This will also drop the lock file, thus unlocking the directory
*incr_comp_session = IncrCompSession::InvalidBecauseOfErrors {
session_directory: session_directory
};
}

pub fn incr_comp_session_dir(&self) -> cell::Ref<PathBuf> {
let incr_comp_session = self.incr_comp_session.borrow();
cell::Ref::map(incr_comp_session, |incr_comp_session| {
match *incr_comp_session {
IncrCompSession::NotInitialized => {
bug!("Trying to get session directory from IncrCompSession `{:?}`",
*incr_comp_session)
}
IncrCompSession::Active { ref session_directory, .. } |
IncrCompSession::Finalized { ref session_directory } |
IncrCompSession::InvalidBecauseOfErrors { ref session_directory } => {
session_directory
}
}
})
}

pub fn incr_comp_session_dir_opt(&self) -> Option<cell::Ref<PathBuf>> {
if self.opts.incremental.is_some() {
Some(self.incr_comp_session_dir())
} else {
None
}
}
}

pub fn build_session(sopts: config::Options,
Expand Down Expand Up @@ -446,13 +519,39 @@ pub fn build_session_(sopts: config::Options,
injected_panic_runtime: Cell::new(None),
available_macros: RefCell::new(HashSet::new()),
imported_macro_spans: RefCell::new(HashMap::new()),
incr_comp_session: RefCell::new(IncrCompSession::NotInitialized),
};

init_llvm(&sess);

sess
}

/// Holds data on the current incremental compilation session, if there is one.
#[derive(Debug)]
pub enum IncrCompSession {
// This is the state the session will be in until the incr. comp. dir is
// needed.
NotInitialized,
// This is the state during which the session directory is private and can
// be modified.
Active {
session_directory: PathBuf,
lock_file: flock::Lock,
},
// This is the state after the session directory has been finalized. In this
// state, the contents of the directory must not be modified any more.
Finalized {
session_directory: PathBuf,
},
// This is an error state that is reached when some compilation error has
// occurred. It indicates that the contents of the session directory must
// not be used, since they might be invalid.
InvalidBecauseOfErrors {
session_directory: PathBuf,
}
}

fn init_llvm(sess: &Session) {
unsafe {
// Before we touch LLVM, make sure that multithreading is enabled.
Expand Down
41 changes: 38 additions & 3 deletions src/librustc/util/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,49 @@ pub fn fix_windows_verbatim_for_gcc(p: &Path) -> PathBuf {
}
}

pub enum LinkOrCopy {
Link,
Copy
}

/// Copy `p` into `q`, preferring to use hard-linking if possible. If
/// `q` already exists, it is removed first.
pub fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Q) -> io::Result<()> {
/// The result indicates which of the two operations has been performed.
pub fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Q) -> io::Result<LinkOrCopy> {
let p = p.as_ref();
let q = q.as_ref();
if q.exists() {
try!(fs::remove_file(&q));
}
fs::hard_link(p, q)
.or_else(|_| fs::copy(p, q).map(|_| ()))

match fs::hard_link(p, q) {
Ok(()) => Ok(LinkOrCopy::Link),
Err(_) => {
match fs::copy(p, q) {
Ok(_) => Ok(LinkOrCopy::Copy),
Err(e) => Err(e)
}
}
}
}

// Like std::fs::create_dir_all, except handles concurrent calls among multiple
// threads or processes.
pub fn create_dir_racy(path: &Path) -> io::Result<()> {
match fs::create_dir(path) {
Ok(()) => return Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => return Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
Err(e) => return Err(e),
}
match path.parent() {
Some(p) => try!(create_dir_racy(p)),
None => return Err(io::Error::new(io::ErrorKind::Other,
"failed to create whole tree")),
}
match fs::create_dir(path) {
Ok(()) => Ok(()),
Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists => Ok(()),
Err(e) => Err(e),
}
}
Loading

0 comments on commit d86838e

Please sign in to comment.