]> git.lizzy.rs Git - rust.git/blob - src/tls.rs
992e4fd05619045c4f55a52f17898580d6c8e528
[rust.git] / src / tls.rs
1 use std::collections::BTreeMap;
2
3 use rustc_target::abi::LayoutOf;
4 use rustc::{ty, ty::layout::HasDataLayout, mir};
5
6 use crate::{
7     EvalResult, InterpError, StackPopCleanup,
8     MPlaceTy, Scalar, Borrow,
9 };
10
11 pub type TlsKey = u128;
12
13 #[derive(Copy, Clone, Debug)]
14 pub struct TlsEntry<'tcx> {
15     pub(crate) data: Scalar<Borrow>, // Will eventually become a map from thread IDs to `Scalar`s, if we ever support more than one thread.
16     pub(crate) dtor: Option<ty::Instance<'tcx>>,
17 }
18
19 #[derive(Debug)]
20 pub struct TlsData<'tcx> {
21     /// The Key to use for the next thread-local allocation.
22     pub(crate) next_key: TlsKey,
23
24     /// pthreads-style thread-local storage.
25     pub(crate) keys: BTreeMap<TlsKey, TlsEntry<'tcx>>,
26 }
27
28 impl<'tcx> Default for TlsData<'tcx> {
29     fn default() -> Self {
30         TlsData {
31             next_key: 1, // start with 1 as we must not use 0 on Windows
32             keys: Default::default(),
33         }
34     }
35 }
36
37 impl<'tcx> TlsData<'tcx> {
38     pub fn create_tls_key(
39         &mut self,
40         dtor: Option<ty::Instance<'tcx>>,
41         cx: &impl HasDataLayout,
42     ) -> TlsKey {
43         let new_key = self.next_key;
44         self.next_key += 1;
45         self.keys.insert(
46             new_key,
47             TlsEntry {
48                 data: Scalar::ptr_null(cx).into(),
49                 dtor,
50             },
51         );
52         trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor);
53         new_key
54     }
55
56     pub fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx> {
57         match self.keys.remove(&key) {
58             Some(_) => {
59                 trace!("TLS key {} removed", key);
60                 Ok(())
61             }
62             None => err!(TlsOutOfBounds),
63         }
64     }
65
66     pub fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Scalar<Borrow>> {
67         match self.keys.get(&key) {
68             Some(&TlsEntry { data, .. }) => {
69                 trace!("TLS key {} loaded: {:?}", key, data);
70                 Ok(data)
71             }
72             None => err!(TlsOutOfBounds),
73         }
74     }
75
76     pub fn store_tls(&mut self, key: TlsKey, new_data: Scalar<Borrow>) -> EvalResult<'tcx> {
77         match self.keys.get_mut(&key) {
78             Some(&mut TlsEntry { ref mut data, .. }) => {
79                 trace!("TLS key {} stored: {:?}", key, new_data);
80                 *data = new_data;
81                 Ok(())
82             }
83             None => err!(TlsOutOfBounds),
84         }
85     }
86
87     /// Returns a dtor, its argument and its index, if one is supposed to run
88     ///
89     /// An optional destructor function may be associated with each key value.
90     /// At thread exit, if a key value has a non-NULL destructor pointer,
91     /// and the thread has a non-NULL value associated with that key,
92     /// the value of the key is set to NULL, and then the function pointed
93     /// to is called with the previously associated value as its sole argument.
94     /// The order of destructor calls is unspecified if more than one destructor
95     /// exists for a thread when it exits.
96     ///
97     /// If, after all the destructors have been called for all non-NULL values
98     /// with associated destructors, there are still some non-NULL values with
99     /// associated destructors, then the process is repeated.
100     /// If, after at least {PTHREAD_DESTRUCTOR_ITERATIONS} iterations of destructor
101     /// calls for outstanding non-NULL values, there are still some non-NULL values
102     /// with associated destructors, implementations may stop calling destructors,
103     /// or they may continue calling destructors until no non-NULL values with
104     /// associated destructors exist, even though this might result in an infinite loop.
105     fn fetch_tls_dtor(
106         &mut self,
107         key: Option<TlsKey>,
108         cx: &impl HasDataLayout,
109     ) -> Option<(ty::Instance<'tcx>, Scalar<Borrow>, TlsKey)> {
110         use std::collections::Bound::*;
111
112         let thread_local = &mut self.keys;
113         let start = match key {
114             Some(key) => Excluded(key),
115             None => Unbounded,
116         };
117         for (&key, &mut TlsEntry { ref mut data, dtor }) in
118             thread_local.range_mut((start, Unbounded))
119         {
120             if !data.is_null_ptr(cx) {
121                 if let Some(dtor) = dtor {
122                     let ret = Some((dtor, *data, key));
123                     *data = Scalar::ptr_null(cx);
124                     return ret;
125                 }
126             }
127         }
128         None
129     }
130 }
131
132 impl<'a, 'mir, 'tcx> EvalContextExt<'a, 'mir, 'tcx> for crate::MiriEvalContext<'a, 'mir, 'tcx> {}
133 pub trait EvalContextExt<'a, 'mir, 'tcx: 'a+'mir>: crate::MiriEvalContextExt<'a, 'mir, 'tcx> {
134     fn run_tls_dtors(&mut self) -> EvalResult<'tcx> {
135         let this = self.eval_context_mut();
136         let mut dtor = this.machine.tls.fetch_tls_dtor(None, &*this.tcx);
137         // FIXME: replace loop by some structure that works with stepping
138         while let Some((instance, ptr, key)) = dtor {
139             trace!("Running TLS dtor {:?} on {:?}", instance, ptr);
140             // TODO: Potentially, this has to support all the other possible instances?
141             // See eval_fn_call in interpret/terminator/mod.rs
142             let mir = this.load_mir(instance.def)?;
143             let ret_place = MPlaceTy::dangling(this.layout_of(this.tcx.mk_unit())?, this).into();
144             this.push_stack_frame(
145                 instance,
146                 mir.span,
147                 mir,
148                 Some(ret_place),
149                 StackPopCleanup::None { cleanup: true },
150             )?;
151             let arg_local = this.frame().mir.args_iter().next().ok_or_else(
152                 || InterpError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()),
153             )?;
154             let dest = this.eval_place(&mir::Place::Base(mir::PlaceBase::Local(arg_local)))?;
155             this.write_scalar(ptr, dest)?;
156
157             // step until out of stackframes
158             this.run()?;
159
160             dtor = match this.machine.tls.fetch_tls_dtor(Some(key), &*this.tcx) {
161                 dtor @ Some(_) => dtor,
162                 None => this.machine.tls.fetch_tls_dtor(None, &*this.tcx),
163             };
164         }
165         // FIXME: On a windows target, call `unsafe extern "system" fn on_tls_callback`.
166         Ok(())
167     }
168 }