RegisteredThread.h (6002B)
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 #ifndef RegisteredThread_h 8 #define RegisteredThread_h 9 10 #include "platform.h" 11 #include "ThreadInfo.h" 12 13 namespace mozilla { 14 namespace baseprofiler { 15 16 // This class contains the state for a single thread that is accessible without 17 // protection from gPSMutex in platform.cpp. Because there is no external 18 // protection against data races, it must provide internal protection. Hence 19 // the "Racy" prefix. 20 // 21 class RacyRegisteredThread final { 22 public: 23 explicit RacyRegisteredThread(BaseProfilerThreadId aThreadId) 24 : mThreadId(aThreadId), mSleep(AWAKE), mIsBeingProfiled(false) {} 25 26 ~RacyRegisteredThread() {} 27 28 void SetIsBeingProfiled(bool aIsBeingProfiled) { 29 mIsBeingProfiled = aIsBeingProfiled; 30 } 31 32 bool IsBeingProfiled() const { return mIsBeingProfiled; } 33 34 // This is called on every profiler restart. Put things that should happen at 35 // that time here. 36 void ReinitializeOnResume() { 37 // This is needed to cause an initial sample to be taken from sleeping 38 // threads that had been observed prior to the profiler stopping and 39 // restarting. Otherwise sleeping threads would not have any samples to 40 // copy forward while sleeping. 41 (void)mSleep.compareExchange(SLEEPING_OBSERVED, SLEEPING_NOT_OBSERVED); 42 } 43 44 // This returns true for the second and subsequent calls in each sleep cycle. 45 bool CanDuplicateLastSampleDueToSleep() { 46 if (mSleep == AWAKE) { 47 return false; 48 } 49 50 if (mSleep.compareExchange(SLEEPING_NOT_OBSERVED, SLEEPING_OBSERVED)) { 51 return false; 52 } 53 54 return true; 55 } 56 57 // Call this whenever the current thread sleeps. Calling it twice in a row 58 // without an intervening setAwake() call is an error. 59 void SetSleeping() { 60 MOZ_ASSERT(mSleep == AWAKE); 61 mSleep = SLEEPING_NOT_OBSERVED; 62 } 63 64 // Call this whenever the current thread wakes. Calling it twice in a row 65 // without an intervening setSleeping() call is an error. 66 void SetAwake() { 67 MOZ_ASSERT(mSleep != AWAKE); 68 mSleep = AWAKE; 69 } 70 71 bool IsSleeping() { return mSleep != AWAKE; } 72 73 BaseProfilerThreadId ThreadId() const { return mThreadId; } 74 75 class ProfilingStack& ProfilingStack() { return mProfilingStack; } 76 const class ProfilingStack& ProfilingStack() const { return mProfilingStack; } 77 78 private: 79 class ProfilingStack mProfilingStack; 80 81 // mThreadId contains the thread ID of the current thread. It is safe to read 82 // this from multiple threads concurrently, as it will never be mutated. 83 const BaseProfilerThreadId mThreadId; 84 85 // mSleep tracks whether the thread is sleeping, and if so, whether it has 86 // been previously observed. This is used for an optimization: in some cases, 87 // when a thread is asleep, we duplicate the previous sample, which is 88 // cheaper than taking a new sample. 89 // 90 // mSleep is atomic because it is accessed from multiple threads. 91 // 92 // - It is written only by this thread, via setSleeping() and setAwake(). 93 // 94 // - It is read by SamplerThread::Run(). 95 // 96 // There are two cases where racing between threads can cause an issue. 97 // 98 // - If CanDuplicateLastSampleDueToSleep() returns false but that result is 99 // invalidated before being acted upon, we will take a full sample 100 // unnecessarily. This is additional work but won't cause any correctness 101 // issues. (In actual fact, this case is impossible. In order to go from 102 // CanDuplicateLastSampleDueToSleep() returning false to it returning true 103 // requires an intermediate call to it in order for mSleep to go from 104 // SLEEPING_NOT_OBSERVED to SLEEPING_OBSERVED.) 105 // 106 // - If CanDuplicateLastSampleDueToSleep() returns true but that result is 107 // invalidated before being acted upon -- i.e. the thread wakes up before 108 // DuplicateLastSample() is called -- we will duplicate the previous 109 // sample. This is inaccurate, but only slightly... we will effectively 110 // treat the thread as having slept a tiny bit longer than it really did. 111 // 112 // This latter inaccuracy could be avoided by moving the 113 // CanDuplicateLastSampleDueToSleep() check within the thread-freezing code, 114 // e.g. the section where Tick() is called. But that would reduce the 115 // effectiveness of the optimization because more code would have to be run 116 // before we can tell that duplication is allowed. 117 // 118 static const int AWAKE = 0; 119 static const int SLEEPING_NOT_OBSERVED = 1; 120 static const int SLEEPING_OBSERVED = 2; 121 Atomic<int> mSleep; 122 123 // Is this thread being profiled? (e.g., should markers be recorded?) 124 Atomic<bool, MemoryOrdering::Relaxed> mIsBeingProfiled; 125 }; 126 127 // This class contains information that's relevant to a single thread only 128 // while that thread is running and registered with the profiler, but 129 // regardless of whether the profiler is running. All accesses to it are 130 // protected by the profiler state lock. 131 class RegisteredThread final { 132 public: 133 RegisteredThread(ThreadInfo* aInfo, void* aStackTop); 134 ~RegisteredThread(); 135 136 class RacyRegisteredThread& RacyRegisteredThread() { 137 return mRacyRegisteredThread; 138 } 139 const class RacyRegisteredThread& RacyRegisteredThread() const { 140 return mRacyRegisteredThread; 141 } 142 143 PlatformData* GetPlatformData() const { return mPlatformData.get(); } 144 const void* StackTop() const { return mStackTop; } 145 146 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; 147 148 const RefPtr<ThreadInfo> Info() const { return mThreadInfo; } 149 150 private: 151 class RacyRegisteredThread mRacyRegisteredThread; 152 153 const UniquePlatformData mPlatformData; 154 const void* mStackTop; 155 156 const RefPtr<ThreadInfo> mThreadInfo; 157 }; 158 159 } // namespace baseprofiler 160 } // namespace mozilla 161 162 #endif // RegisteredThread_h