Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement synchronization scheme for incr. comp. directory #35718

Merged
merged 11 commits into from
Sep 1, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be horribly slow on Windows using ReFS filesystems. The CreateHardLink is only supported on the NTFS systems. ReFS supports copy-on-write (read: reflinks), but not hard links, so it is yet another reason to use reflinks if these are supported.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find any documentation about reflinks on ReFS, so I guess that any copy operation within the same ReFS file system is automatically the equivalent of creating a reflink? In that case, we could just fall back to always copying if the first try of creating a hard-link has failed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT simple copy is not reflink on ReFS, sadly.

There’s FSCTL in a Windows Server 2016+ which seems like it should behave like the reflink equivalent for btrfs.

That being said, it also seems like ReFSv1 (which is what you get by default) does not in fact support/expose that operation, so I guess there’s nothing we can do here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that's a pity :(
But thanks for the information!

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<()> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this just copied from compiletest?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's moved here from rustc_incremental::persist::util which doesn't exist anymore.

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