@@ -49,38 +49,47 @@ static THREAD_ID_MANAGER: Lazy<Mutex<ThreadIdManager>> =
4949/// A thread ID may be reused after a thread exits.
5050#[ derive( Clone , Copy ) ]
5151pub ( crate ) struct Thread {
52- /// The thread ID obtained from the thread ID manager.
53- pub ( crate ) id : usize ,
5452 /// The bucket this thread's local storage will be in.
5553 pub ( crate ) bucket : usize ,
56- /// The size of the bucket this thread's local storage will be in.
57- pub ( crate ) bucket_size : usize ,
5854 /// The index into the bucket this thread's local storage is in.
5955 pub ( crate ) index : usize ,
6056}
57+
6158impl Thread {
59+ /// id: The thread ID obtained from the thread ID manager.
60+ #[ inline]
6261 fn new ( id : usize ) -> Self {
6362 let bucket = usize:: from ( POINTER_WIDTH ) - ( ( id + 1 ) . leading_zeros ( ) as usize ) - 1 ;
6463 let bucket_size = 1 << bucket;
6564 let index = id - ( bucket_size - 1 ) ;
65+ Self { bucket, index }
66+ }
6667
67- Self {
68- id,
69- bucket,
70- bucket_size,
71- index,
72- }
68+ /// The size of the bucket this thread's local storage will be in.
69+ #[ inline]
70+ pub fn bucket_size ( & self ) -> usize {
71+ 1 << self . bucket
7372 }
7473}
7574
7675cfg_if:: cfg_if! {
7776 if #[ cfg( feature = "nightly" ) ] {
77+ use memoffset:: offset_of;
78+ use std:: ptr:: null;
79+ use std:: cell:: UnsafeCell ;
80+
7881 // This is split into 2 thread-local variables so that we can check whether the
7982 // thread is initialized without having to register a thread-local destructor.
8083 //
8184 // This makes the fast path smaller.
8285 #[ thread_local]
83- static mut THREAD : Option <Thread > = None ;
86+ static THREAD : UnsafeCell <ThreadWrapper > = UnsafeCell :: new( ThreadWrapper {
87+ self_ptr: null( ) ,
88+ thread: Thread {
89+ index: 0 ,
90+ bucket: 0 ,
91+ } ,
92+ } ) ;
8493 thread_local! { static THREAD_GUARD : ThreadGuard = const { ThreadGuard { id: Cell :: new( 0 ) } } ; }
8594
8695 // Guard to ensure the thread ID is released on thread exit.
@@ -97,17 +106,41 @@ cfg_if::cfg_if! {
97106 // will go through get_slow which will either panic or
98107 // initialize a new ThreadGuard.
99108 unsafe {
100- THREAD = None ;
109+ ( & mut * THREAD . get ( ) ) . self_ptr = null ( ) ;
101110 }
102111 THREAD_ID_MANAGER . lock( ) . free( self . id. get( ) ) ;
103112 }
104113 }
105114
115+ /// Data which is unique to the current thread while it is running.
116+ /// A thread ID may be reused after a thread exits.
117+ ///
118+ /// This wrapper exists to hide multiple accesses to the TLS data
119+ /// from the backend as this can lead to inefficient codegen
120+ /// (to be precise it can lead to multiple TLS address lookups)
121+ #[ derive( Clone , Copy ) ]
122+ struct ThreadWrapper {
123+ self_ptr: * const Thread ,
124+ thread: Thread ,
125+ }
126+
127+ impl ThreadWrapper {
128+ /// The thread ID obtained from the thread ID manager.
129+ #[ inline]
130+ fn new( id: usize ) -> Self {
131+ Self {
132+ self_ptr: ( ( THREAD . get( ) . cast_const( ) as usize ) + offset_of!( ThreadWrapper , thread) ) as * const Thread ,
133+ thread: Thread :: new( id) ,
134+ }
135+ }
136+ }
137+
106138 /// Returns a thread ID for the current thread, allocating one if needed.
107139 #[ inline]
108140 pub ( crate ) fn get( ) -> Thread {
109- if let Some ( thread) = unsafe { THREAD } {
110- thread
141+ let thread = unsafe { * THREAD . get( ) } ;
142+ if !thread. self_ptr. is_null( ) {
143+ unsafe { thread. self_ptr. read( ) }
111144 } else {
112145 get_slow( )
113146 }
@@ -116,12 +149,13 @@ cfg_if::cfg_if! {
116149 /// Out-of-line slow path for allocating a thread ID.
117150 #[ cold]
118151 fn get_slow( ) -> Thread {
119- let new = Thread :: new( THREAD_ID_MANAGER . lock( ) . alloc( ) ) ;
152+ let id = THREAD_ID_MANAGER . lock( ) . alloc( ) ;
153+ let new = ThreadWrapper :: new( id) ;
120154 unsafe {
121- THREAD = Some ( new) ;
155+ * THREAD . get ( ) = new;
122156 }
123- THREAD_GUARD . with( |guard| guard. id. set( new . id) ) ;
124- new
157+ THREAD_GUARD . with( |guard| guard. id. set( id) ) ;
158+ new. thread
125159 }
126160 } else {
127161 // This is split into 2 thread-local variables so that we can check whether the
@@ -164,9 +198,10 @@ cfg_if::cfg_if! {
164198 /// Out-of-line slow path for allocating a thread ID.
165199 #[ cold]
166200 fn get_slow( thread: & Cell <Option <Thread >>) -> Thread {
167- let new = Thread :: new( THREAD_ID_MANAGER . lock( ) . alloc( ) ) ;
201+ let id = THREAD_ID_MANAGER . lock( ) . alloc( ) ;
202+ let new = Thread :: new( id) ;
168203 thread. set( Some ( new) ) ;
169- THREAD_GUARD . with( |guard| guard. id. set( new . id) ) ;
204+ THREAD_GUARD . with( |guard| guard. id. set( id) ) ;
170205 new
171206 }
172207 }
@@ -175,32 +210,27 @@ cfg_if::cfg_if! {
175210#[ test]
176211fn test_thread ( ) {
177212 let thread = Thread :: new ( 0 ) ;
178- assert_eq ! ( thread. id, 0 ) ;
179213 assert_eq ! ( thread. bucket, 0 ) ;
180- assert_eq ! ( thread. bucket_size, 1 ) ;
214+ assert_eq ! ( thread. bucket_size( ) , 1 ) ;
181215 assert_eq ! ( thread. index, 0 ) ;
182216
183217 let thread = Thread :: new ( 1 ) ;
184- assert_eq ! ( thread. id, 1 ) ;
185218 assert_eq ! ( thread. bucket, 1 ) ;
186- assert_eq ! ( thread. bucket_size, 2 ) ;
219+ assert_eq ! ( thread. bucket_size( ) , 2 ) ;
187220 assert_eq ! ( thread. index, 0 ) ;
188221
189222 let thread = Thread :: new ( 2 ) ;
190- assert_eq ! ( thread. id, 2 ) ;
191223 assert_eq ! ( thread. bucket, 1 ) ;
192- assert_eq ! ( thread. bucket_size, 2 ) ;
224+ assert_eq ! ( thread. bucket_size( ) , 2 ) ;
193225 assert_eq ! ( thread. index, 1 ) ;
194226
195227 let thread = Thread :: new ( 3 ) ;
196- assert_eq ! ( thread. id, 3 ) ;
197228 assert_eq ! ( thread. bucket, 2 ) ;
198- assert_eq ! ( thread. bucket_size, 4 ) ;
229+ assert_eq ! ( thread. bucket_size( ) , 4 ) ;
199230 assert_eq ! ( thread. index, 0 ) ;
200231
201232 let thread = Thread :: new ( 19 ) ;
202- assert_eq ! ( thread. id, 19 ) ;
203233 assert_eq ! ( thread. bucket, 4 ) ;
204- assert_eq ! ( thread. bucket_size, 16 ) ;
234+ assert_eq ! ( thread. bucket_size( ) , 16 ) ;
205235 assert_eq ! ( thread. index, 4 ) ;
206236}
0 commit comments