Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make AllocId decoding thread-safe #50957

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
60 changes: 28 additions & 32 deletions src/librustc/mir/interpret/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ pub use self::error::{EvalError, EvalResult, EvalErrorKind, AssertMessage};

pub use self::value::{PrimVal, PrimValKind, Value, Pointer, ConstValue};

use std::collections::hash_map::Entry;
use std::fmt;
use mir;
use hir::def_id::DefId;
Expand Down Expand Up @@ -195,7 +194,21 @@ where
assert!(cache(encoder).insert(alloc_id, pos).is_none());
trace!("encoding {:?} with {:#?}", alloc_id, alloc);
AllocKind::Alloc.encode(encoder)?;

// Write placeholder for size
let size_pos = encoder.position();
0usize.encode(encoder)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work because of variable-length integer encoding.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rustc has some similar code elsewhere and works around this by using a 4 byte array that the size is encoded into. Somewhat space-wasteful though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be simpler to just remember the in the global_cache during decoding?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that doesn't work, because we need this value also when another thread hasn't finished decoding the allocation yet.


let start = encoder.position();
alloc.encode(encoder)?;
let end = encoder.position();

// Overwrite the placeholder with the real size
let size: usize = end - start;
encoder.set_position(size_pos);
size.encode(encoder)?;
encoder.set_position(end);

}
AllocType::Function(fn_instance) => {
trace!("encoding {:?} with {:#?}", alloc_id, fn_instance);
Expand Down Expand Up @@ -230,40 +243,23 @@ pub fn specialized_decode_alloc_id<
decoder.with_position(real_pos, AllocId::decode)
},
AllocKind::Alloc => {
let alloc_id = {
let mut cache = global_cache(decoder);
let entry = cache.entry(pos);
match entry {
Entry::Occupied(occupied) => {
let id = occupied.get().0;

// If the alloc id is fully loaded we just return here.
if occupied.get().1 {
return Ok(id)
}

// It was only partially loaded.
// This may be loading further up the stack
// or concurrently in another thread.
id
}
Entry::Vacant(vacant) => {
// Insert early to allow recursive allocs
let id = tcx.alloc_map.lock().reserve();
vacant.insert((id, false));
id
}
}
};

// Insert early to allow recursive allocs and ot indicate that the current
// session will eventually fully load this alloc id
if !local_cache(decoder).insert(alloc_id) {
// Read the size of the allocation.
// Used to skip ahead if we don't need to decode this.
let alloc_size = usize::decode(decoder)?;

let (alloc_id, fully_loaded) = *global_cache(decoder).entry(pos).or_insert_with(|| {
// Create an id which is not fully loaded
(tcx.alloc_map.lock().reserve(), false)
});
if fully_loaded || !local_cache(decoder).insert(alloc_id) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if one thread tstarts decoding, the next thread takes over the CPU, gets here for the same AllocId, skips over and tries to access the allocation? It'll ICE about uncached alloc or error with dangling pointer, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the fully_loaded case this isn't a problem since the AllocId has an Allocation assigned. For the !local_cache(decoder).insert(alloc_id) case, we know that some stack frame above us will assign an AllocId before the result will be used. Since local_cache is thread local another thread won't see the value inserted here. It may instead decode the same allocation in parallel.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, neat. Please make this explanation a comment on that if statement

// We have started decoding this alloc id already, so just return it.
// Its content is already filled in or will be filled in by functions
// further up the stack.
return Ok(alloc_id);


// Skip the allocation
let pos = decoder.position();
decoder.set_position(pos + alloc_size);
return Ok(alloc_id)
}

let allocation = <&'tcx Allocation as Decodable>::decode(decoder)?;
Expand Down
7 changes: 7 additions & 0 deletions src/librustc/ty/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,18 @@ impl<'tcx> EncodableWithShorthand for ty::Predicate<'tcx> {

pub trait TyEncoder: Encoder {
fn position(&self) -> usize;
fn set_position(&mut self, usize);
}

impl<'buf> TyEncoder for opaque::Encoder<'buf> {
#[inline]
fn position(&self) -> usize {
self.position()
}
#[inline]
fn set_position(&mut self, p: usize) {
self.cursor.set_position(p as u64)
}
}

/// Encode the given value or a previously cached shorthand.
Expand Down Expand Up @@ -123,6 +128,8 @@ pub trait TyDecoder<'a, 'tcx: 'a>: Decoder {

fn position(&self) -> usize;

fn set_position(&mut self, usize);

fn cached_ty_for_shorthand<F>(&mut self,
shorthand: usize,
or_insert_with: F)
Expand Down
9 changes: 9 additions & 0 deletions src/librustc/ty/maps/on_disk_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,11 @@ impl<'a, 'tcx: 'a, 'x> ty_codec::TyDecoder<'a, 'tcx> for CacheDecoder<'a, 'tcx,
self.opaque.position()
}

#[inline]
fn set_position(&mut self, p: usize) {
self.opaque.set_position(p)
}

#[inline]
fn peek_byte(&self) -> u8 {
self.opaque.data[self.opaque.position()]
Expand Down Expand Up @@ -860,6 +865,10 @@ impl<'enc, 'a, 'tcx, E> ty_codec::TyEncoder for CacheEncoder<'enc, 'a, 'tcx, E>
fn position(&self) -> usize {
self.encoder.position()
}
#[inline]
fn set_position(&mut self, p: usize) {
self.encoder.set_position(p)
}
}

impl<'enc, 'a, 'tcx, E> SpecializedEncoder<CrateNum> for CacheEncoder<'enc, 'a, 'tcx, E>
Expand Down
5 changes: 5 additions & 0 deletions src/librustc_metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ impl<'a, 'tcx: 'a> TyDecoder<'a, 'tcx> for DecodeContext<'a, 'tcx> {
self.opaque.position()
}

#[inline]
fn set_position(&mut self, p: usize) {
self.opaque.set_position(p)
}

fn cached_ty_for_shorthand<F>(&mut self,
shorthand: usize,
or_insert_with: F)
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ impl<'a, 'tcx> TyEncoder for EncodeContext<'a, 'tcx> {
fn position(&self) -> usize {
self.opaque.position()
}
fn set_position(&mut self, p: usize) {
self.opaque.set_position(p)
}
}

impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
Expand Down