DCHECK that a Mutex is not locked when destroyed

Destroying a locked Mutex is undefined behavior and is already flagged by
ThreadSanitizer via `ABSL_TSAN_MUTEX_DESTROY`. Previously, non-TSan debug builds
tolerated it and called `ForgetDeadlockInfo()`, which left stale entries in the
holding thread's `SynchLocksHeld` list (filling all 40 slots in
`Mutex.LockedMutexDestructionBug` and disabling deadlock detection on that
thread for the rest of the process).

In debug builds (`!defined(NDEBUG)`), `Mutex::Dtor()` now `ABSL_RAW_DCHECK`s
that the `Mutex` is not held in exclusive or shared mode when destroyed.
`Mutex.LockedMutexDestructionBug` and `Mutex.DeadlockIdBug` are replaced with
`Mutex.LockedMutexDestructionDeathTest`.

PiperOrigin-RevId: 1001021469
diff --git a/absl/synchronization/mutex.cc b/absl/synchronization/mutex.cc
index d6e2aac..0286198 100644
--- a/absl/synchronization/mutex.cc
+++ b/absl/synchronization/mutex.cc
@@ -756,6 +756,9 @@
 #if !defined(NDEBUG) || defined(ABSL_HAVE_THREAD_SANITIZER) || \
     defined(ABSL_BUILD_DLL)
 void Mutex::Dtor() {
+  ABSL_RAW_DCHECK(
+      (mu_.load(std::memory_order_relaxed) & (kMuWriter | kMuReader)) == 0,
+      "Mutex destroyed while locked");
   if (kDebugMode) {
     this->ForgetDeadlockInfo();
   }
diff --git a/absl/synchronization/mutex_test.cc b/absl/synchronization/mutex_test.cc
index a96bcff..e71fa90 100644
--- a/absl/synchronization/mutex_test.cc
+++ b/absl/synchronization/mutex_test.cc
@@ -856,27 +856,22 @@
 }
 #endif  // !ABSL_MUTEX_READER_LOCK_IS_EXCLUSIVE
 
-// Test that we correctly handle the situation when a lock is
-// held and then destroyed (w/o unlocking).
-#ifdef ABSL_HAVE_THREAD_SANITIZER
-// TSAN reports errors when locked Mutexes are destroyed.
-TEST(Mutex, DISABLED_LockedMutexDestructionBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {
-#else
-TEST(Mutex, LockedMutexDestructionBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {
-#endif
-  for (int i = 0; i != 10; i++) {
-    // Create, lock and destroy 10 locks.
-    const int kNumLocks = 10;
-    auto mu = std::make_unique<absl::Mutex[]>(kNumLocks);
-    for (int j = 0; j != kNumLocks; j++) {
-      if ((j % 2) == 0) {
-        mu[j].lock();
-      } else {
-        mu[j].lock_shared();
-      }
-    }
-  }
+#if !defined(NDEBUG) && !defined(ABSL_HAVE_THREAD_SANITIZER)
+TEST(Mutex, LockedMutexDestructionDeathTest) ABSL_NO_THREAD_SAFETY_ANALYSIS {
+  EXPECT_DEATH_IF_SUPPORTED(
+      {
+        absl::Mutex mu;
+        mu.lock();
+      },
+      "Mutex destroyed while locked");
+  EXPECT_DEATH_IF_SUPPORTED(
+      {
+        absl::Mutex mu;
+        mu.lock_shared();
+      },
+      "Mutex destroyed while locked");
 }
+#endif
 
 // Some functions taking pointers to non-const.
 bool Equals42(int *p) { return *p == 42; }
@@ -1318,44 +1313,6 @@
   }
 }
 
-#ifdef ABSL_HAVE_THREAD_SANITIZER
-// TSAN reports errors when locked Mutexes are destroyed.
-TEST(Mutex, DISABLED_DeadlockIdBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {
-#else
-TEST(Mutex, DeadlockIdBug) ABSL_NO_THREAD_SAFETY_ANALYSIS {
-#endif
-  // Test a scenario where a cached deadlock graph node id in the
-  // list of held locks is not invalidated when the corresponding
-  // mutex is deleted.
-  absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort);
-  // Mutex that will be destroyed while being held
-  absl::Mutex *a = new absl::Mutex;
-  // Other mutexes needed by test
-  absl::Mutex b, c;
-
-  // Hold mutex.
-  a->lock();
-
-  // Force deadlock id assignment by acquiring another lock.
-  b.lock();
-  b.unlock();
-
-  // Delete the mutex. The Mutex destructor tries to remove held locks,
-  // but the attempt isn't foolproof.  It can fail if:
-  //   (a) Deadlock detection is currently disabled.
-  //   (b) The destruction is from another thread.
-  // We exploit (a) by temporarily disabling deadlock detection.
-  absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kIgnore);
-  delete a;
-  absl::SetMutexDeadlockDetectionMode(absl::OnDeadlockCycle::kAbort);
-
-  // Now acquire another lock which will force a deadlock id assignment.
-  // We should end up getting assigned the same deadlock id that was
-  // freed up when "a" was deleted, which will cause a spurious deadlock
-  // report if the held lock entry for "a" was not invalidated.
-  c.lock();
-  c.unlock();
-}
 
 // --------------------------------------------------------
 // Test for timeouts/deadlines on condition waits that are specified using