Skip to content

Commit 252ac51

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 <[email protected]>
1 parent f826770 commit 252ac51

File tree

1 file changed

+47
-35
lines changed

1 file changed

+47
-35
lines changed

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

+47-35
Original file line numberDiff line numberDiff line change
@@ -23,61 +23,66 @@ 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_attr(test, linkage = "available_externally")]
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_attr(test, linkage = "available_externally")]
32+
#[export_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key9DTORSE"]
33+
static DTORS: AtomicPtr<Node> = AtomicPtr::new(ptr::null_mut());
34+
35+
fn tls_ptr_addr() -> *mut *mut u8 {
3036
let mut tp: usize;
3137
unsafe {
3238
asm!(
3339
"mv {}, tp",
3440
out(reg) tp,
3541
);
3642
}
37-
core::ptr::from_exposed_addr_mut::<usize>(tp)
43+
core::ptr::from_exposed_addr_mut::<*mut u8>(tp)
3844
}
3945

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

51+
if !tp.is_null() {
52+
return unsafe {
53+
core::slice::from_raw_parts_mut(tp, TLS_MEMORY_SIZE / core::mem::size_of::<*mut u8>())
54+
};
55+
}
4556
// If the TP register is `0`, then this thread hasn't initialized
4657
// 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-
}
58+
let tp = unsafe {
59+
map_memory(
60+
None,
61+
None,
62+
TLS_MEMORY_SIZE / core::mem::size_of::<*mut u8>(),
63+
MemoryFlags::R | MemoryFlags::W,
64+
)
5665
.expect("Unable to allocate memory for thread local storage")
57-
.as_mut_ptr();
66+
};
5867

59-
unsafe {
60-
// Key #0 is currently unused.
61-
(tp).write_volatile(0);
68+
for val in tp.iter() {
69+
assert!(*val as usize == 0);
70+
}
6271

63-
// Set the thread's `$tp` register
64-
asm!(
65-
"mv tp, {}",
66-
in(reg) tp as usize,
67-
);
68-
}
72+
unsafe {
73+
// Set the thread's `$tp` register
74+
asm!(
75+
"mv tp, {}",
76+
in(reg) tp.as_mut_ptr() as usize,
77+
);
6978
}
7079
tp
7180
}
7281

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-
7882
#[inline]
7983
pub unsafe fn create(dtor: Option<Dtor>) -> Key {
80-
let key = tls_alloc();
84+
// Allocate a new TLS key. These keys are shared among all threads.
85+
let key = TLS_KEY_INDEX.fetch_add(1, SeqCst);
8186
if let Some(f) = dtor {
8287
unsafe { register_dtor(key, f) };
8388
}
@@ -87,18 +92,20 @@ pub unsafe fn create(dtor: Option<Dtor>) -> Key {
8792
#[inline]
8893
pub unsafe fn set(key: Key, value: *mut u8) {
8994
assert!((key < 1022) && (key >= 1));
90-
unsafe { tls_ptr().add(key).write_volatile(value as usize) };
95+
let table = tls_table();
96+
table[key] = value;
9197
}
9298

9399
#[inline]
94100
pub unsafe fn get(key: Key) -> *mut u8 {
95101
assert!((key < 1022) && (key >= 1));
96-
core::ptr::from_exposed_addr_mut::<u8>(unsafe { tls_ptr().add(key).read_volatile() })
102+
tls_table()[key]
97103
}
98104

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

104111
// -------------------------------------------------------------------------
@@ -127,8 +134,6 @@ pub unsafe fn destroy(_key: Key) {
127134
// key but also a slot for the destructor queue on windows. An optimization for
128135
// another day!
129136

130-
static DTORS: AtomicPtr<Node> = AtomicPtr::new(ptr::null_mut());
131-
132137
struct Node {
133138
dtor: Dtor,
134139
key: Key,
@@ -155,6 +160,7 @@ pub unsafe fn destroy_tls() {
155160
if tp.is_null() {
156161
return;
157162
}
163+
158164
unsafe { run_dtors() };
159165

160166
// Finally, free the TLS array
@@ -169,6 +175,12 @@ pub unsafe fn destroy_tls() {
169175

170176
unsafe fn run_dtors() {
171177
let mut any_run = true;
178+
179+
// Run the destructor "some" number of times. This is 5x on Windows,
180+
// so we copy it here. This allows TLS variables to create new
181+
// TLS variables upon destruction that will also get destroyed.
182+
// Keep going until we run out of tries or until we have nothing
183+
// left to destroy.
172184
for _ in 0..5 {
173185
if !any_run {
174186
break;

0 commit comments

Comments
 (0)