Skip to content

Commit 007bf7a

Browse files
committed
std: xous: fix thread_local_key under tests
When running tests, libstd gets implemented as a second library. Due to this fact, the `create()` and `destroy()` functions come from different libraries. To work around this, stash the `destroy_tls()` pointer in the first unused slot in the thread local storage pool. That way even if the destruction comes from a different version of libstd, the correct `DTORS` list will be consulted. Signed-off-by: Sean Cross <sean@xobs.io>
1 parent 762e58a commit 007bf7a

File tree

1 file changed

+63
-38
lines changed

1 file changed

+63
-38
lines changed

library/std/src/sys/pal/xous/thread_local_key.rs

Lines changed: 63 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -23,61 +23,76 @@ pub type Dtor = unsafe extern "C" fn(*mut u8);
2323

2424
const TLS_MEMORY_SIZE: usize = 4096;
2525

26-
/// TLS keys start at `1` to mimic POSIX.
26+
/// TLS keys start at `1`. Index `0` is unused
27+
#[cfg(not(test))]
28+
#[export_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key13TLS_KEY_INDEXE"]
2729
static TLS_KEY_INDEX: AtomicUsize = AtomicUsize::new(1);
2830

29-
fn tls_ptr_addr() -> *mut usize {
31+
#[cfg(not(test))]
32+
#[export_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key9DTORSE"]
33+
static DTORS: AtomicPtr<Node> = AtomicPtr::new(ptr::null_mut());
34+
35+
#[cfg(test)]
36+
extern "Rust" {
37+
#[link_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key13TLS_KEY_INDEXE"]
38+
static TLS_KEY_INDEX: AtomicUsize;
39+
40+
#[link_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key9DTORSE"]
41+
static DTORS: AtomicPtr<Node>;
42+
}
43+
44+
fn tls_ptr_addr() -> *mut *mut u8 {
3045
let mut tp: usize;
3146
unsafe {
3247
asm!(
3348
"mv {}, tp",
3449
out(reg) tp,
3550
);
3651
}
37-
core::ptr::from_exposed_addr_mut::<usize>(tp)
52+
core::ptr::from_exposed_addr_mut::<*mut u8>(tp)
3853
}
3954

4055
/// Create an area of memory that's unique per thread. This area will
4156
/// contain all thread local pointers.
42-
fn tls_ptr() -> *mut usize {
43-
let mut tp = tls_ptr_addr();
57+
fn tls_table() -> &'static mut [*mut u8] {
58+
let tp = tls_ptr_addr();
4459

60+
if !tp.is_null() {
61+
return unsafe {
62+
core::slice::from_raw_parts_mut(tp, TLS_MEMORY_SIZE / core::mem::size_of::<*mut u8>())
63+
};
64+
}
4565
// If the TP register is `0`, then this thread hasn't initialized
4666
// its TLS yet. Allocate a new page to store this memory.
47-
if tp.is_null() {
48-
tp = unsafe {
49-
map_memory(
50-
None,
51-
None,
52-
TLS_MEMORY_SIZE / core::mem::size_of::<usize>(),
53-
MemoryFlags::R | MemoryFlags::W,
54-
)
55-
}
67+
let tp = unsafe {
68+
map_memory(
69+
None,
70+
None,
71+
TLS_MEMORY_SIZE / core::mem::size_of::<*mut u8>(),
72+
MemoryFlags::R | MemoryFlags::W,
73+
)
5674
.expect("Unable to allocate memory for thread local storage")
57-
.as_mut_ptr();
75+
};
5876

59-
unsafe {
60-
// Key #0 is currently unused.
61-
(tp).write_volatile(0);
77+
for val in tp.iter() {
78+
assert!(*val as usize == 0);
79+
}
6280

63-
// Set the thread's `$tp` register
64-
asm!(
65-
"mv tp, {}",
66-
in(reg) tp as usize,
67-
);
68-
}
81+
unsafe {
82+
// Set the thread's `$tp` register
83+
asm!(
84+
"mv tp, {}",
85+
in(reg) tp.as_mut_ptr() as usize,
86+
);
6987
}
7088
tp
7189
}
7290

73-
/// Allocate a new TLS key. These keys are shared among all threads.
74-
fn tls_alloc() -> usize {
75-
TLS_KEY_INDEX.fetch_add(1, SeqCst)
76-
}
77-
7891
#[inline]
7992
pub unsafe fn create(dtor: Option<Dtor>) -> Key {
80-
let key = tls_alloc();
93+
// Allocate a new TLS key. These keys are shared among all threads.
94+
#[allow(unused_unsafe)]
95+
let key = unsafe { TLS_KEY_INDEX.fetch_add(1, SeqCst) };
8196
if let Some(f) = dtor {
8297
unsafe { register_dtor(key, f) };
8398
}
@@ -87,18 +102,20 @@ pub unsafe fn create(dtor: Option<Dtor>) -> Key {
87102
#[inline]
88103
pub unsafe fn set(key: Key, value: *mut u8) {
89104
assert!((key < 1022) && (key >= 1));
90-
unsafe { tls_ptr().add(key).write_volatile(value as usize) };
105+
let table = tls_table();
106+
table[key] = value;
91107
}
92108

93109
#[inline]
94110
pub unsafe fn get(key: Key) -> *mut u8 {
95111
assert!((key < 1022) && (key >= 1));
96-
core::ptr::from_exposed_addr_mut::<u8>(unsafe { tls_ptr().add(key).read_volatile() })
112+
tls_table()[key]
97113
}
98114

99115
#[inline]
100116
pub unsafe fn destroy(_key: Key) {
101-
panic!("can't destroy keys on Xous");
117+
// Just leak the key. Probably not great on long-running systems that create
118+
// lots of TLS variables, but in practice that's not an issue.
102119
}
103120

104121
// -------------------------------------------------------------------------
@@ -127,8 +144,6 @@ pub unsafe fn destroy(_key: Key) {
127144
// key but also a slot for the destructor queue on windows. An optimization for
128145
// another day!
129146

130-
static DTORS: AtomicPtr<Node> = AtomicPtr::new(ptr::null_mut());
131-
132147
struct Node {
133148
dtor: Dtor,
134149
key: Key,
@@ -138,10 +153,12 @@ struct Node {
138153
unsafe fn register_dtor(key: Key, dtor: Dtor) {
139154
let mut node = ManuallyDrop::new(Box::new(Node { key, dtor, next: ptr::null_mut() }));
140155

141-
let mut head = DTORS.load(SeqCst);
156+
#[allow(unused_unsafe)]
157+
let mut head = unsafe { DTORS.load(SeqCst) };
142158
loop {
143159
node.next = head;
144-
match DTORS.compare_exchange(head, &mut **node, SeqCst, SeqCst) {
160+
#[allow(unused_unsafe)]
161+
match unsafe { DTORS.compare_exchange(head, &mut **node, SeqCst, SeqCst) } {
145162
Ok(_) => return, // nothing to drop, we successfully added the node to the list
146163
Err(cur) => head = cur,
147164
}
@@ -155,6 +172,7 @@ pub unsafe fn destroy_tls() {
155172
if tp.is_null() {
156173
return;
157174
}
175+
158176
unsafe { run_dtors() };
159177

160178
// Finally, free the TLS array
@@ -169,12 +187,19 @@ pub unsafe fn destroy_tls() {
169187

170188
unsafe fn run_dtors() {
171189
let mut any_run = true;
190+
191+
// Run the destructor "some" number of times. This is 5x on Windows,
192+
// so we copy it here. This allows TLS variables to create new
193+
// TLS variables upon destruction that will also get destroyed.
194+
// Keep going until we run out of tries or until we have nothing
195+
// left to destroy.
172196
for _ in 0..5 {
173197
if !any_run {
174198
break;
175199
}
176200
any_run = false;
177-
let mut cur = DTORS.load(SeqCst);
201+
#[allow(unused_unsafe)]
202+
let mut cur = unsafe { DTORS.load(SeqCst) };
178203
while !cur.is_null() {
179204
let ptr = unsafe { get((*cur).key) };
180205

0 commit comments

Comments
 (0)