BaseProfilerDetail.h (9669B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 // Internal Base Profiler utilities. 8 9 #ifndef BaseProfilerDetail_h 10 #define BaseProfilerDetail_h 11 12 #include "mozilla/Atomics.h" 13 #include "mozilla/Attributes.h" 14 #include "mozilla/Maybe.h" 15 #include "mozilla/PlatformMutex.h" 16 #include "mozilla/PlatformRWLock.h" 17 #include "mozilla/BaseProfilerUtils.h" 18 19 namespace mozilla { 20 namespace baseprofiler { 21 22 namespace detail { 23 24 // Thin shell around mozglue PlatformMutex, for Base Profiler internal use. 25 class MOZ_CAPABILITY("mutex") BaseProfilerMutex 26 : private ::mozilla::detail::MutexImpl { 27 public: 28 BaseProfilerMutex() : ::mozilla::detail::MutexImpl() {} 29 explicit BaseProfilerMutex(const char* aName) 30 : ::mozilla::detail::MutexImpl(), mName(aName) {} 31 32 BaseProfilerMutex(const BaseProfilerMutex&) = delete; 33 BaseProfilerMutex& operator=(const BaseProfilerMutex&) = delete; 34 BaseProfilerMutex(BaseProfilerMutex&&) = delete; 35 BaseProfilerMutex& operator=(BaseProfilerMutex&&) = delete; 36 37 #ifdef DEBUG 38 ~BaseProfilerMutex() { 39 MOZ_ASSERT(!BaseProfilerThreadId::FromNumber(mOwningThreadId).IsSpecified(), 40 "BaseProfilerMutex should have been unlocked when destroyed"); 41 } 42 #endif // DEBUG 43 44 [[nodiscard]] bool IsLockedOnCurrentThread() const { 45 return BaseProfilerThreadId::FromNumber(mOwningThreadId) == 46 baseprofiler::profiler_current_thread_id(); 47 } 48 49 void AssertCurrentThreadOwns() const MOZ_ASSERT_CAPABILITY(this) { 50 MOZ_ASSERT(IsLockedOnCurrentThread()); 51 } 52 53 void Lock() MOZ_CAPABILITY_ACQUIRE() { 54 const BaseProfilerThreadId tid = baseprofiler::profiler_current_thread_id(); 55 MOZ_ASSERT(tid.IsSpecified()); 56 MOZ_ASSERT(!IsLockedOnCurrentThread(), "Recursive locking"); 57 ::mozilla::detail::MutexImpl::lock(); 58 MOZ_ASSERT(!BaseProfilerThreadId::FromNumber(mOwningThreadId).IsSpecified(), 59 "Not unlocked properly"); 60 mOwningThreadId = tid.ToNumber(); 61 } 62 63 [[nodiscard]] bool TryLock() MOZ_TRY_ACQUIRE(true) { 64 const BaseProfilerThreadId tid = baseprofiler::profiler_current_thread_id(); 65 MOZ_ASSERT(tid.IsSpecified()); 66 MOZ_ASSERT(!IsLockedOnCurrentThread(), "Recursive locking"); 67 if (!::mozilla::detail::MutexImpl::tryLock()) { 68 // Failed to lock, nothing more to do. 69 return false; 70 } 71 MOZ_ASSERT(!BaseProfilerThreadId::FromNumber(mOwningThreadId).IsSpecified(), 72 "Not unlocked properly"); 73 mOwningThreadId = tid.ToNumber(); 74 return true; 75 } 76 77 void Unlock() MOZ_CAPABILITY_RELEASE() { 78 MOZ_ASSERT(IsLockedOnCurrentThread(), "Unlocking when not locked here"); 79 // We're still holding the mutex here, so it's safe to just reset 80 // `mOwningThreadId`. 81 mOwningThreadId = BaseProfilerThreadId{}.ToNumber(); 82 ::mozilla::detail::MutexImpl::unlock(); 83 } 84 85 const char* GetName() const { return mName; } 86 87 private: 88 // Thread currently owning the lock, or 0. 89 // Atomic because it may be read at any time independent of the mutex. 90 // Relaxed because threads only need to know if they own it already, so: 91 // - If it's their id, only *they* wrote that value with a locked mutex. 92 // - If it's different from their thread id it doesn't matter what other 93 // number it is (0 or another id) and that it can change again at any time. 94 Atomic<typename BaseProfilerThreadId::NumberType, MemoryOrdering::Relaxed> 95 mOwningThreadId; 96 97 const char* mName = nullptr; 98 }; 99 100 // RAII class to lock a mutex. 101 class MOZ_RAII BaseProfilerAutoLock { 102 public: 103 explicit BaseProfilerAutoLock(BaseProfilerMutex& aMutex) : mMutex(aMutex) { 104 mMutex.Lock(); 105 } 106 107 BaseProfilerAutoLock(const BaseProfilerAutoLock&) = delete; 108 BaseProfilerAutoLock& operator=(const BaseProfilerAutoLock&) = delete; 109 BaseProfilerAutoLock(BaseProfilerAutoLock&&) = delete; 110 BaseProfilerAutoLock& operator=(BaseProfilerAutoLock&&) = delete; 111 112 ~BaseProfilerAutoLock() { mMutex.Unlock(); } 113 114 private: 115 BaseProfilerMutex& mMutex; 116 }; 117 118 // Thin shell around mozglue PlatformMutex, for Base Profiler internal use. 119 // Actual mutex may be disabled at construction time. 120 class BaseProfilerMaybeMutex : private ::mozilla::detail::MutexImpl { 121 public: 122 explicit BaseProfilerMaybeMutex(bool aActivate) { 123 if (aActivate) { 124 mMaybeMutex.emplace(); 125 } 126 } 127 128 BaseProfilerMaybeMutex(const BaseProfilerMaybeMutex&) = delete; 129 BaseProfilerMaybeMutex& operator=(const BaseProfilerMaybeMutex&) = delete; 130 BaseProfilerMaybeMutex(BaseProfilerMaybeMutex&&) = delete; 131 BaseProfilerMaybeMutex& operator=(BaseProfilerMaybeMutex&&) = delete; 132 133 ~BaseProfilerMaybeMutex() = default; 134 135 bool IsActivated() const { return mMaybeMutex.isSome(); } 136 137 [[nodiscard]] bool IsActivatedAndLockedOnCurrentThread() const { 138 if (!IsActivated()) { 139 // Not activated, so we can never be locked. 140 return false; 141 } 142 return mMaybeMutex->IsLockedOnCurrentThread(); 143 } 144 145 void AssertCurrentThreadOwns() const { 146 #ifdef DEBUG 147 if (IsActivated()) { 148 mMaybeMutex->AssertCurrentThreadOwns(); 149 } 150 #endif // DEBUG 151 } 152 153 MOZ_PUSH_IGNORE_THREAD_SAFETY 154 void Lock() { 155 if (IsActivated()) { 156 mMaybeMutex->Lock(); 157 } 158 } 159 160 void Unlock() { 161 if (IsActivated()) { 162 mMaybeMutex->Unlock(); 163 } 164 } 165 MOZ_POP_THREAD_SAFETY 166 167 private: 168 Maybe<BaseProfilerMutex> mMaybeMutex; 169 }; 170 171 // RAII class to lock a mutex. 172 class MOZ_RAII BaseProfilerMaybeAutoLock { 173 public: 174 explicit BaseProfilerMaybeAutoLock(BaseProfilerMaybeMutex& aMaybeMutex) 175 : mMaybeMutex(aMaybeMutex) { 176 mMaybeMutex.Lock(); 177 } 178 179 BaseProfilerMaybeAutoLock(const BaseProfilerMaybeAutoLock&) = delete; 180 BaseProfilerMaybeAutoLock& operator=(const BaseProfilerMaybeAutoLock&) = 181 delete; 182 BaseProfilerMaybeAutoLock(BaseProfilerMaybeAutoLock&&) = delete; 183 BaseProfilerMaybeAutoLock& operator=(BaseProfilerMaybeAutoLock&&) = delete; 184 185 ~BaseProfilerMaybeAutoLock() { mMaybeMutex.Unlock(); } 186 187 private: 188 BaseProfilerMaybeMutex& mMaybeMutex; 189 }; 190 191 class BaseProfilerSharedMutex : public ::mozilla::detail::RWLockImpl { 192 public: 193 #ifdef DEBUG 194 ~BaseProfilerSharedMutex() { 195 MOZ_ASSERT(!BaseProfilerThreadId::FromNumber(mOwningThreadId).IsSpecified(), 196 "BaseProfilerMutex should have been unlocked when destroyed"); 197 } 198 #endif // DEBUG 199 200 [[nodiscard]] bool IsLockedExclusiveOnCurrentThread() const { 201 return BaseProfilerThreadId::FromNumber(mOwningThreadId) == 202 baseprofiler::profiler_current_thread_id(); 203 } 204 205 void LockExclusive() { 206 const BaseProfilerThreadId tid = baseprofiler::profiler_current_thread_id(); 207 MOZ_ASSERT(tid.IsSpecified()); 208 MOZ_ASSERT(!IsLockedExclusiveOnCurrentThread(), "Recursive locking"); 209 ::mozilla::detail::RWLockImpl::writeLock(); 210 MOZ_ASSERT(!BaseProfilerThreadId::FromNumber(mOwningThreadId).IsSpecified(), 211 "Not unlocked properly"); 212 mOwningThreadId = tid.ToNumber(); 213 } 214 215 void UnlockExclusive() { 216 MOZ_ASSERT(IsLockedExclusiveOnCurrentThread(), 217 "Unlocking when not locked here"); 218 // We're still holding the mutex here, so it's safe to just reset 219 // `mOwningThreadId`. 220 mOwningThreadId = BaseProfilerThreadId{}.ToNumber(); 221 writeUnlock(); 222 } 223 224 void LockShared() { readLock(); } 225 226 void UnlockShared() { readUnlock(); } 227 228 private: 229 // Thread currently owning the exclusive lock, or 0. 230 // Atomic because it may be read at any time independent of the mutex. 231 // Relaxed because threads only need to know if they own it already, so: 232 // - If it's their id, only *they* wrote that value with a locked mutex. 233 // - If it's different from their thread id it doesn't matter what other 234 // number it is (0 or another id) and that it can change again at any time. 235 Atomic<typename BaseProfilerThreadId::NumberType, MemoryOrdering::Relaxed> 236 mOwningThreadId; 237 }; 238 239 // RAII class to lock a shared mutex exclusively. 240 class MOZ_RAII BaseProfilerAutoLockExclusive { 241 public: 242 explicit BaseProfilerAutoLockExclusive(BaseProfilerSharedMutex& aSharedMutex) 243 : mSharedMutex(aSharedMutex) { 244 mSharedMutex.LockExclusive(); 245 } 246 247 BaseProfilerAutoLockExclusive(const BaseProfilerAutoLockExclusive&) = delete; 248 BaseProfilerAutoLockExclusive& operator=( 249 const BaseProfilerAutoLockExclusive&) = delete; 250 BaseProfilerAutoLockExclusive(BaseProfilerAutoLockExclusive&&) = delete; 251 BaseProfilerAutoLockExclusive& operator=(BaseProfilerAutoLockExclusive&&) = 252 delete; 253 254 ~BaseProfilerAutoLockExclusive() { mSharedMutex.UnlockExclusive(); } 255 256 private: 257 BaseProfilerSharedMutex& mSharedMutex; 258 }; 259 260 // RAII class to lock a shared mutex non-exclusively, other 261 // BaseProfilerAutoLockShared's may happen in other threads. 262 class MOZ_RAII BaseProfilerAutoLockShared { 263 public: 264 explicit BaseProfilerAutoLockShared(BaseProfilerSharedMutex& aSharedMutex) 265 : mSharedMutex(aSharedMutex) { 266 mSharedMutex.LockShared(); 267 } 268 269 BaseProfilerAutoLockShared(const BaseProfilerAutoLockShared&) = delete; 270 BaseProfilerAutoLockShared& operator=(const BaseProfilerAutoLockShared&) = 271 delete; 272 BaseProfilerAutoLockShared(BaseProfilerAutoLockShared&&) = delete; 273 BaseProfilerAutoLockShared& operator=(BaseProfilerAutoLockShared&&) = delete; 274 275 ~BaseProfilerAutoLockShared() { mSharedMutex.UnlockShared(); } 276 277 private: 278 BaseProfilerSharedMutex& mSharedMutex; 279 }; 280 281 } // namespace detail 282 } // namespace baseprofiler 283 } // namespace mozilla 284 285 #endif // BaseProfilerDetail_h