pub fn lock() -> LockGuard
Expand description
Acquire a partially unsound(!!!) global re-entrant lock over backtrace’s internals.
That is, this lock can be acquired as many times as you want on a single thread without deadlocking, allowing one thread to acquire exclusive access to the ability to make backtraces. Calls to this locking function are freely sprinkled in every place where that needs to be enforced.
§Why
This was first introduced to guard uses of Windows’ dbghelp API, which isn’t threadsafe. It’s unclear if other things now rely on this locking.
§How
The basic idea is to have a single global mutex, and a thread_local boolean saying “yep this is the thread that acquired the mutex”.
The first time a thread acquires the lock, it is handed a
LockGuard(Some(..))
that will actually release the lock on Drop.
All subsequence attempts to lock on the same thread will see
that their thread acquired the lock, and get LockGuard(None)
which will do nothing when dropped.
§Safety
As long as you only ever assign the returned LockGuard to a freshly declared local variable, it will do its job correctly, as the “first” LockGuard will strictly outlive all subsequent LockGuards and properly release the lock when the thread is done with backtracing.
However if you ever attempt to store a LockGuard beyond the scope
it was acquired in, it might actually be a LockGuard(None)
that
doesn’t actually hold the lock! In this case another thread might
acquire the lock and you’ll get races this system was intended to
avoid!
This is why this is “partially unsound”. As a public API this would be unacceptable, but this is crate-private, and if you use this in the most obvious and simplistic way it Just Works™.
Note however that std specifically bypasses this lock, and uses
the *_unsynchronized
backtrace APIs. This is “fine” because
it wraps its own calls to backtrace in a non-reentrant Mutex
that prevents two backtraces from getting interleaved during printing.