@@ -75,9 +75,13 @@ use crate::thread::{current_id, ThreadId};
75
75
// synchronization is left to the mutex, making relaxed memory ordering for
76
76
// the `owner` field fine in all cases.
77
77
//
78
- // On systems without 64 bit atomics we use a simple seqlock to emulate a 64 bit Tid using
79
- // 32 bit atomics (which should be supported on all platforms with `std`). This works
80
- // because only one thread at a time (the one holding the mutex) writes to it.
78
+ // On systems without 64 bit atomics we also store the address of a TLS variable
79
+ // along the 64-bit TID. We then first check that address against the address
80
+ // of that variable on the current thread, and only if they compare equal do we
81
+ // compare the actual TIDs. Because we only ever read the TID on the same thread
82
+ // that it was written on (or a thread sharing the TLS block with that writer thread),
83
+ // we don't need to further synchronize the TID accesses, so they can be regular 64-bit
84
+ // non-atomic accesses.
81
85
#[ unstable( feature = "reentrant_lock" , issue = "121440" ) ]
82
86
pub struct ReentrantLock < T : ?Sized > {
83
87
mutex : sys:: Mutex ,
@@ -103,7 +107,8 @@ cfg_if!(
103
107
}
104
108
105
109
#[ inline]
106
- fn set( & self , tid: Option <ThreadId >) {
110
+ // This is just unsafe to match the API of the Tid type below.
111
+ unsafe fn set( & self , tid: Option <ThreadId >) {
107
112
let value = tid. map_or( 0 , |tid| tid. as_u64( ) . get( ) ) ;
108
113
self . 0 . store( value, Relaxed ) ;
109
114
}
@@ -157,10 +162,11 @@ cfg_if!(
157
162
}
158
163
159
164
#[ inline]
160
- // This may only be called by one thread at a time.
161
- fn set( & self , tid: Option <ThreadId >) {
162
- // It's important that we set `self.tls_addr` to 0 if the
163
- // tid is cleared. Otherwise, there might be race conditions between
165
+ // This may only be called by one thread at a time, and can lead to
166
+ // race conditions otherwise.
167
+ unsafe fn set( & self , tid: Option <ThreadId >) {
168
+ // It's important that we set `self.tls_addr` to 0 if the tid is
169
+ // cleared. Otherwise, there might be race conditions between
164
170
// `set()` and `get()`.
165
171
let tls_addr = if tid. is_some( ) { tls_addr( ) } else { 0 } ;
166
172
let value = tid. map_or( 0 , |tid| tid. as_u64( ) . get( ) ) ;
@@ -273,7 +279,9 @@ impl<T: ?Sized> ReentrantLock<T> {
273
279
/// ```
274
280
pub fn lock ( & self ) -> ReentrantLockGuard < ' _ , T > {
275
281
let this_thread = current_id ( ) ;
276
- // Safety: We only touch lock_count when we own the lock.
282
+ // Safety: We only touch lock_count when we own the inner mutex.
283
+ // Additionally, we only call `self.owner.set()` while holding
284
+ // the inner mutex, so no two threads can call it concurrently.
277
285
unsafe {
278
286
if self . owner . contains ( this_thread) {
279
287
self . increment_lock_count ( ) . expect ( "lock count overflow in reentrant mutex" ) ;
@@ -315,7 +323,9 @@ impl<T: ?Sized> ReentrantLock<T> {
315
323
/// This function does not block.
316
324
pub ( crate ) fn try_lock ( & self ) -> Option < ReentrantLockGuard < ' _ , T > > {
317
325
let this_thread = current_id ( ) ;
318
- // Safety: We only touch lock_count when we own the lock.
326
+ // Safety: We only touch lock_count when we own the inner mutex.
327
+ // Additionally, we only call `self.owner.set()` while holding
328
+ // the inner mutex, so no two threads can call it concurrently.
319
329
unsafe {
320
330
if self . owner . contains ( this_thread) {
321
331
self . increment_lock_count ( ) ?;
0 commit comments