Skip to content

Commit 24dfcbe

Browse files
Make const decoding from the incremental cache thread-safe.
1 parent f9f90ed commit 24dfcbe

File tree

4 files changed

+181
-42
lines changed

4 files changed

+181
-42
lines changed

src/librustc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
#![feature(trace_macros)]
6969
#![feature(trusted_len)]
7070
#![feature(catch_expr)]
71+
#![feature(integer_atomics)]
7172
#![feature(test)]
7273
#![feature(in_band_lifetimes)]
7374
#![feature(macro_at_most_once_rep)]

src/librustc/mir/interpret/mod.rs

+169
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@ use syntax::ast::Mutability;
2626
use rustc_serialize::{Encoder, Decoder, Decodable, Encodable};
2727
use rustc_data_structures::sorted_map::SortedMap;
2828
use rustc_data_structures::fx::FxHashMap;
29+
use rustc_data_structures::sync::{Lock as Mutex, HashMapExt};
30+
use rustc_data_structures::tiny_list::TinyList;
2931
use byteorder::{WriteBytesExt, ReadBytesExt, LittleEndian, BigEndian};
32+
use ty::codec::TyDecoder;
33+
use std::sync::atomic::{AtomicU32, Ordering};
34+
use std::num::NonZeroU32;
3035

3136
#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
3237
pub enum Lock {
@@ -245,6 +250,166 @@ pub fn specialized_decode_alloc_id<
245250
}
246251
}
247252

253+
// Used to avoid infinite recursion when decoding cyclic allocations.
254+
type DecodingSessionId = NonZeroU32;
255+
256+
#[derive(Clone)]
257+
enum State {
258+
Empty,
259+
InProgressNonAlloc(TinyList<DecodingSessionId>),
260+
InProgress(TinyList<DecodingSessionId>, AllocId),
261+
Done(AllocId),
262+
}
263+
264+
pub struct AllocDecodingState {
265+
// For each AllocId we keep track of which decoding state it's currently in.
266+
decoding_state: Vec<Mutex<State>>,
267+
// The offsets of each allocation in the data stream.
268+
data_offsets: Vec<u32>,
269+
}
270+
271+
impl AllocDecodingState {
272+
273+
pub fn new_decoding_session(&self) -> AllocDecodingSession {
274+
static DECODER_SESSION_ID: AtomicU32 = AtomicU32::new(0);
275+
let counter = DECODER_SESSION_ID.fetch_add(1, Ordering::SeqCst);
276+
277+
// Make sure this is never zero
278+
let session_id = DecodingSessionId::new((counter & 0x7FFFFFFF) + 1).unwrap();
279+
280+
AllocDecodingSession {
281+
state: self,
282+
session_id,
283+
}
284+
}
285+
286+
pub fn new(data_offsets: Vec<u32>) -> AllocDecodingState {
287+
let decoding_state: Vec<_> = ::std::iter::repeat(Mutex::new(State::Empty))
288+
.take(data_offsets.len())
289+
.collect();
290+
291+
AllocDecodingState {
292+
decoding_state: decoding_state,
293+
data_offsets,
294+
}
295+
}
296+
}
297+
298+
#[derive(Copy, Clone)]
299+
pub struct AllocDecodingSession<'s> {
300+
state: &'s AllocDecodingState,
301+
session_id: DecodingSessionId,
302+
}
303+
304+
impl<'s> AllocDecodingSession<'s> {
305+
306+
// Decodes an AllocId in a thread-safe way.
307+
pub fn decode_alloc_id<'a, 'tcx, D>(&self,
308+
decoder: &mut D)
309+
-> Result<AllocId, D::Error>
310+
where D: TyDecoder<'a, 'tcx>,
311+
'tcx: 'a,
312+
{
313+
// Read the index of the allocation
314+
let idx = decoder.read_u32()? as usize;
315+
let pos = self.state.data_offsets[idx] as usize;
316+
317+
// Decode the AllocKind now so that we know if we have to reserve an
318+
// AllocId.
319+
let (alloc_kind, pos) = decoder.with_position(pos, |decoder| {
320+
let alloc_kind = AllocKind::decode(decoder)?;
321+
Ok((alloc_kind, decoder.position()))
322+
})?;
323+
324+
// Check the decoding state, see if it's already decoded or if we should
325+
// decode it here.
326+
let alloc_id = {
327+
let mut entry = self.state.decoding_state[idx].lock();
328+
329+
match *entry {
330+
State::Done(alloc_id) => {
331+
return Ok(alloc_id);
332+
}
333+
ref mut entry @ State::Empty => {
334+
// We are allowed to decode
335+
match alloc_kind {
336+
AllocKind::Alloc => {
337+
// If this is an allocation, we need to reserve an
338+
// AllocId so we can decode cyclic graphs.
339+
let alloc_id = decoder.tcx().alloc_map.lock().reserve();
340+
*entry = State::InProgress(
341+
TinyList::new_single(self.session_id),
342+
alloc_id);
343+
Some(alloc_id)
344+
},
345+
AllocKind::Fn | AllocKind::Static => {
346+
// Fns and statics cannot be cyclic and their AllocId
347+
// is determined later by interning
348+
*entry = State::InProgressNonAlloc(
349+
TinyList::new_single(self.session_id));
350+
None
351+
}
352+
}
353+
}
354+
State::InProgressNonAlloc(ref mut sessions) => {
355+
if sessions.contains(&self.session_id) {
356+
bug!("This should be unreachable")
357+
} else {
358+
// Start decoding concurrently
359+
sessions.insert(self.session_id);
360+
None
361+
}
362+
}
363+
State::InProgress(ref mut sessions, alloc_id) => {
364+
if sessions.contains(&self.session_id) {
365+
// Don't recurse.
366+
return Ok(alloc_id)
367+
} else {
368+
// Start decoding concurrently
369+
sessions.insert(self.session_id);
370+
Some(alloc_id)
371+
}
372+
}
373+
}
374+
};
375+
376+
// Now decode the actual data
377+
let alloc_id = decoder.with_position(pos, |decoder| {
378+
match alloc_kind {
379+
AllocKind::Alloc => {
380+
let allocation = <&'tcx Allocation as Decodable>::decode(decoder)?;
381+
// We already have a reserved AllocId.
382+
let alloc_id = alloc_id.unwrap();
383+
trace!("decoded alloc {:?} {:#?}", alloc_id, allocation);
384+
decoder.tcx().alloc_map.lock().set_id_same_memory(alloc_id, allocation);
385+
Ok(alloc_id)
386+
},
387+
AllocKind::Fn => {
388+
assert!(alloc_id.is_none());
389+
trace!("creating fn alloc id");
390+
let instance = ty::Instance::decode(decoder)?;
391+
trace!("decoded fn alloc instance: {:?}", instance);
392+
let alloc_id = decoder.tcx().alloc_map.lock().create_fn_alloc(instance);
393+
Ok(alloc_id)
394+
},
395+
AllocKind::Static => {
396+
assert!(alloc_id.is_none());
397+
trace!("creating extern static alloc id at");
398+
let did = DefId::decode(decoder)?;
399+
let alloc_id = decoder.tcx().alloc_map.lock().intern_static(did);
400+
Ok(alloc_id)
401+
}
402+
}
403+
})?;
404+
405+
self.state.decoding_state[idx].with_lock(|entry| {
406+
*entry = State::Done(alloc_id);
407+
});
408+
409+
Ok(alloc_id)
410+
}
411+
}
412+
248413
impl fmt::Display for AllocId {
249414
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250415
write!(f, "{}", self.0)
@@ -340,6 +505,10 @@ impl<'tcx, M: fmt::Debug + Eq + Hash + Clone> AllocMap<'tcx, M> {
340505
bug!("tried to set allocation id {}, but it was already existing as {:#?}", id, old);
341506
}
342507
}
508+
509+
pub fn set_id_same_memory(&mut self, id: AllocId, mem: M) {
510+
self.id_to_type.insert_same(id, AllocType::Memory(mem));
511+
}
343512
}
344513

