tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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