Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit df1eee8

Browse files
authoredAug 23, 2016
Rollup merge of rust-lang#35718 - michaelwoerister:incr-comp-dir-locking, 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
2 parents 462ed98 + 95d3cf3 commit df1eee8

File tree

18 files changed

+1390
-177
lines changed

18 files changed

+1390
-177
lines changed
 

‎mk/crates.mk

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ DEPS_rustc := syntax fmt_macros flate arena serialize getopts rbml \
112112
rustc_const_math syntax_pos rustc_errors
113113
DEPS_rustc_back := std syntax flate log libc
114114
DEPS_rustc_borrowck := rustc log graphviz syntax syntax_pos rustc_errors rustc_mir
115-
DEPS_rustc_data_structures := std log serialize
115+
DEPS_rustc_data_structures := std log serialize libc
116116
DEPS_rustc_driver := arena flate getopts graphviz libc rustc rustc_back rustc_borrowck \
117117
rustc_typeck rustc_mir rustc_resolve log syntax serialize rustc_llvm \
118118
rustc_trans rustc_privacy rustc_lint rustc_plugin \
@@ -137,9 +137,8 @@ DEPS_rustc_save_analysis := rustc log syntax syntax_pos serialize
137137
DEPS_rustc_typeck := rustc syntax syntax_pos rustc_platform_intrinsics rustc_const_math \
138138
rustc_const_eval rustc_errors
139139

140-
DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts \
141-
test rustc_lint rustc_const_eval syntax_pos
142-
140+
DEPS_rustdoc := rustc rustc_driver native:hoedown serialize getopts test \
141+
rustc_lint rustc_const_eval syntax_pos rustc_data_structures
143142

144143
TOOL_DEPS_compiletest := test getopts log serialize
145144
TOOL_DEPS_rustdoc := rustdoc

‎src/librustc/hir/svh.rs

Lines changed: 15 additions & 0 deletions
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

Lines changed: 100 additions & 1 deletion
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,76 @@ 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+
let session_directory = match *incr_comp_session {
370+
IncrCompSession::Active { ref session_directory, .. } => {
371+
session_directory.clone()
372+
}
373+
_ => bug!("Trying to invalidate IncrCompSession `{:?}`",
374+
*incr_comp_session),
375+
};
376+
377+
// Note: This will also drop the lock file, thus unlocking the directory
378+
*incr_comp_session = IncrCompSession::InvalidBecauseOfErrors {
379+
session_directory: session_directory
380+
};
381+
}
382+
383+
pub fn incr_comp_session_dir(&self) -> cell::Ref<PathBuf> {
384+
let incr_comp_session = self.incr_comp_session.borrow();
385+
cell::Ref::map(incr_comp_session, |incr_comp_session| {
386+
match *incr_comp_session {
387+
IncrCompSession::NotInitialized => {
388+
bug!("Trying to get session directory from IncrCompSession `{:?}`",
389+
*incr_comp_session)
390+
}
391+
IncrCompSession::Active { ref session_directory, .. } |
392+
IncrCompSession::Finalized { ref session_directory } |
393+
IncrCompSession::InvalidBecauseOfErrors { ref session_directory } => {
394+
session_directory
395+
}
396+
}
397+
})
398+
}
399+
400+
pub fn incr_comp_session_dir_opt(&self) -> Option<cell::Ref<PathBuf>> {
401+
if self.opts.incremental.is_some() {
402+
Some(self.incr_comp_session_dir())
403+
} else {
404+
None
405+
}
406+
}
334407
}
335408

336409
pub fn build_session(sopts: config::Options,
@@ -446,13 +519,39 @@ pub fn build_session_(sopts: config::Options,
446519
injected_panic_runtime: Cell::new(None),
447520
available_macros: RefCell::new(HashSet::new()),
448521
imported_macro_spans: RefCell::new(HashMap::new()),
522+
incr_comp_session: RefCell::new(IncrCompSession::NotInitialized),
449523
};
450524

451525
init_llvm(&sess);
452526

453527
sess
454528
}
455529

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

