Skip to content

Commit 87645db

Browse files
author
Jethro Beekman
committed
SGX target: add thread local storage
1 parent 740e70b commit 87645db

File tree

4 files changed

+271
-17
lines changed

4 files changed

+271
-17
lines changed

src/libstd/sys/sgx/abi/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ mod mem;
1717
pub(super) mod panic;
1818

1919
// library features
20+
pub mod thread;
21+
pub mod tls;
2022
#[macro_use]
2123
mod usercalls;
2224

@@ -63,6 +65,10 @@ pub unsafe extern "C" fn tcs_init(secondary: bool) {
6365
#[no_mangle]
6466
#[allow(unreachable_code)]
6567
pub extern "C" fn entry(p1: u64, p2: u64, p3: u64, secondary: bool, p4: u64, p5: u64) -> (u64, u64) {
68+
// FIXME: how to support TLS in library mode?
69+
let tls = Box::new(tls::Tls::new());
70+
let _tls_guard = unsafe { tls.activate() };
71+
6672
if secondary {
6773
unimplemented!("thread entrypoint");
6874

src/libstd/sys/sgx/abi/thread.rs

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
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 fortanix_sgx_abi::Tcs;
12+
13+
/// Get the ID for the current thread. The ID is guaranteed to be unique among
14+
/// all currently running threads in the enclave, and it is guaranteed to be
15+
/// constant for the lifetime of the thread. More specifically for SGX, there
16+
/// is a one-to-one correspondence of the ID to the address of the TCS.
17+
pub fn current() -> Tcs {
18+
extern "C" { fn get_tcs_addr() -> Tcs; }
19+
unsafe { get_tcs_addr() }
20+
}

src/libstd/sys/sgx/abi/tls.rs

+240
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
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+
}

src/libstd/sys/sgx/thread_local.rs

+5-17
Original file line numberDiff line numberDiff line change
@@ -8,40 +8,28 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use boxed::Box;
12-
use ptr;
11+
use super::abi::tls::{Tls, Key as AbiKey};
1312

1413
pub type Key = usize;
1514

16-
struct Allocated {
17-
value: *mut u8,
18-
dtor: Option<unsafe extern fn(*mut u8)>,
19-
}
20-
2115
#[inline]
2216
pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key {
23-
Box::into_raw(Box::new(Allocated {
24-
value: ptr::null_mut(),
25-
dtor,
26-
})) as usize
17+
Tls::create(dtor).as_usize()
2718
}
2819

2920
#[inline]
3021
pub unsafe fn set(key: Key, value: *mut u8) {
31-
(*(key as *mut Allocated)).value = value;
22+
Tls::set(AbiKey::from_usize(key), value)
3223
}
3324

3425
#[inline]
3526
pub unsafe fn get(key: Key) -> *mut u8 {
36-
(*(key as *mut Allocated)).value
27+
Tls::get(AbiKey::from_usize(key))
3728
}
3829

3930
#[inline]
4031
pub unsafe fn destroy(key: Key) {
41-
let key = Box::from_raw(key as *mut Allocated);
42-
if let Some(f) = key.dtor {
43-
f(key.value);
44-
}
32+
Tls::destroy(AbiKey::from_usize(key))
4533
}
4634

4735
#[inline]

0 commit comments

Comments
 (0)