345514
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]

src/librustc/ty/maps/on_disk_cache.rs

+10-41
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ use hir::def_id::{CrateNum, DefIndex, DefId, LocalDefId,
1616
use hir::map::definitions::DefPathHash;
1717
use ich::{CachingCodemapView, Fingerprint};
1818
use mir::{self, interpret};
19+
use mir::interpret::{AllocDecodingSession, AllocDecodingState};
1920
use rustc_data_structures::fx::FxHashMap;
2021
use rustc_data_structures::sync::{Lrc, Lock, HashMapExt, Once};
2122
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
2223
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque,
2324
SpecializedDecoder, SpecializedEncoder,
2425
UseSpecializedDecodable, UseSpecializedEncodable};
2526
use session::{CrateDisambiguator, Session};
26-
use std::cell::RefCell;
2727
use std::mem;
2828
use syntax::ast::NodeId;
2929
use syntax::codemap::{CodeMap, StableFilemapId};
@@ -77,11 +77,7 @@ pub struct OnDiskCache<'sess> {
7777
// `serialized_data`.
7878
prev_diagnostics_index: FxHashMap<SerializedDepNodeIndex, AbsoluteBytePos>,
7979

80-
// Alloc indices to memory location map
81-
prev_interpret_alloc_index: Vec<AbsoluteBytePos>,
82-
83-
/// Deserialization: A cache to ensure we don't read allocations twice
84-
interpret_alloc_cache: RefCell<FxHashMap<usize, interpret::AllocId>>,
80+
alloc_decoding_state: AllocDecodingState,
8581
}
8682

8783
// This type is used only for (de-)serialization.
@@ -92,7 +88,7 @@ struct Footer {
9288
query_result_index: EncodedQueryResultIndex,
9389
diagnostics_index: EncodedQueryResultIndex,
9490
// the location of all allocations
95-
interpret_alloc_index: Vec<AbsoluteBytePos>,
91+
interpret_alloc_index: Vec<u32>,
9692
}
9793