‎src/librustc/util/fs.rs

Lines changed: 38 additions & 3 deletions
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/librustdoc/flock.rs renamed to ‎src/librustc_data_structures/flock.rs

Lines changed: 121 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
//! librustdoc, it is not production quality at all.
1616
1717
#![allow(non_camel_case_types)]
18+
use std::path::Path;
1819

1920
pub use self::imp::Lock;
2021

@@ -41,6 +42,7 @@ mod imp {
4142
pub l_sysid: libc::c_int,
4243
}
4344

45+
pub const F_RDLCK: libc::c_short = 0;
4446
pub const F_WRLCK: libc::c_short = 1;
4547
pub const F_UNLCK: libc::c_short = 2;
4648
pub const F_SETLK: libc::c_int = 6;
@@ -60,6 +62,7 @@ mod imp {
6062
pub l_sysid: libc::c_int,
6163
}
6264

65+
pub const F_RDLCK: libc::c_short = 1;
6366
pub const F_UNLCK: libc::c_short = 2;
6467
pub const F_WRLCK: libc::c_short = 3;
6568
pub const F_SETLK: libc::c_int = 12;
@@ -84,6 +87,7 @@ mod imp {
8487
pub l_sysid: libc::c_int,
8588
}
8689

90+
pub const F_RDLCK: libc::c_short = 1;
8791
pub const F_UNLCK: libc::c_short = 2;
8892
pub const F_WRLCK: libc::c_short = 3;
8993
pub const F_SETLK: libc::c_int = 8;
@@ -105,6 +109,7 @@ mod imp {
105109
pub l_sysid: libc::c_int,
106110
}
107111

112+
pub const F_RDLCK: libc::c_short = 1;
108113
pub const F_UNLCK: libc::c_short = 2;
109114
pub const F_WRLCK: libc::c_short = 3;
110115
pub const F_SETLK: libc::c_int = 8;
@@ -124,43 +129,66 @@ mod imp {
124129
pub l_pid: libc::pid_t,
125130
}
126131

132+
pub const F_RDLCK: libc::c_short = 1;
127133
pub const F_WRLCK: libc::c_short = 2;
128134
pub const F_UNLCK: libc::c_short = 3;
129135
pub const F_SETLK: libc::c_int = 6;
130136
pub const F_SETLKW: libc::c_int = 7;
131137
}
132138

139+
#[derive(Debug)]
133140
pub struct Lock {
134141
fd: libc::c_int,
135142
}
136143

