@@ -23,61 +23,66 @@ 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_attr( test, linkage = "available_externally" ) ]
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_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 {
30
36
let mut tp: usize ;
31
37
unsafe {
32
38
asm ! (
33
39
"mv {}, tp" ,
34
40
out( reg) tp,
35
41
) ;
36
42
}
37
- core:: ptr:: from_exposed_addr_mut :: < usize > ( tp)
43
+ core:: ptr:: from_exposed_addr_mut :: < * mut u8 > ( tp)
38
44
}
39
45
40
46
/// Create an area of memory that's unique per thread. This area will
41
47
/// 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 ( ) ;
44
50
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
+ }
45
56
// If the TP register is `0`, then this thread hasn't initialized
46
57
// 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
+ )
56
65
. expect ( "Unable to allocate memory for thread local storage" )
57
- . as_mut_ptr ( ) ;
66
+ } ;
58
67
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
+ }
62
71
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
+ ) ;
69
78
}
70
79
tp
71
80
}
72
81
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
82
#[ inline]
79
83
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 ) ;
81
86
if let Some ( f) = dtor {
82
87
unsafe { register_dtor ( key, f) } ;
83
88
}
@@ -87,18 +92,20 @@ pub unsafe fn create(dtor: Option<Dtor>) -> Key {
87
92
#[ inline]
88
93
pub unsafe fn set ( key : Key , value : * mut u8 ) {
89
94
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;
91
97
}
92
98
93
99
#[ inline]
94
100
pub unsafe fn get ( key : Key ) -> * mut u8 {
95
101
assert ! ( ( key < 1022 ) && ( key >= 1 ) ) ;
96
- core :: ptr :: from_exposed_addr_mut :: < u8 > ( unsafe { tls_ptr ( ) . add ( key) . read_volatile ( ) } )
102
+ tls_table ( ) [ key]
97
103
}
98
104
99
105
#[ inline]
100
106
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.
102
109
}
103
110
104
111
// -------------------------------------------------------------------------
@@ -127,8 +134,6 @@ pub unsafe fn destroy(_key: Key) {
127
134
// key but also a slot for the destructor queue on windows. An optimization for
128
135
// another day!
129
136
130
- static DTORS : AtomicPtr < Node > = AtomicPtr :: new ( ptr:: null_mut ( ) ) ;
131
-
132
137
struct Node {
133
138
dtor : Dtor ,
134
139
key : Key ,
@@ -155,6 +160,7 @@ pub unsafe fn destroy_tls() {
155
160
if tp. is_null ( ) {
156
161
return ;
157
162
}
163
+
158
164
unsafe { run_dtors ( ) } ;
159
165
160
166
// Finally, free the TLS array
@@ -169,6 +175,12 @@ pub unsafe fn destroy_tls() {
169
175
170
176
unsafe fn run_dtors ( ) {
171
177
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.
172
184
for _ in 0 ..5 {
173
185
if !any_run {
174
186
break ;
0 commit comments