9894
type EncodedQueryResultIndex = Vec<(SerializedDepNodeIndex, AbsoluteBytePos)>;
@@ -149,8 +145,7 @@ impl<'sess> OnDiskCache<'sess> {
149145
query_result_index: footer.query_result_index.into_iter().collect(),
150146
prev_diagnostics_index: footer.diagnostics_index.into_iter().collect(),
151147
synthetic_expansion_infos: Lock::new(FxHashMap()),
152-
prev_interpret_alloc_index: footer.interpret_alloc_index,
153-
interpret_alloc_cache: RefCell::new(FxHashMap::default()),
148+
alloc_decoding_state: AllocDecodingState::new(footer.interpret_alloc_index),
154149
}
155150
}
156151

@@ -166,8 +161,7 @@ impl<'sess> OnDiskCache<'sess> {
166161
query_result_index: FxHashMap(),
167162
prev_diagnostics_index: FxHashMap(),
168163
synthetic_expansion_infos: Lock::new(FxHashMap()),
169-
prev_interpret_alloc_index: Vec::new(),
170-
interpret_alloc_cache: RefCell::new(FxHashMap::default()),
164+
alloc_decoding_state: AllocDecodingState::new(Vec::new()),
171165
}
172166
}
173167

@@ -291,7 +285,7 @@ impl<'sess> OnDiskCache<'sess> {
291285
}
292286
for idx in n..new_n {
293287
let id = encoder.interpret_allocs_inverse[idx];
294-
let pos = AbsoluteBytePos::new(encoder.position());
288+
let pos = encoder.position() as u32;
295289
interpret_alloc_index.push(pos);
296290
interpret::specialized_encode_alloc_id(
297291
&mut encoder,
@@ -424,8 +418,7 @@ impl<'sess> OnDiskCache<'sess> {
424418
file_index_to_file: &self.file_index_to_file,
425419
file_index_to_stable_id: &self.file_index_to_stable_id,
426420
synthetic_expansion_infos: &self.synthetic_expansion_infos,
427-
prev_interpret_alloc_index: &self.prev_interpret_alloc_index,
428-
interpret_alloc_cache: &self.interpret_alloc_cache,
421+
alloc_decoding_session: self.alloc_decoding_state.new_decoding_session(),
429422
};
430423

431424
match decode_tagged(&mut decoder, dep_node_index) {
@@ -487,9 +480,7 @@ struct CacheDecoder<'a, 'tcx: 'a, 'x> {
487480
synthetic_expansion_infos: &'x Lock<FxHashMap<AbsoluteBytePos, SyntaxContext>>,
488481
file_index_to_file: &'x Lock<FxHashMap<FileMapIndex, Lrc<FileMap>>>,
489482
file_index_to_stable_id: &'x FxHashMap<FileMapIndex, StableFilemapId>,
490-
interpret_alloc_cache: &'x RefCell<FxHashMap<usize, interpret::AllocId>>,
491-
/// maps from index in the cache file to location in the cache file
492-
prev_interpret_alloc_index: &'x [AbsoluteBytePos],
483+
alloc_decoding_session: AllocDecodingSession<'x>,
493484
}
494485

495486
impl<'a, 'tcx, 'x> CacheDecoder<'a, 'tcx, 'x> {
@@ -612,30 +603,8 @@ implement_ty_decoder!( CacheDecoder<'a, 'tcx, 'x> );
612603

613604
impl<'a, 'tcx, 'x> SpecializedDecoder<interpret::AllocId> for CacheDecoder<'a, 'tcx, 'x> {
614605
fn specialized_decode(&mut self) -> Result<interpret::AllocId, Self::Error> {
615-
let tcx = self.tcx;
616-
let idx = usize::decode(self)?;
617-
trace!("loading index {}", idx);
618-
619-
if let Some(cached) = self.interpret_alloc_cache.borrow().get(&idx).cloned() {
620-
trace!("loading alloc id {:?} from alloc_cache", cached);
621-
return Ok(cached);
622-
}
623-
let pos = self.prev_interpret_alloc_index[idx].to_usize();
624-
trace!("loading position {}", pos);
625-
self.with_position(pos, |this| {
626-
interpret::specialized_decode_alloc_id(
627-
this,
628-
tcx,
629-
|this, alloc_id| {
630-
trace!("caching idx {} for alloc id {} at position {}", idx, alloc_id, pos);
631-
assert!(this
632-
.interpret_alloc_cache
633-
.borrow_mut()
634-
.insert(idx, alloc_id)
635-
.is_none());
636-
},
637-
)
638-
})
606+
let alloc_decoding_session = self.alloc_decoding_session;
607+
alloc_decoding_session.decode_alloc_id(self)
639608
}
640609
}
641610
impl<'a, 'tcx, 'x> SpecializedDecoder<Span> for CacheDecoder<'a, 'tcx, 'x> {

src/librustc_data_structures/tiny_list.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ mod test {
174174
}
175175
}
176176

177-
assert!(!list.contains(i));
177+
assert!(!list.contains(&i));
178178

179179
if do_insert(i) {
180180
list.insert(i);

0 commit comments

Comments
 (0)