+impl Eq for LockMetadata {}
+impl std::hash::Hash for LockMetadata {
+ fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) { hasher.write_u64(self.lock_idx); }
+}
+
+impl LockMetadata {
+ fn new() -> LockMetadata {
+ LockMetadata {
+ locked_before: StdMutex::new(HashSet::new()),
+ lock_idx: LOCK_IDX.fetch_add(1, Ordering::Relaxed) as u64,
+ #[cfg(feature = "backtrace")]
+ lock_construction_bt: Backtrace::new(),
+ }
+ }
+
+ // Returns whether we were a recursive lock (only relevant for read)
+ fn _pre_lock(this: &Arc<LockMetadata>, read: bool) -> bool {
+ let mut inserted = false;
+ LOCKS_HELD.with(|held| {
+ // For each lock which is currently locked, check that no lock's locked-before
+ // set includes the lock we're about to lock, which would imply a lockorder
+ // inversion.
+ for locked in held.borrow().iter() {
+ if read && *locked == *this {
+ // Recursive read locks are explicitly allowed
+ return;
+ }
+ }
+ for locked in held.borrow().iter() {
+ if !read && *locked == *this {
+ panic!("Tried to lock a lock while it was held!");
+ }
+ for locked_dep in locked.locked_before.lock().unwrap().iter() {
+ if *locked_dep == *this {
+ #[cfg(feature = "backtrace")]
+ panic!("Tried to violate existing lockorder.\nMutex that should be locked after the current lock was created at the following backtrace.\nNote that to get a backtrace for the lockorder violation, you should set RUST_BACKTRACE=1\n{:?}", locked.lock_construction_bt);
+ #[cfg(not(feature = "backtrace"))]
+ panic!("Tried to violate existing lockorder. Build with the backtrace feature for more info.");
+ }
+ }
+ // Insert any already-held locks in our locked-before set.
+ this.locked_before.lock().unwrap().insert(Arc::clone(locked));
+ }
+ held.borrow_mut().insert(Arc::clone(this));
+ inserted = true;
+ });
+ inserted
+ }
+
+ fn pre_lock(this: &Arc<LockMetadata>) { Self::_pre_lock(this, false); }
+ fn pre_read_lock(this: &Arc<LockMetadata>) -> bool { Self::_pre_lock(this, true) }
+
+ fn try_locked(this: &Arc<LockMetadata>) {
+ LOCKS_HELD.with(|held| {
+ // Since a try-lock will simply fail if the lock is held already, we do not
+ // consider try-locks to ever generate lockorder inversions. However, if a try-lock
+ // succeeds, we do consider it to have created lockorder dependencies.
+ for locked in held.borrow().iter() {
+ this.locked_before.lock().unwrap().insert(Arc::clone(locked));
+ }
+ held.borrow_mut().insert(Arc::clone(this));
+ });
+ }