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
//! 基于操作系统的线程本地存储
//!
//! 该模块提供了一个基于操作系统的线程本地存储的实现,使用了本地操作系统提供的工具 (想想 `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)]
#[cfg(test)]
mod tests;
use crate::sync::atomic::{self, AtomicUsize, Ordering};
use crate::sys::thread_local_key as imp;
/// 静态分配的 TLS 密钥的类型。
///
/// 使用此类型是完全 `unsafe` 的,因为它不能防止在释放后使用或释放期间使用。
///
///
/// 第一次使用时会延迟分配实际的 OS-TLS 密钥。
/// 当 Rust 运行时退出或调用 `destroy` (以先到者为准) 时,也将释放该键。
///
/// # Examples
///
/// ```ignore (cannot-doctest-private-modules)
/// use tls::os::{StaticKey, INIT};
///
/// // 使用常规静态存储密钥。
/// static KEY: StaticKey = INIT;
///
/// // 通过 `get` 和 `set` 提供的状态是线程本地的。
/// 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)>,
}
/// 静态 TLS 密钥的常量初始化值。
///
/// 默认情况下,此值不指定析构函数。
pub const INIT: StaticKey = StaticKey::new(None);
// 定义一个可能不会作为 TLS 密钥返回的标记值。
//
#[cfg(not(target_os = "nto"))]
const KEY_SENTVAL: usize = 0;
// 在 QNX Neutrino 上,当前未使用时始终返回 0。
// 使用 0 意味着总是创建两个键并在之后立即远程第一个 (值为 0)。
//
#[cfg(target_os = "nto")]
const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1;
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(KEY_SENTVAL), 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) {
KEY_SENTVAL => self.lazy_init() as imp::Key,
n => n as imp::Key,
}
}
unsafe fn lazy_init(&self) -> usize {
// POSIX 允许此处创建的密钥为 KEY_SENTVAL,但下面的 compare_exchange 依赖于使用 KEY_SENTVAL 作为标记值来检查谁赢得了设置共享 TLS 密钥的竞赛。
// 据我所知,没有保证值不能作为 posix_key_create 键返回,因此没有值可以初始化内部键以证明尚未设置。
//
// 因此,我们将继续使用 KEY_SENTVAL 的值,但会进行一些调整以确保我们从创建例程中返回一个非 KEY_SENTVAL 值。
// FIXME: 这显然是一个 hack,应该对其进行清理。
//
//
//
//
let key1 = imp::create(self.dtor);
let key = if key1 as usize != KEY_SENTVAL {
key1
} else {
let key2 = imp::create(self.dtor);
imp::destroy(key1);
key2
};
rtassert!(key as usize != KEY_SENTVAL);
match self.key.compare_exchange(
KEY_SENTVAL,
key as usize,
Ordering::SeqCst,
Ordering::SeqCst,
) {
// CAS 成功了,所以我们创建了实际的密钥
Ok(_) => key as usize,
// 如果有人超过了我们,请改用他们的秘钥
Err(n) => {
imp::destroy(key);
n
}
}
}
}