@@ -160,32 +160,95 @@ mod lock {
160160 use std:: ptr;
161161 use std:: sync:: { Mutex , MutexGuard , Once } ;
162162
163+ /// A "Maybe" LockGuard
163164 pub struct LockGuard ( Option < MutexGuard < ' static , ( ) > > ) ;
164165
166+ /// The global lock, lazily allocated on first use
165167 static mut LOCK : * mut Mutex < ( ) > = ptr:: null_mut ( ) ;
166168 static INIT : Once = Once :: new ( ) ;
169+ // Whether this thread is the one that holds the lock
167170 thread_local ! ( static LOCK_HELD : Cell <bool > = Cell :: new( false ) ) ;
168171
169172 impl Drop for LockGuard {
170173 fn drop ( & mut self ) {
174+ // Don't do anything if we're a LockGuard(None)
171175 if self . 0 . is_some ( ) {
172176 LOCK_HELD . with ( |slot| {
177+ // Immediately crash if we somehow aren't the thread holding this lock
173178 assert ! ( slot. get( ) ) ;
179+ // We are no longer the thread holding this lock
174180 slot. set ( false ) ;
175181 } ) ;
176182 }
183+ // lock implicitly released here, if we're a LockGuard(Some(..))
177184 }
178185 }
179186
187+ /// Acquire a partially unsound(!!!) global re-entrant lock over
188+ /// backtrace's internals.
189+ ///
190+ /// That is, this lock can be acquired as many times as you want
191+ /// on a single thread without deadlocking, allowing one thread
192+ /// to acquire exclusive access to the ability to make backtraces.
193+ /// Calls to this locking function are freely sprinkled in every place
194+ /// where that needs to be enforced.
195+ ///
196+ ///
197+ /// # Why
198+ ///
199+ /// This was first introduced to guard uses of Windows' dbghelp API,
200+ /// which isn't threadsafe. It's unclear if other things now rely on
201+ /// this locking.
202+ ///
203+ ///
204+ /// # How
205+ ///
206+ /// The basic idea is to have a single global mutex, and a thread_local
207+ /// boolean saying "yep this is the thread that acquired the mutex".
208+ ///
209+ /// The first time a thread acquires the lock, it is handed a
210+ /// `LockGuard(Some(..))` that will actually release the lock on Drop.
211+ /// All subsequence attempts to lock on the same thread will see
212+ /// that their thread acquired the lock, and get `LockGuard(None)`
213+ /// which will do nothing when dropped.
214+ ///
215+ ///
216+ /// # Safety
217+ ///
218+ /// As long as you only ever assign the returned LockGuard to a freshly
219+ /// declared local variable, it will do its job correctly, as the "first"
220+ /// LockGuard will strictly outlive all subsequent LockGuards and
221+ /// properly release the lock when the thread is done with backtracing.
222+ ///
223+ /// However if you ever attempt to store a LockGuard beyond the scope
224+ /// it was acquired in, it might actually be a `LockGuard(None)` that
225+ /// doesn't actually hold the lock! In this case another thread might
226+ /// acquire the lock and you'll get races this system was intended to
227+ /// avoid!
228+ ///
229+ /// This is why this is "partially unsound". As a public API this would
230+ /// be unacceptable, but this is crate-private, and if you use this in
231+ /// the most obvious and simplistic way it Just Works™.
232+ ///
233+ /// Note however that std specifically bypasses this lock, and uses
234+ /// the `*_unsynchronized` backtrace APIs. This is "fine" because
235+ /// it wraps its own calls to backtrace in a non-reentrant Mutex
236+ /// that prevents two backtraces from getting interleaved during printing.
180237 pub fn lock ( ) -> LockGuard {
238+ // If we're the thread holding this lock, pretend to acquire the lock
239+ // again by returning a LockGuard(None)
181240 if LOCK_HELD . with ( |l| l. get ( ) ) {
182241 return LockGuard ( None ) ;
183242 }
243+ // Insist that we totally are the thread holding the lock
244+ // (our thread will block until we are)
184245 LOCK_HELD . with ( |s| s. set ( true ) ) ;
185246 unsafe {
247+ // lazily allocate the lock if necessary
186248 INIT . call_once ( || {
187249 LOCK = Box :: into_raw ( Box :: new ( Mutex :: new ( ( ) ) ) ) ;
188250 } ) ;
251+ // ok *actually* try to acquire the lock, blocking as necessary
189252 LockGuard ( Some ( ( * LOCK ) . lock ( ) . unwrap ( ) ) )
190253 }
191254 }
0 commit comments