|
| 1 | +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT |
| 2 | +// file at the top-level directory of this distribution and at |
| 3 | +// http://rust-lang.org/COPYRIGHT. |
| 4 | +// |
| 5 | +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 6 | +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 7 | +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| 8 | +// option. This file may not be copied, modified, or distributed |
| 9 | +// except according to those terms. |
| 10 | + |
| 11 | +use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; |
| 12 | +use ptr; |
| 13 | +use mem; |
| 14 | +use cell::Cell; |
| 15 | +use num::NonZeroUsize; |
| 16 | +use self::sync_bitset::*; |
| 17 | + |
| 18 | +#[cfg(target_pointer_width="64")] |
| 19 | +const USIZE_BITS: usize = 64; |
| 20 | +const TLS_KEYS: usize = 128; // Same as POSIX minimum |
| 21 | +const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS; |
| 22 | + |
| 23 | +static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT; |
| 24 | +macro_rules! dup { |
| 25 | + ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* )); |
| 26 | + (() $($val:tt)*) => ([$($val),*]) |
| 27 | +} |
| 28 | +static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) ATOMIC_USIZE_INIT); |
| 29 | + |
| 30 | +extern "C" { |
| 31 | + fn get_tls_ptr() -> *const u8; |
| 32 | + fn set_tls_ptr(tls: *const u8); |
| 33 | +} |
| 34 | + |
| 35 | +#[derive(Copy, Clone)] |
| 36 | +#[repr(C)] |
| 37 | +pub struct Key(NonZeroUsize); |
| 38 | + |
| 39 | +impl Key { |
| 40 | + fn to_index(self) -> usize { |
| 41 | + self.0.get() - 1 |
| 42 | + } |
| 43 | + |
| 44 | + fn from_index(index: usize) -> Self { |
| 45 | + Key(NonZeroUsize::new(index + 1).unwrap()) |
| 46 | + } |
| 47 | + |
| 48 | + pub fn as_usize(self) -> usize { |
| 49 | + self.0.get() |
| 50 | + } |
| 51 | + |
| 52 | + pub fn from_usize(index: usize) -> Self { |
| 53 | + Key(NonZeroUsize::new(index).unwrap()) |
| 54 | + } |
| 55 | +} |
| 56 | + |
| 57 | +#[repr(C)] |
| 58 | +pub struct Tls { |
| 59 | + data: [Cell<*mut u8>; TLS_KEYS] |
| 60 | +} |
| 61 | + |
| 62 | +pub struct ActiveTls<'a> { |
| 63 | + tls: &'a Tls |
| 64 | +} |
| 65 | + |
| 66 | +impl<'a> Drop for ActiveTls<'a> { |
| 67 | + fn drop(&mut self) { |
| 68 | + let value_with_destructor = |key: usize| { |
| 69 | + let ptr = TLS_DESTRUCTOR[key].load(Ordering::Relaxed); |
| 70 | + unsafe { mem::transmute::<_,Option<unsafe extern fn(*mut u8)>>(ptr) } |
| 71 | + .map(|dtor| (&self.tls.data[key], dtor)) |
| 72 | + }; |
| 73 | + |
| 74 | + let mut any_non_null_dtor = true; |
| 75 | + while any_non_null_dtor { |
| 76 | + any_non_null_dtor = false; |
| 77 | + for (value, dtor) in TLS_KEY_IN_USE.iter().filter_map(&value_with_destructor) { |
| 78 | + let value = value.replace(ptr::null_mut()); |
| 79 | + if value != ptr::null_mut() { |
| 80 | + any_non_null_dtor = true; |
| 81 | + unsafe { dtor(value) } |
| 82 | + } |
| 83 | + } |
| 84 | + } |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +impl Tls { |
| 89 | + pub fn new() -> Tls { |
| 90 | + Tls { data: dup!((* * * * * * *) (Cell::new(ptr::null_mut()))) } |
| 91 | + } |
| 92 | + |
| 93 | + pub unsafe fn activate(&self) -> ActiveTls { |
| 94 | + set_tls_ptr(self as *const Tls as _); |
| 95 | + ActiveTls { tls: self } |
| 96 | + } |
| 97 | + |
| 98 | + #[allow(unused)] |
| 99 | + pub unsafe fn activate_persistent(self: Box<Self>) { |
| 100 | + set_tls_ptr((&*self) as *const Tls as _); |
| 101 | + mem::forget(self); |
| 102 | + } |
| 103 | + |
| 104 | + unsafe fn current<'a>() -> &'a Tls { |
| 105 | + &*(get_tls_ptr() as *const Tls) |
| 106 | + } |
| 107 | + |
| 108 | + pub fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { |
| 109 | + let index = TLS_KEY_IN_USE.set().expect("TLS limit exceeded"); |
| 110 | + TLS_DESTRUCTOR[index].store(dtor.map_or(0, |f| f as usize), Ordering::Relaxed); |
| 111 | + Key::from_index(index) |
| 112 | + } |
| 113 | + |
| 114 | + pub fn set(key: Key, value: *mut u8) { |
| 115 | + let index = key.to_index(); |
| 116 | + assert!(TLS_KEY_IN_USE.get(index)); |
| 117 | + unsafe { Self::current() }.data[index].set(value); |
| 118 | + } |
| 119 | + |
| 120 | + pub fn get(key: Key) -> *mut u8 { |
| 121 | + let index = key.to_index(); |
| 122 | + assert!(TLS_KEY_IN_USE.get(index)); |
| 123 | + unsafe { Self::current() }.data[index].get() |
| 124 | + } |
| 125 | + |
| 126 | + pub fn destroy(key: Key) { |
| 127 | + TLS_KEY_IN_USE.clear(key.to_index()); |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +mod sync_bitset { |
| 132 | + use sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; |
| 133 | + use iter::{Enumerate, Peekable}; |
| 134 | + use slice::Iter; |
| 135 | + use super::{TLS_KEYS_BITSET_SIZE, USIZE_BITS}; |
| 136 | + |
| 137 | + /// A bitset that can be used synchronously. |
| 138 | + pub(super) struct SyncBitset([AtomicUsize; TLS_KEYS_BITSET_SIZE]); |
| 139 | + |
| 140 | + pub(super) const SYNC_BITSET_INIT: SyncBitset = SyncBitset([ATOMIC_USIZE_INIT, ATOMIC_USIZE_INIT]); |
| 141 | + |
| 142 | + impl SyncBitset { |
| 143 | + pub fn get(&self, index: usize) -> bool { |
| 144 | + let (hi, lo) = Self::split(index); |
| 145 | + (self.0[hi].load(Ordering::Relaxed) & lo) != 0 |
| 146 | + } |
| 147 | + |
| 148 | + /// Not atomic. |
| 149 | + pub fn iter(&self) -> SyncBitsetIter { |
| 150 | + SyncBitsetIter { |
| 151 | + iter: self.0.iter().enumerate().peekable(), |
| 152 | + elem_idx: 0, |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + pub fn clear(&self, index: usize) { |
| 157 | + let (hi, lo) = Self::split(index); |
| 158 | + self.0[hi].fetch_and(!lo, Ordering::Relaxed); |
| 159 | + } |
| 160 | + |
| 161 | + /// Set any unset bit. Not atomic. Returns `None` if all bits were |
| 162 | + /// observed to be set. |
| 163 | + pub fn set(&self) -> Option<usize> { |
| 164 | + 'elems: for (idx, elem) in self.0.iter().enumerate() { |
| 165 | + let mut current = elem.load(Ordering::Relaxed); |
| 166 | + loop { |
| 167 | + if 0 == !current { |
| 168 | + continue 'elems; |
| 169 | + } |
| 170 | + let trailing_ones = (!current).trailing_zeros() as usize; |
| 171 | + match elem.compare_exchange(current, current | (1 << trailing_ones), Ordering::AcqRel, Ordering::Relaxed) { |
| 172 | + Ok(_) => return Some(idx * USIZE_BITS + trailing_ones), |
| 173 | + Err(previous) => current = previous, |
| 174 | + } |
| 175 | + } |
| 176 | + } |
| 177 | + None |
| 178 | + } |
| 179 | + |
| 180 | + fn split(index: usize) -> (usize, usize) { |
| 181 | + (index / USIZE_BITS, 1 << (index % USIZE_BITS)) |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | + pub(super) struct SyncBitsetIter<'a> { |
| 186 | + iter: Peekable<Enumerate<Iter<'a, AtomicUsize>>>, |
| 187 | + elem_idx: usize, |
| 188 | + } |
| 189 | + |
| 190 | + impl<'a> Iterator for SyncBitsetIter<'a> { |
| 191 | + type Item = usize; |
| 192 | + |
| 193 | + fn next(&mut self) -> Option<usize> { |
| 194 | + self.iter.peek().cloned().and_then(|(idx, elem)| { |
| 195 | + let elem = elem.load(Ordering::Relaxed); |
| 196 | + let low_mask = (1 << self.elem_idx) - 1; |
| 197 | + let next = elem & !low_mask; |
| 198 | + let next_idx = next.trailing_zeros() as usize; |
| 199 | + self.elem_idx = next_idx + 1; |
| 200 | + if self.elem_idx >= 64 { |
| 201 | + self.elem_idx = 0; |
| 202 | + self.iter.next(); |
| 203 | + } |
| 204 | + match next_idx { |
| 205 | + 64 => self.next(), |
| 206 | + _ => Some(idx * USIZE_BITS + next_idx), |
| 207 | + } |
| 208 | + }) |
| 209 | + } |
| 210 | + } |
| 211 | + |
| 212 | + #[cfg(test)] |
| 213 | + mod tests { |
| 214 | + use super::*; |
| 215 | + |
| 216 | + fn test_data(bitset: [usize; 2], bit_indices: &[usize]) { |
| 217 | + let set = SyncBitset([AtomicUsize::new(bitset[0]), AtomicUsize::new(bitset[1])]); |
| 218 | + assert_eq!(set.iter().collect::<Vec<_>>(), bit_indices); |
| 219 | + for &i in bit_indices { |
| 220 | + assert!(set.get(i)); |
| 221 | + } |
| 222 | + } |
| 223 | + |
| 224 | + #[test] |
| 225 | + fn iter() { |
| 226 | + test_data([0b0110_1001, 0], &[0, 3, 5, 6]); |
| 227 | + test_data([0x8000_0000_0000_0000, 0x8000_0000_0000_0001], &[63, 64, 127]); |
| 228 | + test_data([0, 0], &[]); |
| 229 | + } |
| 230 | + |
| 231 | + #[test] |
| 232 | + fn set_get_clear() { |
| 233 | + let set = SYNC_BITSET_INIT; |
| 234 | + let key = set.set().unwrap(); |
| 235 | + assert!(set.get(key)); |
| 236 | + set.clear(key); |
| 237 | + assert!(!set.get(key)); |
| 238 | + } |
| 239 | + } |
| 240 | +} |
0 commit comments