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