xref: /relibc/ralloc/src/tls.rs (revision 2cb61efbaa30da912d4f1015c8b2b361005533f0)
1 //! Safe implementation of thread-local storage.
2 //!
3 //! This module provides lightweight abstractions for TLS similar to the ones provided by libstd.
4 
5 use core::{marker, mem};
6 
7 use shim::thread_destructor;
8 
9 /// A thread-local container.
10 pub struct Key<T: 'static> {
11     /// The inner data.
12     inner: T,
13 }
14 
15 impl<T: 'static> Key<T> {
16     /// Create a new `Key` wrapper.
17     ///
18     /// # Safety
19     ///
20     /// This is invariant-breaking (assumes thread-safety) and thus unsafe.
21     pub const unsafe fn new(inner: T) -> Key<T> {
22         Key { inner: inner }
23     }
24 
25     /// Obtain a reference temporarily.
26     ///
27     /// Due to [the lack of thread lifetimes](https://github.com/rust-lang/rfcs/pull/1705#issuecomment-238015901), we use a closure to make sure no leakage happens.
28     ///
29     /// Having a reference newtype would be unsound, due to the ability to leak a reference to
30     /// another thread.
31     #[inline]
32     pub fn with<F, R>(&'static self, f: F) -> R
33         where F: FnOnce(&T) -> R {
34         // Logging.
35         log!(INTERNAL, "Accessing TLS variable.");
36 
37         f(&self.inner)
38     }
39 
40     /// Register a TLS destructor on the current thread.
41     ///
42     /// Note that this has to be registered for every thread, it is needed for.
43     // TODO: Make this automatic on `Drop`.
44     #[inline]
45     pub fn register_thread_destructor(&'static self, dtor: extern fn(&T)) {
46         // Logging.
47         log!(INTERNAL, "Registering thread destructor.");
48 
49         // This is safe due to sharing memory layout.
50         thread_destructor::register(&self.inner as *const T as *const u8 as *mut u8,
51                                     unsafe { mem::transmute(dtor) });
52     }
53 }
54 
55 unsafe impl<T> marker::Sync for Key<T> {}
56 
57 /// Declare a thread-local static variable.
58 ///
59 /// TLS works by copying the initial data on every new thread creation. This allows access to a
60 /// variable, which is only available for the current thread, meaning that there is no need for
61 /// syncronization.
62 ///
63 /// For this reason, in contrast to other `static`s in Rust, this need not thread-safety, which is
64 /// what this macro "fixes".
65 macro_rules! tls {
66     (static $name:ident: $ty:ty = $val:expr;) => { tls! { #[] static $name: $ty = $val; } };
67     (#[$($attr:meta),*] static $name:ident: $ty:ty = $val:expr;) => {
68         $(#[$attr])*
69         #[thread_local]
70         static $name: tls::Key<$ty> = unsafe { tls::Key::new($val) };
71     }
72 }
73