@@ -23,10 +23,15 @@ use std::io;
2323use std:: ops:: { Deref , DerefMut } ;
2424use std:: hash:: Hash ;
2525use syntax:: ast:: Mutability ;
26- use rustc_serialize:: { Encoder , Decoder , Decodable , Encodable } ;
26+ use rustc_serialize:: { Encoder , Decodable , Encodable } ;
2727use rustc_data_structures:: sorted_map:: SortedMap ;
2828use rustc_data_structures:: fx:: FxHashMap ;
29+ use rustc_data_structures:: sync:: { Lock as Mutex , HashMapExt } ;
30+ use rustc_data_structures:: tiny_list:: TinyList ;
2931use 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 ) ]
3237pub enum Lock {
@@ -204,44 +209,163 @@ pub fn specialized_encode_alloc_id<
204209 Ok ( ( ) )
205210}
206211
207- pub fn specialized_decode_alloc_id <
208- ' a , ' tcx ,
209- D : Decoder ,
210- CACHE : FnOnce ( & mut D , AllocId ) ,
211- > (
212- decoder : & mut D ,
213- tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
214- cache : CACHE ,
215- ) -> Result < AllocId , D :: Error > {
216- match AllocKind :: decode ( decoder) ? {
217- AllocKind :: Alloc => {
218- let alloc_id = tcx. alloc_map . lock ( ) . reserve ( ) ;
219- trace ! ( "creating alloc id {:?}" , alloc_id) ;
220- // insert early to allow recursive allocs
221- cache ( decoder, alloc_id) ;
222-
223- let allocation = <& ' tcx Allocation as Decodable >:: decode ( decoder) ?;
224- trace ! ( "decoded alloc {:?} {:#?}" , alloc_id, allocation) ;
225- tcx. alloc_map . lock ( ) . set_id_memory ( alloc_id, allocation) ;
226-
227- Ok ( alloc_id)
228- } ,
229- AllocKind :: Fn => {
230- trace ! ( "creating fn alloc id" ) ;
231- let instance = ty:: Instance :: decode ( decoder) ?;
232- trace ! ( "decoded fn alloc instance: {:?}" , instance) ;
233- let id = tcx. alloc_map . lock ( ) . create_fn_alloc ( instance) ;
234- trace ! ( "created fn alloc id: {:?}" , id) ;
235- cache ( decoder, id) ;
236- Ok ( id)
237- } ,
238- AllocKind :: Static => {
239- trace ! ( "creating extern static alloc id at" ) ;
240- let did = DefId :: decode ( decoder) ?;
241- let alloc_id = tcx. alloc_map . lock ( ) . intern_static ( did) ;
242- cache ( decoder, alloc_id) ;
243- Ok ( alloc_id)
244- } ,
212+ // Used to avoid infinite recursion when decoding cyclic allocations.
213+ type DecodingSessionId = NonZeroU32 ;
214+
215+ #[ derive( Clone ) ]
216+ enum State {
217+ Empty ,
218+ InProgressNonAlloc ( TinyList < DecodingSessionId > ) ,
219+ InProgress ( TinyList < DecodingSessionId > , AllocId ) ,
220+ Done ( AllocId ) ,
221+ }
222+
223+ pub struct AllocDecodingState {
224+ // For each AllocId we keep track of which decoding state it's currently in.
225+ decoding_state : Vec < Mutex < State > > ,
226+ // The offsets of each allocation in the data stream.
227+ data_offsets : Vec < u32 > ,
228+ }
229+
230+ impl AllocDecodingState {
231+
232+ pub fn new_decoding_session ( & self ) -> AllocDecodingSession {
233+ static DECODER_SESSION_ID : AtomicU32 = AtomicU32 :: new ( 0 ) ;
234+ let counter = DECODER_SESSION_ID . fetch_add ( 1 , Ordering :: SeqCst ) ;
235+
236+ // Make sure this is never zero
237+ let session_id = DecodingSessionId :: new ( ( counter & 0x7FFFFFFF ) + 1 ) . unwrap ( ) ;
238+
239+ AllocDecodingSession {
240+ state : self ,
241+ session_id,
242+ }
243+ }
244+
245+ pub fn new ( data_offsets : Vec < u32 > ) -> AllocDecodingState {
246+ let decoding_state: Vec < _ > = :: std:: iter:: repeat ( Mutex :: new ( State :: Empty ) )
247+ . take ( data_offsets. len ( ) )
248+ . collect ( ) ;
249+
250+ AllocDecodingState {
251+ decoding_state : decoding_state,
252+ data_offsets,
253+ }
254+ }
255+ }
256+
257+ #[ derive( Copy , Clone ) ]
258+ pub struct AllocDecodingSession < ' s > {
259+ state : & ' s AllocDecodingState ,
260+ session_id : DecodingSessionId ,
261+ }
262+
263+ impl < ' s > AllocDecodingSession < ' s > {
264+
265+ // Decodes an AllocId in a thread-safe way.
266+ pub fn decode_alloc_id < ' a , ' tcx , D > ( & self ,
267+ decoder : & mut D )
268+ -> Result < AllocId , D :: Error >
269+ where D : TyDecoder < ' a , ' tcx > ,
270+ ' tcx : ' a ,
271+ {
272+ // Read the index of the allocation
273+ let idx = decoder. read_u32 ( ) ? as usize ;
274+ let pos = self . state . data_offsets [ idx] as usize ;
275+
276+ // Decode the AllocKind now so that we know if we have to reserve an
277+ // AllocId.
278+ let ( alloc_kind, pos) = decoder. with_position ( pos, |decoder| {
279+ let alloc_kind = AllocKind :: decode ( decoder) ?;
280+ Ok ( ( alloc_kind, decoder. position ( ) ) )
281+ } ) ?;
282+
283+ // Check the decoding state, see if it's already decoded or if we should
284+ // decode it here.
285+ let alloc_id = {
286+ let mut entry = self . state . decoding_state [ idx] . lock ( ) ;
287+
288+ match * entry {
289+ State :: Done ( alloc_id) => {
290+ return Ok ( alloc_id) ;
291+ }
292+ ref mut entry @ State :: Empty => {
293+ // We are allowed to decode
294+ match alloc_kind {
295+ AllocKind :: Alloc => {
296+ // If this is an allocation, we need to reserve an
297+ // AllocId so we can decode cyclic graphs.
298+ let alloc_id = decoder. tcx ( ) . alloc_map . lock ( ) . reserve ( ) ;
299+ * entry = State :: InProgress (
300+ TinyList :: new_single ( self . session_id ) ,
301+ alloc_id) ;
302+ Some ( alloc_id)
303+ } ,
304+ AllocKind :: Fn | AllocKind :: Static => {
305+ // Fns and statics cannot be cyclic and their AllocId
306+ // is determined later by interning
307+ * entry = State :: InProgressNonAlloc (
308+ TinyList :: new_single ( self . session_id ) ) ;
309+ None
310+ }
311+ }
312+ }
313+ State :: InProgressNonAlloc ( ref mut sessions) => {
314+ if sessions. contains ( & self . session_id ) {
315+ bug ! ( "This should be unreachable" )
316+ } else {
317+ // Start decoding concurrently
318+ sessions. insert ( self . session_id ) ;
319+ None
320+ }
321+ }
322+ State :: InProgress ( ref mut sessions, alloc_id) => {
323+ if sessions. contains ( & self . session_id ) {
324+ // Don't recurse.
325+ return Ok ( alloc_id)
326+ } else {
327+ // Start decoding concurrently
328+ sessions. insert ( self . session_id ) ;
329+ Some ( alloc_id)
330+ }
331+ }
332+ }
333+ } ;
334+
335+ // Now decode the actual data
336+ let alloc_id = decoder. with_position ( pos, |decoder| {
337+ match alloc_kind {
338+ AllocKind :: Alloc => {
339+ let allocation = <& ' tcx Allocation as Decodable >:: decode ( decoder) ?;
340+ // We already have a reserved AllocId.
341+ let alloc_id = alloc_id. unwrap ( ) ;
342+ trace ! ( "decoded alloc {:?} {:#?}" , alloc_id, allocation) ;
343+ decoder. tcx ( ) . alloc_map . lock ( ) . set_id_same_memory ( alloc_id, allocation) ;
344+ Ok ( alloc_id)
345+ } ,
346+ AllocKind :: Fn => {
347+ assert ! ( alloc_id. is_none( ) ) ;
348+ trace ! ( "creating fn alloc id" ) ;
349+ let instance = ty:: Instance :: decode ( decoder) ?;
350+ trace ! ( "decoded fn alloc instance: {:?}" , instance) ;
351+ let alloc_id = decoder. tcx ( ) . alloc_map . lock ( ) . create_fn_alloc ( instance) ;
352+ Ok ( alloc_id)
353+ } ,
354+ AllocKind :: Static => {
355+ assert ! ( alloc_id. is_none( ) ) ;
356+ trace ! ( "creating extern static alloc id at" ) ;
357+ let did = DefId :: decode ( decoder) ?;
358+ let alloc_id = decoder. tcx ( ) . alloc_map . lock ( ) . intern_static ( did) ;
359+ Ok ( alloc_id)
360+ }
361+ }
362+ } ) ?;
363+
364+ self . state . decoding_state [ idx] . with_lock ( |entry| {
365+ * entry = State :: Done ( alloc_id) ;
366+ } ) ;
367+
368+ Ok ( alloc_id)
245369 }
246370}
247371
@@ -340,6 +464,10 @@ impl<'tcx, M: fmt::Debug + Eq + Hash + Clone> AllocMap<'tcx, M> {
340464 bug ! ( "tried to set allocation id {}, but it was already existing as {:#?}" , id, old) ;
341465 }
342466 }
467+
468+ pub fn set_id_same_memory ( & mut self , id : AllocId , mem : M ) {
469+ self . id_to_type . insert_same ( id, AllocType :: Memory ( mem) ) ;
470+ }
343471}
344472
345473#[ derive( Clone , Debug , Eq , PartialEq , PartialOrd , Ord , Hash , RustcEncodable , RustcDecodable ) ]
0 commit comments