@@ -23,61 +23,76 @@ pub type Dtor = unsafe extern "C" fn(*mut u8);
23
23
24
24
const TLS_MEMORY_SIZE : usize = 4096 ;
25
25
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" ]
27
29
static TLS_KEY_INDEX : AtomicUsize = AtomicUsize :: new ( 1 ) ;
28
30
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 {
30
45
let mut tp: usize ;
31
46
unsafe {
32
47
asm ! (
33
48
"mv {}, tp" ,
34
49
out( reg) tp,
35
50
) ;
36
51
}
37
- core:: ptr:: from_exposed_addr_mut :: < usize > ( tp)
52
+ core:: ptr:: from_exposed_addr_mut :: < * mut u8 > ( tp)
38
53
}
39
54
40
55
/// Create an area of memory that's unique per thread. This area will
41
56
/// 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 ( ) ;
44
59
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
+ }
45
65
// If the TP register is `0`, then this thread hasn't initialized
46
66
// 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
+ )
56
74
. expect ( "Unable to allocate memory for thread local storage" )
57
- . as_mut_ptr ( ) ;
75
+ } ;
58
76
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
+ }
62
80
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
+ ) ;
69
87
}
70
88
tp
71
89
}
72
90
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
-
78
91
#[ inline]
79
92
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 ) } ;
81
96
if let Some ( f) = dtor {
82
97
unsafe { register_dtor ( key, f) } ;
83
98
}
@@ -87,18 +102,20 @@ pub unsafe fn create(dtor: Option<Dtor>) -> Key {
87
102
#[ inline]
88
103
pub unsafe fn set ( key : Key , value : * mut u8 ) {
89
104
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;
91
107
}
92
108
93
109
#[ inline]
94
110
pub unsafe fn get ( key : Key ) -> * mut u8 {
95
111
assert ! ( ( key < 1022 ) && ( key >= 1 ) ) ;
96
- core :: ptr :: from_exposed_addr_mut :: < u8 > ( unsafe { tls_ptr ( ) . add ( key) . read_volatile ( ) } )
112
+ tls_table ( ) [ key]
97
113
}
98
114
99
115
#[ inline]
100
116
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.
102
119
}
103
120
104
121
// -------------------------------------------------------------------------
@@ -127,8 +144,6 @@ pub unsafe fn destroy(_key: Key) {
127
144
// key but also a slot for the destructor queue on windows. An optimization for
128
145
// another day!
129
146
130
- static DTORS : AtomicPtr < Node > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
131
-
132
147
struct Node {
133
148
dtor : Dtor ,
134
149
key : Key ,
@@ -138,10 +153,12 @@ struct Node {
138
153
unsafe fn register_dtor ( key : Key , dtor : Dtor ) {
139
154
let mut node = ManuallyDrop :: new ( Box :: new ( Node { key, dtor, next : ptr:: null_mut ( ) } ) ) ;
140
155
141
- let mut head = DTORS . load ( SeqCst ) ;
156
+ #[ allow( unused_unsafe) ]
157
+ let mut head = unsafe { DTORS . load ( SeqCst ) } ;
142
158
loop {
143
159
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 ) } {
145
162
Ok ( _) => return , // nothing to drop, we successfully added the node to the list
146
163
Err ( cur) => head = cur,
147
164
}
@@ -155,6 +172,7 @@ pub unsafe fn destroy_tls() {
155
172
if tp. is_null ( ) {
156
173
return ;
157
174
}
175
+
158
176
unsafe { run_dtors ( ) } ;
159
177
160
178
// Finally, free the TLS array
@@ -169,12 +187,19 @@ pub unsafe fn destroy_tls() {
169
187
170
188
unsafe fn run_dtors ( ) {
171
189
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.
172
196
for _ in 0 ..5 {
173
197
if !any_run {
174
198
break ;
175
199
}
176
200
any_run = false ;
177
- let mut cur = DTORS . load ( SeqCst ) ;
201
+ #[ allow( unused_unsafe) ]
202
+ let mut cur = unsafe { DTORS . load ( SeqCst ) } ;
178
203
while !cur. is_null ( ) {
179
204
let ptr = unsafe { get ( ( * cur) . key ) } ;
180
205
0 commit comments