+
+impl LockMetadata {
+ fn new() -> Arc<LockMetadata> {
+ let backtrace = Backtrace::new();
+ let lock_idx = LOCK_IDX.fetch_add(1, Ordering::Relaxed) as u64;
+
+ let res = Arc::new(LockMetadata {
+ locked_before: StdMutex::new(HashMap::new()),
+ lock_idx,
+ _lock_construction_bt: backtrace,
+ });
+
+ #[cfg(feature = "backtrace")]
+ {
+ let lock_constr_location = get_construction_location(&res._lock_construction_bt);
+ LOCKS_INIT.call_once(|| { unsafe { LOCKS = Some(StdMutex::new(HashMap::new())); } });
+ let mut locks = unsafe { LOCKS.as_ref() }.unwrap().lock().unwrap();
+ match locks.entry(lock_constr_location) {
+ hash_map::Entry::Occupied(e) => return Arc::clone(e.get()),
+ hash_map::Entry::Vacant(e) => { e.insert(Arc::clone(&res)); },
+ }
+ }
+ res
+ }
+
+ // 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_idx, _locked) in held.borrow().iter() {
+ if read && *locked_idx == this.lock_idx {
+ // Recursive read locks are explicitly allowed
+ return;
+ }
+ }
+ for (locked_idx, locked) in held.borrow().iter() {
+ if !read && *locked_idx == this.lock_idx {
+ // With `feature = "backtrace"` set, we may be looking at different instances
+ // of the same lock.
+ debug_assert!(cfg!(feature = "backtrace"), "Tried to acquire a lock while it was held!");
+ }
+ for (locked_dep_idx, _locked_dep) in locked.locked_before.lock().unwrap().iter() {
+ if *locked_dep_idx == this.lock_idx && *locked_dep_idx != locked.lock_idx {
+ #[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\nLock being taken constructed at: {} ({}):\n{:?}\nLock constructed at: {} ({})\n{:?}\n\nLock dep created at:\n{:?}\n\n",
+ get_construction_location(&this._lock_construction_bt), this.lock_idx, this._lock_construction_bt,
+ get_construction_location(&locked._lock_construction_bt), locked.lock_idx, locked._lock_construction_bt,
+ _locked_dep._lockdep_trace);
+ #[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.
+ let mut locked_before = this.locked_before.lock().unwrap();
+ if !locked_before.contains_key(&locked.lock_idx) {
+ let lockdep = LockDep { lock: Arc::clone(locked), _lockdep_trace: Backtrace::new() };
+ locked_before.insert(lockdep.lock.lock_idx, lockdep);
+ }
+ }
+ held.borrow_mut().insert(this.lock_idx, 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.
+ let mut locked_before = this.locked_before.lock().unwrap();
+ for (locked_idx, locked) in held.borrow().iter() {
+ if !locked_before.contains_key(locked_idx) {
+ let lockdep = LockDep { lock: Arc::clone(locked), _lockdep_trace: Backtrace::new() };
+ locked_before.insert(*locked_idx, lockdep);
+ }
+ }
+ held.borrow_mut().insert(this.lock_idx, Arc::clone(this));
+ });
+ }