Skip to content
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
40 changes: 39 additions & 1 deletion compiler/rustc_incremental/src/persist/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ use rustc_data_structures::svh::Svh;
use rustc_data_structures::unord::{UnordMap, UnordSet};
use rustc_data_structures::{base_n, flock};
use rustc_fs_util::{LinkOrCopy, link_or_copy, try_canonicalize};
use rustc_middle::dep_graph::WorkProduct;
use rustc_session::config::OutputType;
use rustc_session::{IncrCompSession, Session, StableCrateId};
use rustc_span::{Symbol, bug};
use tracing::debug;
Expand Down Expand Up @@ -332,7 +334,31 @@ pub fn finalize_session_directory(
let new_path = incr_comp_session_dir.parent().unwrap().join(&*sub_dir_name);
debug!("finalize_session_directory() - new path: {}", new_path.display());

match rename_path_with_retry(&*incr_comp_session_dir, &new_path, 3) {
let result = std_fs::rename(&*incr_comp_session_dir, &new_path).or_else(|e| {
if !cfg!(windows) || e.kind() != ErrorKind::PermissionDenied {
return Err(e);
}

// On ReFS, renaming a directory that contains a hard link to the metadata workproduct file
// can fail if it is being used by another process (such as another rustc instance).
// As a fallback, we try to replace the hard link with a copy, which should allow the
// rename to succeed.
// See https://github.com/rust-lang/rust/issues/151181
if let Err(err) = replace_hard_link_with_copy(&in_incr_comp_dir_sess(
Comment thread
TheHighestBit marked this conversation as resolved.
&incr_comp_session,
&format!(
"{}.{}",
WorkProduct::METADATA_WORKPRODUCT_CGU_NAME,
OutputType::Metadata.extension()
),
)) {
debug!("finalize_session_directory() - error replacing hard link with copy: {}", err);
}

rename_path_with_retry(&*incr_comp_session_dir, &new_path, 3)
});

match result {
Ok(_) => {
debug!("finalize_session_directory() - directory renamed successfully");
}
Expand Down Expand Up @@ -893,3 +919,15 @@ fn rename_path_with_retry(from: &Path, to: &Path, mut retries_left: usize) -> st
}
}
}

/// Turns a hard link of the file at `path` into a copy.
fn replace_hard_link_with_copy(path: &Path) -> std::io::Result<()> {
let tmp_name = path.with_added_extension("tmp");

// In case a stale temporary file was linked from a previous failed attempt.
safe_remove_file(&tmp_name)?;

std_fs::copy(path, &tmp_name).and_then(|_| std_fs::rename(&tmp_name, path)).inspect_err(|_| {
let _ = safe_remove_file(&tmp_name);
})
}
15 changes: 15 additions & 0 deletions compiler/rustc_incremental/src/persist/fs/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,18 @@ fn test_find_source_directory_in_iter() {
None
);
}

#[test]
fn test_replace_hard_link_with_copy_unshares_hard_link() {
let dir = rustc_fs_util::TempDirBuilder::new().tempdir_in(std::env::temp_dir()).unwrap();
let file = dir.path().join("file");
let link = dir.path().join("link");
std_fs::write(&file, b"original").unwrap();
std_fs::hard_link(&file, &link).unwrap();

replace_hard_link_with_copy(&link).unwrap();

std_fs::write(&file, b"changed").unwrap();
assert_eq!(std_fs::read(&link).unwrap(), b"original");
assert!(!link.with_added_extension("tmp").exists());
}
6 changes: 3 additions & 3 deletions compiler/rustc_interface/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rustc_data_structures::svh::Svh;
use rustc_errors::timings::TimingSection;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{DepGraph, WorkProductMap};
use rustc_middle::dep_graph::{DepGraph, WorkProduct, WorkProductMap};
use rustc_middle::ty::TyCtxt;
use rustc_session::config::{self, OutputFilenames, OutputType};
use rustc_session::{IncrCompSession, Session};
Expand Down Expand Up @@ -99,8 +99,8 @@ impl Linker {
let (id, product) = rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir(
sess,
incr_comp_session.as_ref().unwrap(),
"metadata",
&[("rmeta", path)],
WorkProduct::METADATA_WORKPRODUCT_CGU_NAME,
&[(OutputType::Metadata.extension(), path)],
&[],
);
work_products.insert(id, product);
Expand Down
9 changes: 5 additions & 4 deletions compiler/rustc_metadata/src/rmeta/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalDefIdSet};
use rustc_hir::definitions::DefPathData;
use rustc_hir::find_attr;
use rustc_hir_pretty::id_to_string;
use rustc_middle::dep_graph::WorkProductId;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::mir::interpret;
use rustc_middle::query::Providers;
Expand All @@ -28,7 +28,7 @@ use rustc_middle::ty::codec::TyEncoder;
use rustc_middle::ty::fast_reject::{self, TreatParams};
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque};
use rustc_session::config::mitigation_coverage::DeniedPartialMitigation;
use rustc_session::config::{OptLevel, TargetModifier};
use rustc_session::config::{OptLevel, OutputType, TargetModifier};
use rustc_span::def_id::CRATE_MOD_ID;
use rustc_span::hygiene::HygieneEncodeContext;
use rustc_span::{
Expand Down Expand Up @@ -2502,11 +2502,12 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) {

// If the metadata dep-node is green, try to reuse the saved work product.
if tcx.dep_graph.is_fully_enabled()
&& let work_product_id = WorkProductId::from_cgu_name("metadata")
&& let work_product_id =
WorkProductId::from_cgu_name(WorkProduct::METADATA_WORKPRODUCT_CGU_NAME)
&& let Some(work_product) = tcx.dep_graph.previous_work_product(&work_product_id)
&& tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some()
{
let saved_path = &work_product.saved_files["rmeta"];
let saved_path = &work_product.saved_files[OutputType::Metadata.extension()];
let incr_comp_session_dir = &tcx.incr_comp_session.unwrap().session_directory;
let source_file_in_incr_dir = &incr_comp_session_dir.join(saved_path);
debug!("copying preexisting metadata from {source_file_in_incr_dir:?} to {path:?}");
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_middle/src/dep_graph/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1143,6 +1143,13 @@ pub struct WorkProduct {
pub saved_files: UnordMap<String, String>,
}

impl WorkProduct {
/// The metadata work product is not produced by any CGU and thus its
/// name cannot be derived from `CodegenUnit`. Both the writers and readers
/// of the metadata work product use this constant to agree on the name.
pub const METADATA_WORKPRODUCT_CGU_NAME: &str = "metadata";
}

pub type WorkProductMap = UnordMap<WorkProductId, WorkProduct>;

// Index type for `DepNodeData`'s edges.
Expand Down
Loading