Skip to content
/ rust Public
forked from rust-lang/rust

Commit dceda15

Browse files
authored
Rollup merge of rust-lang#157620 - Daniel-B-Smith:smithdb3/fileencoder-strategy, r=cjgillot
Add a strategy FnMut to inject behavior into the flush cycle This PR adds the interface needed to avoid forking FileEncoder in rust-lang#154724. This adds a FnMut used as a strategy/observer to see the bytes being flushed to disk. For rust-lang#154724, that would be used to calculate the metadata hash on the raw metadata bytes. The 'static hack is pretty ugly, but otherwise, the FileEncoder lifetime goes exceptionally viral and requires some very unfortunate workarounds to avoid self referential lifetimes. That's a bit of a timebomb if the wrong FileEncoder client tries to use the new API, but the worst case outcome is a wild goose chase of lifetime chasing. Full disclosure: I used Claude Code to generate much of the PR. I have fully reviewed the code and stand behind it. r? @cjgillot
2 parents f191bd3 + 6324b4a commit dceda15

12 files changed

Lines changed: 81 additions & 24 deletions

File tree

compiler/rustc_data_structures/src/marker.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ already_send!(
6464
[std::io::Error][std::fs::File][std::panic::Location<'_>][rustc_arena::DroplessArena]
6565
[jobserver_crate::Client][jobserver_crate::HelperThread][crate::memmap::Mmap]
6666
[crate::profiling::SelfProfiler][crate::owned_slice::OwnedSlice]
67+
[rustc_serialize::opaque::FileEncoder<'_>]
6768
);
6869

6970
#[cfg(target_has_atomic = "64")]

compiler/rustc_incremental/src/persist/file_format.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ const FILE_MAGIC: &[u8] = b"RSIC";
2828
/// Change this if the header format changes.
2929
const HEADER_FORMAT_VERSION: u16 = 0;
3030

31-
pub(crate) fn write_file_header(stream: &mut FileEncoder, sess: &Session) {
31+
pub(crate) fn write_file_header(stream: &mut FileEncoder<'_>, sess: &Session) {
3232
stream.emit_raw_bytes(FILE_MAGIC);
3333
stream.emit_raw_bytes(&u16::to_le_bytes(HEADER_FORMAT_VERSION));
3434

@@ -41,7 +41,7 @@ pub(crate) fn write_file_header(stream: &mut FileEncoder, sess: &Session) {
4141

4242
pub(crate) fn save_in<F>(sess: &Session, path_buf: PathBuf, name: &str, encode: F)
4343
where
44-
F: FnOnce(FileEncoder) -> FileEncodeResult,
44+
F: FnOnce(FileEncoder<'static>) -> FileEncodeResult,
4545
{
4646
debug!("save: storing data in {}", path_buf.display());
4747

compiler/rustc_incremental/src/persist/save.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ pub fn save_work_product_index(
131131
});
132132
}
133133

134-
fn encode_work_product_index(work_products: &WorkProductMap, encoder: &mut FileEncoder) {
134+
fn encode_work_product_index(work_products: &WorkProductMap, encoder: &mut FileEncoder<'_>) {
135135
let serialized_products: Vec<_> = work_products
136136
.to_sorted_stable_ord()
137137
.into_iter()

compiler/rustc_metadata/src/rmeta/encoder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use crate::errors::{FailCreateFileEncoder, FailWriteFile};
4040
use crate::rmeta::*;
4141

4242
pub(super) struct EncodeContext<'a, 'tcx> {
43-
opaque: opaque::FileEncoder,
43+
opaque: opaque::FileEncoder<'a>,
4444
tcx: TyCtxt<'tcx>,
4545
feat: &'tcx rustc_feature::Features,
4646
tables: TableBuilders,

compiler/rustc_metadata/src/rmeta/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ macro_rules! define_tables {
371371
}
372372

373373
impl TableBuilders {
374-
fn encode(&self, buf: &mut FileEncoder) -> LazyTables {
374+
fn encode(&self, buf: &mut FileEncoder<'_>) -> LazyTables {
375375
LazyTables {
376376
$($name1: self.$name1.encode(buf),)+
377377
$($name2: self.$name2.encode(buf),)+

compiler/rustc_metadata/src/rmeta/table.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>> TableBui
487487
}
488488
}
489489

490-
pub(crate) fn encode(&self, buf: &mut FileEncoder) -> LazyTable<I, T> {
490+
pub(crate) fn encode(&self, buf: &mut FileEncoder<'_>) -> LazyTable<I, T> {
491491
let pos = buf.position();
492492

493493
let width = self.width;

compiler/rustc_middle/src/dep_graph/graph.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ impl DepGraph {
135135
session: &Session,
136136
prev_graph: Arc<SerializedDepGraph>,
137137
prev_work_products: WorkProductMap,
138-
encoder: FileEncoder,
138+
encoder: FileEncoder<'static>,
139139
) -> DepGraph {
140140
let prev_graph_node_count = prev_graph.node_count();
141141

@@ -1137,7 +1137,7 @@ impl CurrentDepGraph {
11371137
fn new(
11381138
session: &Session,
11391139
prev_graph_node_count: usize,
1140-
encoder: FileEncoder,
1140+
encoder: FileEncoder<'static>,
11411141
previous: Arc<SerializedDepGraph>,
11421142
) -> Self {
11431143
let mut stable_hasher = StableHasher::new();

compiler/rustc_middle/src/dep_graph/serialized.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -564,13 +564,17 @@ struct LocalEncoderResult {
564564
struct EncoderState {
565565
next_node_index: AtomicU64,
566566
previous: Arc<SerializedDepGraph>,
567-
file: Lock<Option<FileEncoder>>,
567+
file: Lock<Option<FileEncoder<'static>>>,
568568
local: WorkerLocal<RefCell<LocalEncoderState>>,
569569
stats: Option<Lock<FxHashMap<DepKind, Stat>>>,
570570
}
571571

572572
impl EncoderState {
573-
fn new(encoder: FileEncoder, record_stats: bool, previous: Arc<SerializedDepGraph>) -> Self {
573+
fn new(
574+
encoder: FileEncoder<'static>,
575+
record_stats: bool,
576+
previous: Arc<SerializedDepGraph>,
577+
) -> Self {
574578
Self {
575579
previous,
576580
next_node_index: AtomicU64::new(0),
@@ -863,7 +867,7 @@ pub(crate) struct GraphEncoder {
863867
impl GraphEncoder {
864868
pub(crate) fn new(
865869
sess: &Session,
866-
encoder: FileEncoder,
870+
encoder: FileEncoder<'static>,
867871
prev_node_count: usize,
868872
previous: Arc<SerializedDepGraph>,
869873
) -> Self {

compiler/rustc_middle/src/query/on_disk_cache.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ impl OnDiskCache {
201201

202202
/// Serialize the current-session data that will be loaded by [`OnDiskCache`]
203203
/// in a subsequent incremental compilation session.
204-
pub fn serialize(tcx: TyCtxt<'_>, encoder: FileEncoder) -> FileEncodeResult {
204+
pub fn serialize(tcx: TyCtxt<'_>, encoder: FileEncoder<'static>) -> FileEncodeResult {
205205
// Serializing the `DepGraph` should not modify it.
206206
tcx.dep_graph.with_ignore(|| {
207207
// Allocate `SourceFileIndex`es.
@@ -779,7 +779,7 @@ impl_ref_decoder! {<'tcx>
779779
/// An encoder that can write to the incremental compilation cache.
780780
pub struct CacheEncoder<'a, 'tcx> {
781781
tcx: TyCtxt<'tcx>,
782-
encoder: FileEncoder,
782+
encoder: FileEncoder<'static>,
783783
type_shorthands: FxHashMap<Ty<'tcx>, usize>,
784784
predicate_shorthands: FxHashMap<ty::PredicateKind<'tcx>, usize>,
785785
interpret_allocs: FxIndexSet<interpret::AllocId>,

compiler/rustc_serialize/src/opaque.rs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ const BUF_SIZE: usize = 64 * 1024;
2929
/// `Vec`. `FileEncoder` is better because its memory use is determined by the
3030
/// size of the buffer, rather than the full length of the encoded data, and
3131
/// because it doesn't need to reallocate memory along the way.
32-
pub struct FileEncoder {
32+
///
33+
/// The `'a` lifetime is the borrow of the optional flush strategy (see
34+
/// `flush_strategy`); it is unused (`'static`) for encoders created without one.
35+
pub struct FileEncoder<'a> {
3336
// The input buffer. For adequate performance, we need to be able to write
3437
// directly to the unwritten region of the buffer, without calling copy_from_slice.
3538
// Note that our buffer is always initialized so that we can do that direct access
@@ -43,11 +46,12 @@ pub struct FileEncoder {
4346
// comment on `trait Encoder`.
4447
res: Result<(), io::Error>,
4548
path: PathBuf,
49+
flush_strategy: Option<&'a mut (dyn FnMut(&[u8]) + Send)>,
4650
#[cfg(debug_assertions)]
4751
finished: bool,
4852
}
4953

50-
impl FileEncoder {
54+
impl<'a> FileEncoder<'a> {
5155
pub fn new<P: AsRef<Path>>(path: P) -> io::Result<Self> {
5256
// File::create opens the file for writing only. When -Zmeta-stats is enabled, the metadata
5357
// encoder rewinds the file to inspect what was written. So we need to always open the file
@@ -62,11 +66,21 @@ impl FileEncoder {
6266
flushed: 0,
6367
file,
6468
res: Ok(()),
69+
flush_strategy: None,
6570
#[cfg(debug_assertions)]
6671
finished: false,
6772
})
6873
}
6974

75+
pub fn with_flush_strategy<P: AsRef<Path>>(
76+
path: P,
77+
strategy: &'a mut (dyn FnMut(&[u8]) + Send),
78+
) -> io::Result<Self> {
79+
let mut encoder = Self::new(path)?;
80+
encoder.flush_strategy = Some(strategy);
81+
Ok(encoder)
82+
}
83+
7084
#[inline]
7185
pub fn position(&self) -> usize {
7286
// Tracking position this way instead of having a `self.position` field
@@ -86,6 +100,9 @@ impl FileEncoder {
86100
self.res = self.file.write_all(&self.buf[..self.buffered]);
87101
}
88102
self.flushed += self.buffered;
103+
if let Some(f) = &mut self.flush_strategy {
104+
f(&self.buf[..self.buffered]);
105+
}
89106
self.buffered = 0;
90107
}
91108

@@ -115,6 +132,9 @@ impl FileEncoder {
115132
} else {
116133
if self.res.is_ok() {
117134
self.res = self.file.write_all(buf);
135+
if let Some(f) = &mut self.flush_strategy {
136+
f(buf);
137+
}
118138
}
119139
self.flushed += buf.len();
120140
}
@@ -200,7 +220,7 @@ impl FileEncoder {
200220
}
201221

202222
#[cfg(debug_assertions)]
203-
impl Drop for FileEncoder {
223+
impl Drop for FileEncoder<'_> {
204224
fn drop(&mut self) {
205225
if !std::thread::panicking() {
206226
assert!(self.finished);
@@ -217,7 +237,7 @@ macro_rules! write_leb128 {
217237
};
218238
}
219239

220-
impl Encoder for FileEncoder {
240+
impl Encoder for FileEncoder<'_> {
221241
write_leb128!(emit_usize, usize, write_usize_leb128);
222242
write_leb128!(emit_u128, u128, write_u128_leb128);
223243
write_leb128!(emit_u64, u64, write_u64_leb128);
@@ -415,8 +435,8 @@ impl<'a> Decoder for MemDecoder<'a> {
415435

416436
// Specialize encoding byte slices. This specialization also applies to encoding `Vec<u8>`s, etc.,
417437
// since the default implementations call `encode` on their slices internally.
418-
impl Encodable<FileEncoder> for [u8] {
419-
fn encode(&self, e: &mut FileEncoder) {
438+
impl Encodable<FileEncoder<'_>> for [u8] {
439+
fn encode(&self, e: &mut FileEncoder<'_>) {
420440
Encoder::emit_usize(e, self.len());
421441
e.emit_raw_bytes(self);
422442
}
@@ -438,9 +458,9 @@ impl IntEncodedWithFixedSize {
438458
pub const ENCODED_SIZE: usize = 8;
439459
}
440460

441-
impl Encodable<FileEncoder> for IntEncodedWithFixedSize {
461+
impl Encodable<FileEncoder<'_>> for IntEncodedWithFixedSize {
442462
#[inline]
443-
fn encode(&self, e: &mut FileEncoder) {
463+
fn encode(&self, e: &mut FileEncoder<'_>) {
444464
let start_pos = e.position();
445465
e.write_array(self.0.to_le_bytes());
446466
let end_pos = e.position();

0 commit comments

Comments
 (0)