From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- mozglue/baseprofiler/core/RegisteredThread.h | 164 +++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 mozglue/baseprofiler/core/RegisteredThread.h (limited to 'mozglue/baseprofiler/core/RegisteredThread.h') diff --git a/mozglue/baseprofiler/core/RegisteredThread.h b/mozglue/baseprofiler/core/RegisteredThread.h new file mode 100644 index 0000000000..baaf7dced6 --- /dev/null +++ b/mozglue/baseprofiler/core/RegisteredThread.h @@ -0,0 +1,164 @@ +/* -*- 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/. */ + +#ifndef RegisteredThread_h +#define RegisteredThread_h + +#include "platform.h" +#include "ThreadInfo.h" + +#include "mozilla/UniquePtr.h" + +namespace mozilla { +namespace baseprofiler { + +// This class contains the state for a single thread that is accessible without +// protection from gPSMutex in platform.cpp. Because there is no external +// protection against data races, it must provide internal protection. Hence +// the "Racy" prefix. +// +class RacyRegisteredThread final { + public: + explicit RacyRegisteredThread(BaseProfilerThreadId aThreadId) + : mThreadId(aThreadId), mSleep(AWAKE), mIsBeingProfiled(false) {} + + ~RacyRegisteredThread() {} + + void SetIsBeingProfiled(bool aIsBeingProfiled) { + mIsBeingProfiled = aIsBeingProfiled; + } + + bool IsBeingProfiled() const { return mIsBeingProfiled; } + + // This is called on every profiler restart. Put things that should happen at + // that time here. + void ReinitializeOnResume() { + // This is needed to cause an initial sample to be taken from sleeping + // threads that had been observed prior to the profiler stopping and + // restarting. Otherwise sleeping threads would not have any samples to + // copy forward while sleeping. + (void)mSleep.compareExchange(SLEEPING_OBSERVED, SLEEPING_NOT_OBSERVED); + } + + // This returns true for the second and subsequent calls in each sleep cycle. + bool CanDuplicateLastSampleDueToSleep() { + if (mSleep == AWAKE) { + return false; + } + + if (mSleep.compareExchange(SLEEPING_NOT_OBSERVED, SLEEPING_OBSERVED)) { + return false; + } + + return true; + } + + // Call this whenever the current thread sleeps. Calling it twice in a row + // without an intervening setAwake() call is an error. + void SetSleeping() { + MOZ_ASSERT(mSleep == AWAKE); + mSleep = SLEEPING_NOT_OBSERVED; + } + + // Call this whenever the current thread wakes. Calling it twice in a row + // without an intervening setSleeping() call is an error. + void SetAwake() { + MOZ_ASSERT(mSleep != AWAKE); + mSleep = AWAKE; + } + + bool IsSleeping() { return mSleep != AWAKE; } + + BaseProfilerThreadId ThreadId() const { return mThreadId; } + + class ProfilingStack& ProfilingStack() { return mProfilingStack; } + const class ProfilingStack& ProfilingStack() const { return mProfilingStack; } + + private: + class ProfilingStack mProfilingStack; + + // mThreadId contains the thread ID of the current thread. It is safe to read + // this from multiple threads concurrently, as it will never be mutated. + const BaseProfilerThreadId mThreadId; + + // mSleep tracks whether the thread is sleeping, and if so, whether it has + // been previously observed. This is used for an optimization: in some cases, + // when a thread is asleep, we duplicate the previous sample, which is + // cheaper than taking a new sample. + // + // mSleep is atomic because it is accessed from multiple threads. + // + // - It is written only by this thread, via setSleeping() and setAwake(). + // + // - It is read by SamplerThread::Run(). + // + // There are two cases where racing between threads can cause an issue. + // + // - If CanDuplicateLastSampleDueToSleep() returns false but that result is + // invalidated before being acted upon, we will take a full sample + // unnecessarily. This is additional work but won't cause any correctness + // issues. (In actual fact, this case is impossible. In order to go from + // CanDuplicateLastSampleDueToSleep() returning false to it returning true + // requires an intermediate call to it in order for mSleep to go from + // SLEEPING_NOT_OBSERVED to SLEEPING_OBSERVED.) + // + // - If CanDuplicateLastSampleDueToSleep() returns true but that result is + // invalidated before being acted upon -- i.e. the thread wakes up before + // DuplicateLastSample() is called -- we will duplicate the previous + // sample. This is inaccurate, but only slightly... we will effectively + // treat the thread as having slept a tiny bit longer than it really did. + // + // This latter inaccuracy could be avoided by moving the + // CanDuplicateLastSampleDueToSleep() check within the thread-freezing code, + // e.g. the section where Tick() is called. But that would reduce the + // effectiveness of the optimization because more code would have to be run + // before we can tell that duplication is allowed. + // + static const int AWAKE = 0; + static const int SLEEPING_NOT_OBSERVED = 1; + static const int SLEEPING_OBSERVED = 2; + Atomic mSleep; + + // Is this thread being profiled? (e.g., should markers be recorded?) + Atomic mIsBeingProfiled; +}; + +// This class contains information that's relevant to a single thread only +// while that thread is running and registered with the profiler, but +// regardless of whether the profiler is running. All accesses to it are +// protected by the profiler state lock. +class RegisteredThread final { + public: + RegisteredThread(ThreadInfo* aInfo, void* aStackTop); + ~RegisteredThread(); + + class RacyRegisteredThread& RacyRegisteredThread() { + return mRacyRegisteredThread; + } + const class RacyRegisteredThread& RacyRegisteredThread() const { + return mRacyRegisteredThread; + } + + PlatformData* GetPlatformData() const { return mPlatformData.get(); } + const void* StackTop() const { return mStackTop; } + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; + + const RefPtr Info() const { return mThreadInfo; } + + private: + class RacyRegisteredThread mRacyRegisteredThread; + + const UniquePlatformData mPlatformData; + const void* mStackTop; + + const RefPtr mThreadInfo; +}; + +} // namespace baseprofiler +} // namespace mozilla + +#endif // RegisteredThread_h -- cgit v1.2.3