1 // Copyright 2011 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // This file is used for debugging assertion support. The Lock class 6 // is functionally a wrapper around the LockImpl class, so the only 7 // real intelligence in the class is in the debugging logic. 8 9 #ifdef UNSAFE_BUFFERS_BUILD 10 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors. 11 #pragma allow_unsafe_buffers 12 #endif 13 14 #include "base/synchronization/lock.h" 15 16 #include <cstdint> 17 18 #if DCHECK_IS_ON() 19 #include <array> 20 21 #include "base/synchronization/lock_subtle.h" 22 #include "base/threading/platform_thread.h" 23 24 namespace base { 25 26 namespace { 27 28 // List of locks held by a thread. 29 // 30 // As of May 2024, no more than 5 locks were held simultaneously by a thread in 31 // a test browsing session or while running the CQ (% locks acquired in unit 32 // tests "WaitSetTest.NoStarvation" and 33 // "MessagePipeTest.DataPipeConsumerHandlePingPong"). An array of size 10 is 34 // therefore considered sufficient to track all locks held by a thread. A 35 // dynamic-size array (e.g. owned by a `ThreadLocalOwnedPointer`) would require 36 // handling reentrancy issues with allocator shims that use `base::Lock`. 37 constexpr size_t kHeldLocksCapacity = 10; 38 thread_local std::array<uintptr_t, kHeldLocksCapacity> 39 g_tracked_locks_held_by_thread; 40 41 // Number of non-nullptr elements in `g_tracked_locks_held_by_thread`. 42 thread_local size_t g_num_tracked_locks_held_by_thread = 0; 43 44 } // namespace 45 ~Lock()46Lock::~Lock() { 47 DCHECK(owning_thread_ref_.is_null()); 48 } 49 Acquire(subtle::LockTracking tracking)50void Lock::Acquire(subtle::LockTracking tracking) { 51 lock_.Lock(); 52 if (tracking == subtle::LockTracking::kEnabled) { 53 AddToLocksHeldOnCurrentThread(); 54 } 55 CheckUnheldAndMark(); 56 } 57 Release()58void Lock::Release() { 59 CheckHeldAndUnmark(); 60 if (in_tracked_locks_held_by_current_thread_) { 61 RemoveFromLocksHeldOnCurrentThread(); 62 } 63 lock_.Unlock(); 64 } 65 Try(subtle::LockTracking tracking)66bool Lock::Try(subtle::LockTracking tracking) { 67 const bool rv = lock_.Try(); 68 if (rv) { 69 if (tracking == subtle::LockTracking::kEnabled) { 70 AddToLocksHeldOnCurrentThread(); 71 } 72 CheckUnheldAndMark(); 73 } 74 return rv; 75 } 76 AssertAcquired() const77void Lock::AssertAcquired() const { 78 DCHECK_EQ(owning_thread_ref_, PlatformThread::CurrentRef()); 79 } 80 AssertNotHeld() const81void Lock::AssertNotHeld() const { 82 DCHECK(owning_thread_ref_.is_null()); 83 } 84 CheckHeldAndUnmark()85void Lock::CheckHeldAndUnmark() { 86 DCHECK_EQ(owning_thread_ref_, PlatformThread::CurrentRef()); 87 owning_thread_ref_ = PlatformThreadRef(); 88 } 89 CheckUnheldAndMark()90void Lock::CheckUnheldAndMark() { 91 DCHECK(owning_thread_ref_.is_null()); 92 owning_thread_ref_ = PlatformThread::CurrentRef(); 93 } 94 AddToLocksHeldOnCurrentThread()95void Lock::AddToLocksHeldOnCurrentThread() { 96 CHECK(!in_tracked_locks_held_by_current_thread_); 97 98 // Check if capacity is exceeded. 99 CHECK_LT(g_num_tracked_locks_held_by_thread, kHeldLocksCapacity) 100 << "This thread holds more than " << kHeldLocksCapacity 101 << " tracked locks simultaneously. Reach out to //base OWNERS to " 102 "determine whether `kHeldLocksCapacity` should be increased."; 103 104 // Add to the list of held locks. 105 g_tracked_locks_held_by_thread[g_num_tracked_locks_held_by_thread] = 106 reinterpret_cast<uintptr_t>(this); 107 ++g_num_tracked_locks_held_by_thread; 108 in_tracked_locks_held_by_current_thread_ = true; 109 } 110 RemoveFromLocksHeldOnCurrentThread()111void Lock::RemoveFromLocksHeldOnCurrentThread() { 112 CHECK(in_tracked_locks_held_by_current_thread_); 113 for (size_t i = 0; i < g_num_tracked_locks_held_by_thread; ++i) { 114 // Traverse from the end since locks are typically acquired and released in 115 // opposite order. 116 const size_t index = g_num_tracked_locks_held_by_thread - i - 1; 117 if (g_tracked_locks_held_by_thread[index] == 118 reinterpret_cast<uintptr_t>(this)) { 119 g_tracked_locks_held_by_thread[index] = 120 g_tracked_locks_held_by_thread[g_num_tracked_locks_held_by_thread - 121 1]; 122 g_tracked_locks_held_by_thread[g_num_tracked_locks_held_by_thread - 1] = 123 reinterpret_cast<uintptr_t>(nullptr); 124 --g_num_tracked_locks_held_by_thread; 125 break; 126 } 127 } 128 in_tracked_locks_held_by_current_thread_ = false; 129 } 130 131 namespace subtle { 132 GetTrackedLocksHeldByCurrentThread()133span<const uintptr_t> GetTrackedLocksHeldByCurrentThread() { 134 return span<const uintptr_t>(g_tracked_locks_held_by_thread.begin(), 135 g_num_tracked_locks_held_by_thread); 136 } 137 138 } // namespace subtle 139 140 } // namespace base 141 142 #endif // DCHECK_IS_ON() 143