@@ -145,32 +145,95 @@ mod lock {
145145 use std:: ptr;
146146 use std:: sync:: { Mutex , MutexGuard , Once } ;
147147
148+ /// A "Maybe" LockGuard
148149 pub struct LockGuard ( Option < MutexGuard < ' static , ( ) > > ) ;
149150
151+ /// The global lock, lazily allocated on first use
150152 static mut LOCK : * mut Mutex < ( ) > = ptr:: null_mut ( ) ;
151153 static INIT : Once = Once :: new ( ) ;
154+ // Whether this thread is the one that holds the lock
152155 thread_local ! ( static LOCK_HELD : Cell <bool > = Cell :: new( false ) ) ;
153156
154157 impl Drop for LockGuard {
155158 fn drop ( & mut self ) {
159+ // Don't do anything if we're a LockGuard(None)
156160 if self . 0 . is_some ( ) {
157161 LOCK_HELD . with ( |slot| {
162+ // Immediately crash if we somehow aren't the thread holding this lock
158163 assert ! ( slot. get( ) ) ;
164+ // We are no longer the thread holding this lock
159165 slot. set ( false ) ;
160166 } ) ;
161167 }
168+ // lock implicitly released here, if we're a LockGuard(Some(..))
162169 }
163170 }
164171
172+ /// Acquire a partially unsound(!!!) global re-entrant lock over
173+ /// backtrace's internals.
174+ ///
175+ /// That is, this lock can be acquired as many times as you want
176+ /// on a single thread without deadlocking, allowing one thread
177+ /// to acquire exclusive access to the ability to make backtraces.
178+ /// Calls to this locking function are freely sprinkled in every place
179+ /// where that needs to be enforced.
180+ ///
181+ ///
182+ /// # Why
183+ ///
184+ /// This was first introduced to guard uses of Windows' dbghelp API,
185+ /// which isn't threadsafe. It's unclear if other things now rely on
186+ /// this locking.
187+ ///
188+ ///
189+ /// # How
190+ ///
191+ /// The basic idea is to have a single global mutex, and a thread_local
192+ /// boolean saying "yep this is the thread that acquired the mutex".
193+ ///
194+ /// The first time a thread acquires the lock, it is handed a
195+ /// `LockGuard(Some(..))` that will actually release the lock on Drop.
196+ /// All subsequence attempts to lock on the same thread will see
197+ /// that their thread acquired the lock, and get `LockGuard(None)`
198+ /// which will do nothing when dropped.
199+ ///
200+ ///
201+ /// # Safety
202+ ///
203+ /// As long as you only ever assign the returned LockGuard to a freshly
204+ /// declared local variable, it will do its job correctly, as the "first"
205+ /// LockGuard will strictly outlive all subsequent LockGuards and
206+ /// properly release the lock when the thread is done with backtracing.
207+ ///
208+ /// However if you ever attempt to store a LockGuard beyond the scope
209+ /// it was acquired in, it might actually be a `LockGuard(None)` that
210+ /// doesn't actually hold the lock! In this case another thread might
211+ /// acquire the lock and you'll get races this system was intended to
212+ /// avoid!
213+ ///
214+ /// This is why this is "partially unsound". As a public API this would
215+ /// be unacceptable, but this is crate-private, and if you use this in
216+ /// the most obvious and simplistic way it Just Works™.
217+ ///
218+ /// Note however that std specifically bypasses this lock, and uses
219+ /// the `*_unsynchronized` backtrace APIs. This is "fine" because
220+ /// it wraps its own calls to backtrace in a non-reentrant Mutex
221+ /// that prevents two backtraces from getting interleaved during printing.
165222 pub fn lock ( ) -> LockGuard {
223+ // If we're the thread holding this lock, pretend to acquire the lock
224+ // again by returning a LockGuard(None)
166225 if LOCK_HELD . with ( |l| l. get ( ) ) {
167226 return LockGuard ( None ) ;
168227 }
228+ // Insist that we totally are the thread holding the lock
229+ // (our thread will block until we are)
169230 LOCK_HELD . with ( |s| s. set ( true ) ) ;
170231 unsafe {
232+ // lazily allocate the lock if necessary
171233 INIT . call_once ( || {
172234 LOCK = Box :: into_raw ( Box :: new ( Mutex :: new ( ( ) ) ) ) ;
173235 } ) ;
236+ // ok *actually* try to acquire the lock, blocking as necessary
174237 LockGuard ( Some ( ( * LOCK ) . lock ( ) . unwrap ( ) ) )
175238 }
176239 }
0 commit comments