Skip to content

Commit 98007e2

Browse files
committed
Drop the query result memmap before serializing it back.
1 parent 6b47e1e commit 98007e2

File tree

4 files changed

+44
-26
lines changed

4 files changed

+44
-26
lines changed

compiler/rustc_incremental/src/persist/file_format.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ where
5252
// Delete the old file, if any.
5353
// Note: It's important that we actually delete the old file and not just
5454
// truncate and overwrite it, since it might be a shared hard-link, the
55-
// underlying data of which we don't want to modify
55+
// underlying data of which we don't want to modify.
56+
//
57+
// We have to ensure we have dropped the memory maps to this file
58+
// before performing this removal.
5659
match fs::remove_file(&path_buf) {
5760
Ok(()) => {
5861
debug!("save: remove old file");
@@ -114,6 +117,12 @@ pub fn read_file(
114117
Err(err) if err.kind() == io::ErrorKind::NotFound => return Ok(None),
115118
Err(err) => return Err(err),
116119
};
120+
// SAFETY: This process must not modify nor remove the backing file while the memory map lives.
121+
// For the dep-graph and the work product index, it is as soon as the decoding is done.
122+
// For the query result cache, the memory map is dropped in save_dep_graph before calling
123+
// save_in and trying to remove the backing file.
124+
//
125+
// There is no way to prevent another process from modifying this file.
117126
let mmap = unsafe { Mmap::map(file) }?;
118127

119128
let mut file = io::Cursor::new(&*mmap);

compiler/rustc_incremental/src/persist/save.rs

+5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
4242
join(
4343
move || {
4444
sess.time("incr_comp_persist_result_cache", || {
45+
// Drop the memory map so that we can remove the file and write to it.
46+
if let Some(odc) = &tcx.on_disk_cache {
47+
odc.drop_serialized_data(tcx);
48+
}
49+
4550
file_format::save_in(sess, query_cache_path, "query cache", |e| {
4651
encode_query_cache(tcx, e)
4752
});

compiler/rustc_middle/src/ty/context.rs

+2
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ pub trait OnDiskCache<'tcx>: rustc_data_structures::sync::Sync {
101101
fn register_reused_dep_node(&self, tcx: TyCtxt<'tcx>, dep_node: &DepNode);
102102
fn store_foreign_def_id_hash(&self, def_id: DefId, hash: DefPathHash);
103103

104+
fn drop_serialized_data(&self, tcx: TyCtxt<'tcx>);
105+
104106
fn serialize(&self, tcx: TyCtxt<'tcx>, encoder: &mut FileEncoder) -> FileEncodeResult;
105107
}
106108

compiler/rustc_query_impl/src/on_disk_cache.rs

+27-25
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::QueryCtxt;
22
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
33
use rustc_data_structures::memmap::Mmap;
4-
use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, OnceCell};
4+
use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, OnceCell, RwLock};
55
use rustc_data_structures::unhash::UnhashMap;
66
use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, StableCrateId, LOCAL_CRATE};
77
use rustc_hir::definitions::DefPathHash;
@@ -43,7 +43,7 @@ const TAG_EXPN_DATA: u8 = 1;
4343
/// any side effects that have been emitted during a query.
4444
pub struct OnDiskCache<'sess> {
4545
// The complete cache data in serialized form.
46-
serialized_data: Option<Mmap>,
46+
serialized_data: RwLock<Option<Mmap>>,
4747

4848
// Collects all `QuerySideEffects` created during the current compilation
4949
// session.
@@ -206,7 +206,7 @@ impl<'sess> rustc_middle::ty::OnDiskCache<'sess> for OnDiskCache<'sess> {
206206
};
207207

208208
Self {
209-
serialized_data: Some(data),
209+
serialized_data: RwLock::new(Some(data)),
210210
file_index_to_stable_id: footer.file_index_to_stable_id,
211211
file_index_to_file: Default::default(),
212212
cnum_map: OnceCell::new(),
@@ -227,7 +227,7 @@ impl<'sess> rustc_middle::ty::OnDiskCache<'sess> for OnDiskCache<'sess> {
227227

228228
fn new_empty(source_map: &'sess SourceMap) -> Self {
229229
Self {
230-
serialized_data: None,
230+
serialized_data: RwLock::new(None),
231231
file_index_to_stable_id: Default::default(),
232232
file_index_to_file: Default::default(),
233233
cnum_map: OnceCell::new(),
@@ -246,7 +246,26 @@ impl<'sess> rustc_middle::ty::OnDiskCache<'sess> for OnDiskCache<'sess> {
246246
}
247247
}
248248

249-
fn serialize(&self, tcx: TyCtxt<'sess>, encoder: &mut FileEncoder) -> FileEncodeResult {
249+
fn drop_serialized_data(&self, tcx: TyCtxt<'tcx>) {
250+
// Register any dep nodes that we reused from the previous session,
251+
// but didn't `DepNode::construct` in this session. This ensures
252+
// that their `DefPathHash` to `RawDefId` mappings are registered
253+
// in 'latest_foreign_def_path_hashes' if necessary, since that
254+
// normally happens in `DepNode::construct`.
255+
tcx.dep_graph.register_reused_dep_nodes(tcx);
256+
257+
// Load everything into memory so we can write it out to the on-disk
258+
// cache. The vast majority of cacheable query results should already
259+
// be in memory, so this should be a cheap operation.
260+
// Do this *before* we clone 'latest_foreign_def_path_hashes', since
261+
// loading existing queries may cause us to create new DepNodes, which
262+
// may in turn end up invoking `store_foreign_def_id_hash`
263+
tcx.dep_graph.exec_cache_promotions(QueryCtxt::from_tcx(tcx));
264+
265+
*self.serialized_data.write() = None;
266+
}
267+
268+
fn serialize<'tcx>(&self, tcx: TyCtxt<'tcx>, encoder: &mut FileEncoder) -> FileEncodeResult {
250269
// Serializing the `DepGraph` should not modify it.
251270
tcx.dep_graph.with_ignore(|| {
252271
// Allocate `SourceFileIndex`es.
@@ -268,21 +287,6 @@ impl<'sess> rustc_middle::ty::OnDiskCache<'sess> for OnDiskCache<'sess> {
268287
(file_to_file_index, file_index_to_stable_id)
269288
};
270289

271-
// Register any dep nodes that we reused from the previous session,
272-
// but didn't `DepNode::construct` in this session. This ensures
273-
// that their `DefPathHash` to `RawDefId` mappings are registered
274-
// in 'latest_foreign_def_path_hashes' if necessary, since that
275-
// normally happens in `DepNode::construct`.
276-
tcx.dep_graph.register_reused_dep_nodes(tcx);
277-
278-
// Load everything into memory so we can write it out to the on-disk
279-
// cache. The vast majority of cacheable query results should already
280-
// be in memory, so this should be a cheap operation.
281-
// Do this *before* we clone 'latest_foreign_def_path_hashes', since
282-
// loading existing queries may cause us to create new DepNodes, which
283-
// may in turn end up invoking `store_foreign_def_id_hash`
284-
tcx.dep_graph.exec_cache_promotions(QueryCtxt::from_tcx(tcx));
285-
286290
let latest_foreign_def_path_hashes = self.latest_foreign_def_path_hashes.lock().clone();
287291
let hygiene_encode_context = HygieneEncodeContext::default();
288292

@@ -566,7 +570,7 @@ impl<'sess> OnDiskCache<'sess> {
566570
})
567571
}
568572

569-
fn with_decoder<'a, 'tcx, T, F: FnOnce(&mut CacheDecoder<'sess, 'tcx>) -> T>(
573+
fn with_decoder<'a, 'tcx, T, F: for<'s> FnOnce(&mut CacheDecoder<'s, 'tcx>) -> T>(
570574
&'sess self,
571575
tcx: TyCtxt<'tcx>,
572576
pos: AbsoluteBytePos,
@@ -577,12 +581,10 @@ impl<'sess> OnDiskCache<'sess> {
577581
{
578582
let cnum_map = self.cnum_map.get_or_init(|| Self::compute_cnum_map(tcx));
579583

584+
let serialized_data = self.serialized_data.read();
580585
let mut decoder = CacheDecoder {
581586
tcx,
582-
opaque: opaque::Decoder::new(
583-
self.serialized_data.as_deref().unwrap_or(&[]),
584-
pos.to_usize(),
585-
),
587+
opaque: opaque::Decoder::new(serialized_data.as_deref().unwrap_or(&[]), pos.to_usize()),
586588
source_map: self.source_map,
587589
cnum_map,
588590
file_index_to_file: &self.file_index_to_file,

0 commit comments

Comments
 (0)