summaryrefslogtreecommitdiffstats
path: root/mozglue/baseprofiler/public/BaseProfilerDetail.h
diff options
context:
space:
mode:
Diffstat (limited to 'mozglue/baseprofiler/public/BaseProfilerDetail.h')
-rw-r--r--mozglue/baseprofiler/public/BaseProfilerDetail.h285
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