137144
impl Lock {
138-
pub fn new(p: &Path) -> Lock {
145+
pub fn new(p: &Path,
146+
wait: bool,
147+
create: bool,
148+
exclusive: bool)
149+
-> io::Result<Lock> {
139150
let os: &OsStr = p.as_ref();
140151
let buf = CString::new(os.as_bytes()).unwrap();
152+
let open_flags = if create {
153+
libc::O_RDWR | libc::O_CREAT
154+
} else {
155+
libc::O_RDWR
156+
};
157+
141158
let fd = unsafe {
142-
libc::open(buf.as_ptr(), libc::O_RDWR | libc::O_CREAT,
159+
libc::open(buf.as_ptr(), open_flags,
143160
libc::S_IRWXU as libc::c_int)
144161
};
145-
assert!(fd > 0, "failed to open lockfile: {}",
146-
io::Error::last_os_error());
162+
163+
if fd < 0 {
164+
return Err(io::Error::last_os_error());
165+
}
166+
167+
let lock_type = if exclusive {
168+
os::F_WRLCK
169+
} else {
170+
os::F_RDLCK
171+
};
172+
147173
let flock = os::flock {
148174
l_start: 0,
149175
l_len: 0,
150176
l_pid: 0,
151177
l_whence: libc::SEEK_SET as libc::c_short,
152-
l_type: os::F_WRLCK,
178+
l_type: lock_type,
153179
l_sysid: 0,
154180
};
181+
let cmd = if wait { os::F_SETLKW } else { os::F_SETLK };
155182
let ret = unsafe {
156-
libc::fcntl(fd, os::F_SETLKW, &flock)
183+
libc::fcntl(fd, cmd, &flock)
157184
};
158185
if ret == -1 {
159186
let err = io::Error::last_os_error();
160187
unsafe { libc::close(fd); }
161-
panic!("could not lock `{}`: {}", p.display(), err);
188+
Err(err)
189+
} else {
190+
Ok(Lock { fd: fd })
162191
}
163-
Lock { fd: fd }
164192
}
165193
}
166194

@@ -191,18 +219,27 @@ mod imp {
191219
use std::os::windows::raw::HANDLE;
192220
use std::path::Path;
193221
use std::fs::{File, OpenOptions};
222+
use std::os::raw::{c_ulong, c_ulonglong, c_int};
223+
224+
type DWORD = c_ulong;
225+
type BOOL = c_int;
226+
type ULONG_PTR = c_ulonglong;
194227

195-
type DWORD = u32;
196228
type LPOVERLAPPED = *mut OVERLAPPED;
197-
type BOOL = i32;
198229
const LOCKFILE_EXCLUSIVE_LOCK: DWORD = 0x00000002;
230+
const LOCKFILE_FAIL_IMMEDIATELY: DWORD = 0x00000001;
231+
232+
const FILE_SHARE_DELETE: DWORD = 0x4;
233+
const FILE_SHARE_READ: DWORD = 0x1;
234+
const FILE_SHARE_WRITE: DWORD = 0x2;
199235

200236
#[repr(C)]
201237
struct OVERLAPPED {
202-
Internal: usize,
203-
InternalHigh: usize,
204-
Pointer: *mut u8,
205-
hEvent: *mut u8,
238+
Internal: ULONG_PTR,
239+
InternalHigh: ULONG_PTR,
240+
Offset: DWORD,
241+
OffsetHigh: DWORD,
242+
hEvent: HANDLE,
206243
}
207244

208245
extern "system" {
@@ -214,24 +251,88 @@ mod imp {
214251
lpOverlapped: LPOVERLAPPED) -> BOOL;
215252
}
216253

254+
#[derive(Debug)]
217255
pub struct Lock {
218256
_file: File,
219257
}
220258

221259
impl Lock {
222-
pub fn new(p: &Path) -> Lock {
223-
let f = OpenOptions::new().read(true).write(true).create(true)
224-
.open(p).unwrap();
260+
pub fn new(p: &Path,
261+
wait: bool,
262+
create: bool,
263+
exclusive: bool)
264+
-> io::Result<Lock> {
265+
assert!(p.parent().unwrap().exists(),
266+
"Parent directory of lock-file must exist: {}",
267+
p.display());
268+
269+
let share_mode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
270+
271+
let mut open_options = OpenOptions::new();
272+
open_options.read(true)
273+
.share_mode(share_mode);
274+
275+
if create {
276+
open_options.create(true)
277+
.write(true);
278+
}
279+
280+
debug!("Attempting to open lock file `{}`", p.display());
281+
let file = match open_options.open(p) {
282+
Ok(file) => {
283+
debug!("Lock file opened successfully");
284+
file
285+
}
286+
Err(err) => {
287+
debug!("Error opening lock file: {}", err);
288+
return Err(err)
289+
}
290+
};
291+
225292
let ret = unsafe {
226293
let mut overlapped: OVERLAPPED = mem::zeroed();
227-
LockFileEx(f.as_raw_handle(), LOCKFILE_EXCLUSIVE_LOCK, 0, 100, 0,
294+
295+
let mut dwFlags = 0;
296+
if !wait {
297+
dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
298+
}
299+
300+
if exclusive {
301+
dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
302+
}
303+
304+
debug!("Attempting to acquire lock on lock file `{}`",
305+
p.display());
306+
LockFileEx(file.as_raw_handle(),
307+
dwFlags,
308+
0,
309+
0xFFFF_FFFF,
310+
0xFFFF_FFFF,
228311
&mut overlapped)
229312
};
230313
if ret == 0 {
231314
let err = io::Error::last_os_error();
232-
panic!("could not lock `{}`: {}", p.display(), err);
315+
debug!("Failed acquiring file lock: {}", err);
316+
Err(err)
317+
} else {
318+
debug!("Successfully acquired lock.");
319+
Ok(Lock { _file: file })
233320
}
234-
Lock { _file: f }
235321
}
236322
}
323+
324+
// Note that we don't need a Drop impl on the Windows: The file is unlocked
325+
// automatically when it's closed.
326+
}
327+
328+
impl imp::Lock {
329+
pub fn panicking_new(p: &Path,
330+
wait: bool,
331+
create: bool,
332+
exclusive: bool)
333+
-> Lock {
334+
Lock::new(p, wait, create, exclusive).unwrap_or_else(|err| {
335+
panic!("could not lock `{}`: {}", p.display(), err);
336+
})
337+
}
237338
}

‎src/librustc_data_structures/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@
3131
#![feature(unboxed_closures)]
3232
#![feature(fn_traits)]
3333

34+
#![cfg_attr(unix, feature(libc))]
3435
#![cfg_attr(test, feature(test))]
3536

3637
extern crate core;
3738
#[macro_use]
3839
extern crate log;
3940
extern crate serialize as rustc_serialize; // used by deriving
41+
#[cfg(unix)]
42+
extern crate libc;
4043

4144
pub mod bitvec;
4245
pub mod graph;
@@ -51,6 +54,7 @@ pub mod fnv;
5154
pub mod tuple_slice;
5255
pub mod veccell;
5356
pub mod control_flow_graph;
57+
pub mod flock;
5458

5559
// See comments in src/librustc/lib.rs
5660
#[doc(hidden)]

‎src/librustc_driver/driver.rs

Lines changed: 11 additions & 8 deletions
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

Lines changed: 2 additions & 0 deletions
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;

‎src/librustc_incremental/persist/fs.rs

Lines changed: 988 additions & 0 deletions
Large diffs are not rendered by default.

‎src/librustc_incremental/persist/hash.rs

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ use rustc::hir::def_id::DefId;
1616
use rustc::hir::svh::Svh;
1717
use rustc::ty::TyCtxt;
1818
use rustc_data_structures::fnv::FnvHashMap;
19+
use rustc_data_structures::flock;
1920
use rustc_serialize::Decodable;
2021
use std::io::{ErrorKind, Read};
2122
use std::fs::File;
2223
use syntax::ast;
2324

2425
use super::data::*;
25-
use super::util::*;
26+
use super::fs::*;
2627

2728
pub struct HashContext<'a, 'tcx: 'a> {
2829
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -124,19 +125,43 @@ impl<'a, 'tcx> HashContext<'a, 'tcx> {
124125
debug!("load_data: svh={}", svh);
125126
assert!(old.is_none(), "loaded data for crate {:?} twice", cnum);
126127

127-
if let Some(path) = metadata_hash_path(self.tcx, cnum) {
128-
debug!("load_data: path={:?}", path);
128+
if let Some(session_dir) = find_metadata_hashes_for(self.tcx, cnum) {
129+
debug!("load_data: session_dir={:?}", session_dir);
130+
131+
// Lock the directory we'll be reading the hashes from.
132+
let lock_file_path = lock_file_path(&session_dir);
133+
let _lock = match flock::Lock::new(&lock_file_path,
134+
false, // don't wait
135+
false, // don't create the lock-file
136+
false) { // shared lock
137+
Ok(lock) => lock,
138+
Err(err) => {
139+
debug!("Could not acquire lock on `{}` while trying to \
140+
load metadata hashes: {}",
141+
lock_file_path.display(),
142+
err);
143+
144+
// Could not acquire the lock. The directory is probably in
145+
// in the process of being deleted. It's OK to just exit
146+
// here. It's the same scenario as if the file had not
147+
// existed in the first place.
148+
return
149+
}
150+
};
151+
152+
let hashes_file_path = metadata_hash_import_path(&session_dir);
153+
129154
let mut data = vec![];
130155
match
131-
File::open(&path)
132-
.and_then(|mut file| file.read_to_end(&mut data))
156+
File::open(&hashes_file_path)
157+
.and_then(|mut file| file.read_to_end(&mut data))
133158
{
134159
Ok(_) => {
135-
match self.load_from_data(cnum, &data) {
160+
match self.load_from_data(cnum, &data, svh) {
136161
Ok(()) => { }
137162
Err(err) => {
138163
bug!("decoding error in dep-graph from `{}`: {}",
139-
path.display(), err);
164+
&hashes_file_path.display(), err);
140165
}
141166
}
142167
}
@@ -148,7 +173,7 @@ impl<'a, 'tcx> HashContext<'a, 'tcx> {
148173
_ => {
149174
self.tcx.sess.err(
150175
&format!("could not load dep information from `{}`: {}",
151-
path.display(), err));
176+
hashes_file_path.display(), err));
152177
return;
153178
}
154179
}
@@ -157,11 +182,22 @@ impl<'a, 'tcx> HashContext<'a, 'tcx> {
157182
}
158183
}
159184

160-
fn load_from_data(&mut self, cnum: ast::CrateNum, data: &[u8]) -> Result<(), Error> {
185+
fn load_from_data(&mut self,
186+
cnum: ast::CrateNum,
187+
data: &[u8],
188+
expected_svh: Svh) -> Result<(), Error> {
161189
debug!("load_from_data(cnum={})", cnum);
162190

163191
// Load up the hashes for the def-ids from this crate.
164192
let mut decoder = Decoder::new(data, 0);
193+
let svh_in_hashes_file = try!(Svh::decode(&mut decoder));
194+
195+
if svh_in_hashes_file != expected_svh {
196+
// We should not be able to get here. If we do, then
197+
// `fs::find_metadata_hashes_for()` has messed up.
198+
bug!("mismatch between SVH in crate and SVH in incr. comp. hashes")
199+
}
200+
165201
let serialized_hashes = try!(SerializedMetadataHashes::decode(&mut decoder));
166202
for serialized_hash in serialized_hashes.hashes {
167203
// the hashes are stored with just a def-index, which is

‎src/librustc_incremental/persist/load.rs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use super::data::*;
2626
use super::directory::*;
2727
use super::dirty_clean;
2828
use super::hash::*;
29-
use super::util::*;
29+
use super::fs::*;
3030

3131
pub type DirtyNodes = FnvHashSet<DepNode<DefPathIndex>>;
3232

@@ -43,18 +43,36 @@ pub fn load_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
4343
return;
4444
}
4545

46+
match prepare_session_directory(tcx) {
47+
Ok(true) => {
48+
// We successfully allocated a session directory and there is
49+
// something in it to load, so continue
50+
}
51+
Ok(false) => {
52+
// We successfully allocated a session directory, but there is no
53+
// dep-graph data in it to load (because this is the first
54+
// compilation session with this incr. comp. dir.)
55+
return
56+
}
57+
Err(()) => {
58+
// Something went wrong while trying to allocate the session
59+
// directory. Don't try to use it any further.
60+
return
61+
}
62+
}
63+
4664
let _ignore = tcx.dep_graph.in_ignore();
4765
load_dep_graph_if_exists(tcx);
4866
}
4967

5068
fn load_dep_graph_if_exists<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
51-
let dep_graph_path = dep_graph_path(tcx).unwrap();
69+
let dep_graph_path = dep_graph_path(tcx.sess);
5270
let dep_graph_data = match load_data(tcx.sess, &dep_graph_path) {
5371
Some(p) => p,
5472
None => return // no file
5573
};
5674

57-
let work_products_path = tcx_work_products_path(tcx).unwrap();
75+
let work_products_path = work_products_path(tcx.sess);
5876
let work_products_data = match load_data(tcx.sess, &work_products_path) {
5977
Some(p) => p,
6078
None => return // no file
@@ -250,7 +268,7 @@ fn reconcile_work_products<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
250268
.saved_files
251269
.iter()
252270
.all(|&(_, ref file_name)| {
253-
let path = in_incr_comp_dir(tcx.sess, &file_name).unwrap();
271+
let path = in_incr_comp_dir_sess(tcx.sess, &file_name);
254272
path.exists()
255273
});
256274
if all_files_exist {
@@ -268,7 +286,7 @@ fn delete_dirty_work_product(tcx: TyCtxt,
268286
swp: SerializedWorkProduct) {
269287
debug!("delete_dirty_work_product({:?})", swp);
270288
for &(_, ref file_name) in &swp.work_product.saved_files {
271-
let path = in_incr_comp_dir(tcx.sess, file_name).unwrap();
289+
let path = in_incr_comp_dir_sess(tcx.sess, file_name);
272290
match fs::remove_file(&path) {
273291
Ok(()) => { }
274292
Err(err) => {

‎src/librustc_incremental/persist/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,16 @@
1515
mod data;
1616
mod directory;
1717
mod dirty_clean;
18+
mod fs;
1819
mod hash;
1920
mod load;
2021
mod preds;
2122
mod save;
22-
mod util;
2323
mod work_product;
2424

25+
pub use self::fs::finalize_session_directory;
26+
pub use self::fs::in_incr_comp_dir;
2527
pub use self::load::load_dep_graph;
2628
pub use self::save::save_dep_graph;
2729
pub use self::save::save_work_products;
2830
pub use self::work_product::save_trans_partition;
29-
pub use self::util::in_incr_comp_dir;

‎src/librustc_incremental/persist/save.rs

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use rbml::opaque::Encoder;
1212
use rustc::dep_graph::DepNode;
1313
use rustc::hir::def_id::DefId;
14-
use rustc::middle::cstore::LOCAL_CRATE;
14+
use rustc::hir::svh::Svh;
1515
use rustc::session::Session;
1616
use rustc::ty::TyCtxt;
1717
use rustc_data_structures::fnv::FnvHashMap;
@@ -20,14 +20,14 @@ use std::hash::{Hash, Hasher, SipHasher};
2020
use std::io::{self, Cursor, Write};
2121
use std::fs::{self, File};
2222
use std::path::PathBuf;
23-
2423
use super::data::*;
2524
use super::directory::*;
2625
use super::hash::*;
2726
use super::preds::*;
28-
use super::util::*;
27+
use super::fs::*;
2928

30-
pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
29+
pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
30+
svh: Svh) {
3131
debug!("save_dep_graph()");
3232
let _ignore = tcx.dep_graph.in_ignore();
3333
let sess = tcx.sess;
@@ -39,31 +39,31 @@ pub fn save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
3939
let query = tcx.dep_graph.query();
4040
let preds = Predecessors::new(&query, &mut hcx);
4141
save_in(sess,
42-
dep_graph_path(tcx),
42+
dep_graph_path(sess),
4343
|e| encode_dep_graph(&preds, &mut builder, e));
4444
save_in(sess,
45-
metadata_hash_path(tcx, LOCAL_CRATE),
46-
|e| encode_metadata_hashes(tcx, &preds, &mut builder, e));
45+
metadata_hash_export_path(sess),
46+
|e| encode_metadata_hashes(tcx, svh, &preds, &mut builder, e));
4747
}
4848

49-
pub fn save_work_products(sess: &Session, local_crate_name: &str) {
49+
pub fn save_work_products(sess: &Session) {
50+
if sess.opts.incremental.is_none() {
51+
return;
52+
}
53+
5054
debug!("save_work_products()");
5155
let _ignore = sess.dep_graph.in_ignore();
52-
let path = sess_work_products_path(sess, local_crate_name);
56+
let path = work_products_path(sess);
5357
save_in(sess, path, |e| encode_work_products(sess, e));
5458
}
5559

56-
fn save_in<F>(sess: &Session, opt_path_buf: Option<PathBuf>, encode: F)
60+
fn save_in<F>(sess: &Session, path_buf: PathBuf, encode: F)
5761
where F: FnOnce(&mut Encoder) -> io::Result<()>
5862
{
59-
let path_buf = match opt_path_buf {
60-
Some(p) => p,
61-
None => return,
62-
};
63-
64-
// FIXME(#32754) lock file?
65-
6663
// delete the old dep-graph, if any
64+
// Note: It's important that we actually delete the old file and not just
65+
// truncate and overwrite it, since it might be a shared hard-link, the
66+
// underlying data of which we don't want to modify
6767
if path_buf.exists() {
6868
match fs::remove_file(&path_buf) {
6969
Ok(()) => {}
@@ -153,6 +153,7 @@ pub fn encode_dep_graph(preds: &Predecessors,
153153
}
154154

155155
pub fn encode_metadata_hashes(tcx: TyCtxt,
156+
svh: Svh,
156157
preds: &Predecessors,
157158
builder: &mut DefIdDirectoryBuilder,
158159
encoder: &mut Encoder)
@@ -218,6 +219,7 @@ pub fn encode_metadata_hashes(tcx: TyCtxt,
218219
}
219220

220221
// Encode everything.
222+
try!(svh.encode(encoder));
221223
try!(serialized_hashes.encode(encoder));
222224

223225
Ok(())

‎src/librustc_incremental/persist/util.rs

Lines changed: 0 additions & 95 deletions
This file was deleted.

‎src/librustc_incremental/persist/work_product.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
//! This module contains files for saving intermediate work-products.
1212
13-
use persist::util::*;
13+
use persist::fs::*;
1414
use rustc::dep_graph::{WorkProduct, WorkProductId};
1515
use rustc::session::Session;
1616
use rustc::session::config::OutputType;
@@ -35,7 +35,7 @@ pub fn save_trans_partition(sess: &Session,
3535
files.iter()
3636
.map(|&(kind, ref path)| {
3737
let file_name = format!("cgu-{}.{}", cgu_name, kind.extension());
38-
let path_in_incr_dir = in_incr_comp_dir(sess, &file_name).unwrap();
38+
let path_in_incr_dir = in_incr_comp_dir_sess(sess, &file_name);
3939
match link_or_copy(path, &path_in_incr_dir) {
4040
Ok(_) => Some((kind, file_name)),
4141
Err(err) => {

‎src/librustc_trans/back/write.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
use back::lto;
1212
use back::link::{get_linker, remove};
13-
use rustc_incremental::save_trans_partition;
13+
use rustc_incremental::{save_trans_partition, in_incr_comp_dir};
1414
use session::config::{OutputFilenames, OutputTypes, Passes, SomePasses, AllPasses};
1515
use session::Session;
1616
use session::config::{self, OutputType};
@@ -328,8 +328,9 @@ struct CodegenContext<'a> {
328328
remark: Passes,
329329
// Worker thread number
330330
worker: usize,
331-
// Directory where incremental data is stored (if any)
332-
incremental: Option<PathBuf>,
331+
// The incremental compilation session directory, or None if we are not
332+
// compiling incrementally
333+
incr_comp_session_dir: Option<PathBuf>
333334
}
334335

335336
impl<'a> CodegenContext<'a> {
@@ -340,7 +341,7 @@ impl<'a> CodegenContext<'a> {
340341
plugin_passes: sess.plugin_llvm_passes.borrow().clone(),
341342
remark: sess.opts.cg.remark.clone(),
342343
worker: 0,
343-
incremental: sess.opts.incremental.clone(),
344+
incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone())
344345
}
345346
}
346347
}
@@ -962,17 +963,20 @@ fn execute_work_item(cgcx: &CodegenContext,
962963
work_item.output_names);
963964
}
964965
ModuleSource::Preexisting(wp) => {
965-
let incremental = cgcx.incremental.as_ref().unwrap();
966+
let incr_comp_session_dir = cgcx.incr_comp_session_dir
967+
.as_ref()
968+
.unwrap();
966969
let name = &work_item.mtrans.name;
967970
for (kind, saved_file) in wp.saved_files {
968971
let obj_out = work_item.output_names.temp_path(kind, Some(name));
969-
let source_file = incremental.join(&saved_file);
972+
let source_file = in_incr_comp_dir(&incr_comp_session_dir,
973+
&saved_file);
970974
debug!("copying pre-existing module `{}` from {:?} to {}",
971975
work_item.mtrans.name,
972976
source_file,
973977
obj_out.display());
974978
match link_or_copy(&source_file, &obj_out) {
975-
Ok(()) => { }
979+
Ok(_) => { }
976980
Err(err) => {
977981
cgcx.handler.err(&format!("unable to copy {} to {}: {}",
978982
source_file.display(),
@@ -1018,7 +1022,7 @@ fn run_work_multithreaded(sess: &Session,
10181022
let mut tx = Some(tx);
10191023
futures.push(rx);
10201024

1021-
let incremental = sess.opts.incremental.clone();
1025+
let incr_comp_session_dir = sess.incr_comp_session_dir_opt().map(|r| r.clone());
10221026

10231027
thread::Builder::new().name(format!("codegen-{}", i)).spawn(move || {
10241028
let diag_handler = Handler::with_emitter(true, false, box diag_emitter);
@@ -1031,7 +1035,7 @@ fn run_work_multithreaded(sess: &Session,
10311035
plugin_passes: plugin_passes,
10321036
remark: remark,
10331037
worker: i,
1034-
incremental: incremental,
1038+
incr_comp_session_dir: incr_comp_session_dir
10351039
};
10361040

10371041
loop {

‎src/librustdoc/html/render.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ use rustc::middle::privacy::AccessLevels;
6161
use rustc::middle::stability;
6262
use rustc::session::config::get_unstable_features_setting;
6363
use rustc::hir;
64+
use rustc_data_structures::flock;
6465

6566
use clean::{self, Attributes, GetDefId};
6667
use doctree;
@@ -650,7 +651,7 @@ fn write_shared(cx: &Context,
650651
// docs placed in the output directory, so this needs to be a synchronized
651652
// operation with respect to all other rustdocs running around.
652653
try_err!(mkdir(&cx.dst), &cx.dst);
653-
let _lock = ::flock::Lock::new(&cx.dst.join(".lock"));
654+
let _lock = flock::Lock::panicking_new(&cx.dst.join(".lock"), true, true, true);
654655

655656
// Add all the static files. These may already exist, but we just
656657
// overwrite them anyway to make sure that they're fresh and up-to-date.

‎src/librustdoc/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ extern crate libc;
3535
extern crate rustc;
3636
extern crate rustc_const_eval;
3737
extern crate rustc_const_math;
38+
extern crate rustc_data_structures;
3839
extern crate rustc_trans;
3940
extern crate rustc_driver;
4041
extern crate rustc_resolve;
@@ -86,7 +87,6 @@ pub mod plugins;
8687
pub mod visit_ast;
8788
pub mod visit_lib;
8889
pub mod test;
89-
mod flock;
9090

9191
use clean::Attributes;
9292

0 commit comments

Comments
 (0)
Please sign in to comment.