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