diff options
Diffstat (limited to 'mozglue/baseprofiler/public/BaseProfilerDetail.h')
-rw-r--r-- | mozglue/baseprofiler/public/BaseProfilerDetail.h | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/mozglue/baseprofiler/public/BaseProfilerDetail.h b/mozglue/baseprofiler/public/BaseProfilerDetail.h new file mode 100644 index 0000000000..6ecf6e117b --- /dev/null +++ b/mozglue/baseprofiler/public/BaseProfilerDetail.h @@ -0,0 +1,285 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Internal Base Profiler utilities. + +#ifndef BaseProfilerDetail_h +#define BaseProfilerDetail_h + +#include "mozilla/Atomics.h" +#include "mozilla/Attributes.h" +#include "mozilla/Maybe.h" +#include "mozilla/PlatformMutex.h" +#include "mozilla/PlatformRWLock.h" +#include "mozilla/BaseProfilerUtils.h" + +namespace mozilla { +namespace baseprofiler { + +namespace detail { + +// Thin shell around mozglue PlatformMutex, for Base Profiler internal use. +class MOZ_CAPABILITY("mutex") BaseProfilerMutex + : private ::mozilla::detail::MutexImpl { + public: + BaseProfilerMutex() : ::mozilla::detail::MutexImpl() {} + explicit BaseProfilerMutex(const char* aName) + : ::mozilla::detail::MutexImpl(), mName(aName) {} + + BaseProfilerMutex(const BaseProfilerMutex&) = delete; + BaseProfilerMutex& operator=(const BaseProfilerMutex&) = delete; + BaseProfilerMutex(BaseProfilerMutex&&) = delete; + BaseProfilerMutex& operator=(BaseProfilerMutex&&) = delete; + +#ifdef DEBUG + ~BaseProfilerMutex() { + MOZ_ASSERT(!BaseProfilerThreadId::FromNumber(mOwningThreadId).IsSpecified(), + "BaseProfilerMutex should have been unlocked when destroyed"); + } +#endif // DEBUG + + [[nodiscard]] bool IsLockedOnCurrentThread() const { + return BaseProfilerThreadId::FromNumber(mOwningThreadId) == + baseprofiler::profiler_current_thread_id(); + } + + void AssertCurrentThreadOwns() const MOZ_ASSERT_CAPABILITY(this) { + MOZ_ASSERT(IsLockedOnCurrentThread()); + } + + void Lock() MOZ_CAPABILITY_ACQUIRE() { + const BaseProfilerThreadId tid = baseprofiler::profiler_current_thread_id(); + MOZ_ASSERT(tid.IsSpecified()); + MOZ_ASSERT(!IsLockedOnCurrentThread(), "Recursive locking"); + ::mozilla::detail::MutexImpl::lock(); + MOZ_ASSERT(!BaseProfilerThreadId::FromNumber(mOwningThreadId).IsSpecified(), + "Not unlocked properly"); + mOwningThreadId = tid.ToNumber(); + } + + [[nodiscard]] bool TryLock() MOZ_TRY_ACQUIRE(true) { + const BaseProfilerThreadId tid = baseprofiler::profiler_current_thread_id(); + MOZ_ASSERT(tid.IsSpecified()); + MOZ_ASSERT(!IsLockedOnCurrentThread(), "Recursive locking"); + if (!::mozilla::detail::MutexImpl::tryLock()) { + // Failed to lock, nothing more to do. + return false; + } + MOZ_ASSERT(!BaseProfilerThreadId::FromNumber(mOwningThreadId).IsSpecified(), + "Not unlocked properly"); + mOwningThreadId = tid.ToNumber(); + return true; + } + + void Unlock() MOZ_CAPABILITY_RELEASE() { + MOZ_ASSERT(IsLockedOnCurrentThread(), "Unlocking when not locked here"); + // We're still holding the mutex here, so it's safe to just reset + // `mOwningThreadId`. + mOwningThreadId = BaseProfilerThreadId{}.ToNumber(); + ::mozilla::detail::MutexImpl::unlock(); + } + + const char* GetName() const { return mName; } + + private: + // Thread currently owning the lock, or 0. + // Atomic because it may be read at any time independent of the mutex. + // Relaxed because threads only need to know if they own it already, so: + // - If it's their id, only *they* wrote that value with a locked mutex. + // - If it's different from their thread id it doesn't matter what other + // number it is (0 or another id) and that it can change again at any time. + Atomic<typename BaseProfilerThreadId::NumberType, MemoryOrdering::Relaxed> + mOwningThreadId; + + const char* mName = nullptr; +}; + +// RAII class to lock a mutex. +class MOZ_RAII BaseProfilerAutoLock { + public: + explicit BaseProfilerAutoLock(BaseProfilerMutex& aMutex) : mMutex(aMutex) { + mMutex.Lock(); + } + + BaseProfilerAutoLock(const BaseProfilerAutoLock&) = delete; + BaseProfilerAutoLock& operator=(const BaseProfilerAutoLock&) = delete; + BaseProfilerAutoLock(BaseProfilerAutoLock&&) = delete; + BaseProfilerAutoLock& operator=(BaseProfilerAutoLock&&) = delete; + + ~BaseProfilerAutoLock() { mMutex.Unlock(); } + + private: + BaseProfilerMutex& mMutex; +}; + +// Thin shell around mozglue PlatformMutex, for Base Profiler internal use. +// Actual mutex may be disabled at construction time. +class BaseProfilerMaybeMutex : private ::mozilla::detail::MutexImpl { + public: + explicit BaseProfilerMaybeMutex(bool aActivate) { + if (aActivate) { + mMaybeMutex.emplace(); + } + } + + BaseProfilerMaybeMutex(const BaseProfilerMaybeMutex&) = delete; + BaseProfilerMaybeMutex& operator=(const BaseProfilerMaybeMutex&) = delete; + BaseProfilerMaybeMutex(BaseProfilerMaybeMutex&&) = delete; + BaseProfilerMaybeMutex& operator=(BaseProfilerMaybeMutex&&) = delete; + + ~BaseProfilerMaybeMutex() = default; + + bool IsActivated() const { return mMaybeMutex.isSome(); } + + [[nodiscard]] bool IsActivatedAndLockedOnCurrentThread() const { + if (!IsActivated()) { + // Not activated, so we can never be locked. + return false; + } + return mMaybeMutex->IsLockedOnCurrentThread(); + } + + void AssertCurrentThreadOwns() const { +#ifdef DEBUG + if (IsActivated()) { + mMaybeMutex->AssertCurrentThreadOwns(); + } +#endif // DEBUG + } + + MOZ_PUSH_IGNORE_THREAD_SAFETY + void Lock() { + if (IsActivated()) { + mMaybeMutex->Lock(); + } + } + + void Unlock() { + if (IsActivated()) { + mMaybeMutex->Unlock(); + } + } + MOZ_POP_THREAD_SAFETY + + private: + Maybe<BaseProfilerMutex> mMaybeMutex; +}; + +// RAII class to lock a mutex. +class MOZ_RAII BaseProfilerMaybeAutoLock { + public: + explicit BaseProfilerMaybeAutoLock(BaseProfilerMaybeMutex& aMaybeMutex) + : mMaybeMutex(aMaybeMutex) { + mMaybeMutex.Lock(); + } + + BaseProfilerMaybeAutoLock(const BaseProfilerMaybeAutoLock&) = delete; + BaseProfilerMaybeAutoLock& operator=(const BaseProfilerMaybeAutoLock&) = + delete; + BaseProfilerMaybeAutoLock(BaseProfilerMaybeAutoLock&&) = delete; + BaseProfilerMaybeAutoLock& operator=(BaseProfilerMaybeAutoLock&&) = delete; + + ~BaseProfilerMaybeAutoLock() { mMaybeMutex.Unlock(); } + + private: + BaseProfilerMaybeMutex& mMaybeMutex; +}; + +class BaseProfilerSharedMutex : public ::mozilla::detail::RWLockImpl { + public: +#ifdef DEBUG + ~BaseProfilerSharedMutex() { + MOZ_ASSERT(!BaseProfilerThreadId::FromNumber(mOwningThreadId).IsSpecified(), + "BaseProfilerMutex should have been unlocked when destroyed"); + } +#endif // DEBUG + + [[nodiscard]] bool IsLockedExclusiveOnCurrentThread() const { + return BaseProfilerThreadId::FromNumber(mOwningThreadId) == + baseprofiler::profiler_current_thread_id(); + } + + void LockExclusive() { + const BaseProfilerThreadId tid = baseprofiler::profiler_current_thread_id(); + MOZ_ASSERT(tid.IsSpecified()); + MOZ_ASSERT(!IsLockedExclusiveOnCurrentThread(), "Recursive locking"); + ::mozilla::detail::RWLockImpl::writeLock(); + MOZ_ASSERT(!BaseProfilerThreadId::FromNumber(mOwningThreadId).IsSpecified(), + "Not unlocked properly"); + mOwningThreadId = tid.ToNumber(); + } + + void UnlockExclusive() { + MOZ_ASSERT(IsLockedExclusiveOnCurrentThread(), + "Unlocking when not locked here"); + // We're still holding the mutex here, so it's safe to just reset + // `mOwningThreadId`. + mOwningThreadId = BaseProfilerThreadId{}.ToNumber(); + writeUnlock(); + } + + void LockShared() { readLock(); } + + void UnlockShared() { readUnlock(); } + + private: + // Thread currently owning the exclusive lock, or 0. + // Atomic because it may be read at any time independent of the mutex. + // Relaxed because threads only need to know if they own it already, so: + // - If it's their id, only *they* wrote that value with a locked mutex. + // - If it's different from their thread id it doesn't matter what other + // number it is (0 or another id) and that it can change again at any time. + Atomic<typename BaseProfilerThreadId::NumberType, MemoryOrdering::Relaxed> + mOwningThreadId; +}; + +// RAII class to lock a shared mutex exclusively. +class MOZ_RAII BaseProfilerAutoLockExclusive { + public: + explicit BaseProfilerAutoLockExclusive(BaseProfilerSharedMutex& aSharedMutex) + : mSharedMutex(aSharedMutex) { + mSharedMutex.LockExclusive(); + } + + BaseProfilerAutoLockExclusive(const BaseProfilerAutoLockExclusive&) = delete; + BaseProfilerAutoLockExclusive& operator=( + const BaseProfilerAutoLockExclusive&) = delete; + BaseProfilerAutoLockExclusive(BaseProfilerAutoLockExclusive&&) = delete; + BaseProfilerAutoLockExclusive& operator=(BaseProfilerAutoLockExclusive&&) = + delete; + + ~BaseProfilerAutoLockExclusive() { mSharedMutex.UnlockExclusive(); } + + private: + BaseProfilerSharedMutex& mSharedMutex; +}; + +// RAII class to lock a shared mutex non-exclusively, other +// BaseProfilerAutoLockShared's may happen in other threads. +class MOZ_RAII BaseProfilerAutoLockShared { + public: + explicit BaseProfilerAutoLockShared(BaseProfilerSharedMutex& aSharedMutex) + : mSharedMutex(aSharedMutex) { + mSharedMutex.LockShared(); + } + + BaseProfilerAutoLockShared(const BaseProfilerAutoLockShared&) = delete; + BaseProfilerAutoLockShared& operator=(const BaseProfilerAutoLockShared&) = + delete; + BaseProfilerAutoLockShared(BaseProfilerAutoLockShared&&) = delete; + BaseProfilerAutoLockShared& operator=(BaseProfilerAutoLockShared&&) = delete; + + ~BaseProfilerAutoLockShared() { mSharedMutex.UnlockShared(); } + + private: + BaseProfilerSharedMutex& mSharedMutex; +}; + +} // namespace detail +} // namespace baseprofiler +} // namespace mozilla + +#endif // BaseProfilerDetail_h |