1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
//! 基于操作系统的线程本地存储 //! //! 该模块提供了一个基于操作系统的线程本地存储的实现,使用了本地操作系统提供的工具 (想想 `TlsAlloc` 或 `pthread_setspecific`)。 //! 它的接口与此 crate 中提供的其他类型的线程本地存储不同,因为基于操作系统的 TLS 只能获取/设置指针大小的数据,可能带有关联的析构函数。 //! //! //! 该模块还提供了两种 TLS。其中一个用于静态初始化,不包含用于释放 OS-TLS 密钥的 `Drop` 实现。 //! 另一个是实现了 `Drop` 的类型,因此有一个安全的接口。 //! //! # Usage //! //! 除非在其他原语的基础上构建,否则不应该直接使用此模块。 //! 像 `thread_local::spawn::Key` 这样的类型在实践中可能比此基于 OS 的版本有用得多,后者可能需要不安全的代码才能进行互操作。 //! //! # Examples //! //! 使用动态分配的 TLS 密钥。请注意,这个键可以通过 `Arc` 在多个线程中共享。 //! //! ```ignore (cannot-doctest-private-modules) //! let key = Key::new(None); //! assert!(key.get().is_null()); //! key.set(1 as *mut u8); //! assert!(!key.get().is_null()); //! //! drop(key); // 释放此 TLS 插槽。 //! ``` //! //! 有时,静态分配的密钥是必需的,或者更易于使用。 //! //! ```ignore (cannot-doctest-private-modules) //! static KEY: StaticKey = INIT; //! //! unsafe { //! assert!(KEY.get().is_null()); //! KEY.set(1 as *mut u8); //! } //! ``` //! //! //! //! //! //! //! //! #![allow(non_camel_case_types)] #![unstable(feature = "thread_local_internals", issue = "none")] #![allow(dead_code)] // sys 尚未导出 #[cfg(test)] mod tests; use crate::sync::atomic::{self, AtomicUsize, Ordering}; use crate::sys::thread_local_key as imp; use crate::sys_common::mutex::StaticMutex; /// 静态分配的 TLS 密钥的类型。 /// /// 使用此类型是完全 `unsafe` 的,因为它不能防止在释放后使用或释放期间使用。 /// /// /// 第一次使用时会延迟分配实际的 OS-TLS 密钥。 /// 当 Rust 运行时退出或调用 `destroy` (以先到者为准) 时,也将释放该键。 /// /// # Examples /// /// ```ignore (cannot-doctest-private-modules) /// use tls::os::{StaticKey, INIT}; /// /// static KEY: StaticKey = INIT; /// /// unsafe { /// assert!(KEY.get().is_null()); /// KEY.set(1 as *mut u8); /// } /// ``` /// pub struct StaticKey { /// 内部静态 TLS 密钥 (internals)。 key: AtomicUsize, /// TLS 值的析构函数。 /// /// 有关析构函数何时运行以及运行方式的信息,请参见 `Key::new`。 /// dtor: Option<unsafe extern "C" fn(*mut u8)>, } /// 一种安全管理的基于 OS 的 TLS 插槽的类型。 /// /// 初始化时,此类型分配 OS TLS 密钥; 当它离开离开作用域时,将释放该密钥。 /// 与 `StaticKey` 相比,此类型完全可以安全使用。 /// /// 但是,实现可能会包含不安全的代码,因为此类型仅在 `*mut u8` (裸指针) 上运行。 /// /// /// # Examples /// /// ```ignore (cannot-doctest-private-modules) /// use tls::os::Key; /// /// let key = Key::new(None); /// assert!(key.get().is_null()); /// key.set(1 as *mut u8); /// assert!(!key.get().is_null()); /// /// drop(key); // 释放此 TLS 插槽。 /// ``` /// pub struct Key { key: imp::Key, } /// 静态 TLS 密钥的常量初始化值。 /// /// 默认情况下,此值不指定析构函数。 pub const INIT: StaticKey = StaticKey::new(None); impl StaticKey { #[rustc_const_unstable(feature = "thread_local_internals", issue = "none")] pub const fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> StaticKey { StaticKey { key: atomic::AtomicUsize::new(0), dtor } } /// 获取与此 TLS 密钥关联的值 /// /// 如果尚未分配 TLS 密钥,则会从操作系统延迟分配 TLS 密钥。 /// #[inline] pub unsafe fn get(&self) -> *mut u8 { imp::get(self.key()) } /// 将此 TLS 密钥设置为新值。 /// /// 如果尚未分配 TLS 密钥,则会从操作系统延迟分配 TLS 密钥。 /// #[inline] pub unsafe fn set(&self, val: *mut u8) { imp::set(self.key(), val) } #[inline] unsafe fn key(&self) -> imp::Key { match self.key.load(Ordering::Relaxed) { 0 => self.lazy_init() as imp::Key, n => n as imp::Key, } } unsafe fn lazy_init(&self) -> usize { // 当前,TLS 的 Windows 实现非常繁琐,如果我们仅同步所有内容,它将大大简化创建过程。 // // // 此外,在 windows 上还没有看到 tls 键的 0 索引,因此我们只是简化了整个分支。 // if imp::requires_synchronized_create() { // 我们从不调用 `INIT_LOCK.init()`,因此尝试重新获得此互斥锁是 UB! // static INIT_LOCK: StaticMutex = StaticMutex::new(); let _guard = INIT_LOCK.lock(); let mut key = self.key.load(Ordering::SeqCst); if key == 0 { key = imp::create(self.dtor) as usize; self.key.store(key, Ordering::SeqCst); } rtassert!(key != 0); return key; } // POSIX 允许此处创建的密钥为 0,但下面的 compare_exchange 依赖于使用 0 作为标记值来检查谁赢得了设置共享 TLS 密钥的竞赛。 // 据我所知,没有保证值不能作为 posix_key_create 键返回,因此没有值可以初始化内部键以证明尚未设置。 // // 因此,我们将继续使用 0 值,但要进行一些旋转以确保我们从创建例程返回了 non-0 值。 // FIXME: 这显然是一个 hack,应该对其进行清理。 // // // // let key1 = imp::create(self.dtor); let key = if key1 != 0 { key1 } else { let key2 = imp::create(self.dtor); imp::destroy(key1); key2 }; rtassert!(key != 0); match self.key.compare_exchange(0, key as usize, Ordering::SeqCst, Ordering::SeqCst) { // CAS 成功了,所以我们创建了实际的密钥 Ok(_) => key as usize, // 如果有人超过了我们,请改用他们的秘钥 Err(n) => { imp::destroy(key); n } } } } impl Key { /// 创建一个新的托管 OS TLS 密钥。 /// /// 当秘钥离开作用域时,该键将被释放。 /// /// 提供的参数是此 TLS 密钥的值的可选指定的析构函数。 /// 当线程退出并且此键的值不为 null 时,将调用析构函数。 /// 在调用析构函数之前,TLS 值将重置为 null。 /// /// 请注意,当 `Key` 离开作用域时,析构函数将不会运行。 /// /// #[inline] pub fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> Key { Key { key: unsafe { imp::create(dtor) } } } /// 请参见 StaticKey::get #[inline] pub fn get(&self) -> *mut u8 { unsafe { imp::get(self.key) } } /// 请参见 StaticKey::set #[inline] pub fn set(&self, val: *mut u8) { unsafe { imp::set(self.key, val) } } } impl Drop for Key { fn drop(&mut self) { // 目前,Windows 不支持 TLS 密钥销毁,但是除了测试以外,其他任何地方都没有使用它,因此只需泄漏 TLS 密钥即可。 // // unsafe { imp::destroy(self.key) } } }