Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / tools / profiler / public / ProfilerThreadRegistry.h
blob4d0fd3ef68049133f3fdf8a7e84e3eec1448ba3e
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/. */
7 #ifndef ProfilerThreadRegistry_h
8 #define ProfilerThreadRegistry_h
10 #include "mozilla/BaseProfilerDetail.h"
11 #include "mozilla/ProfilerThreadRegistration.h"
12 #include "mozilla/Vector.h"
14 namespace mozilla::profiler {
16 class ThreadRegistry {
17 private:
18 using RegistryMutex = baseprofiler::detail::BaseProfilerSharedMutex;
19 using RegistryLockExclusive =
20 baseprofiler::detail::BaseProfilerAutoLockExclusive;
21 using RegistryLockShared = baseprofiler::detail::BaseProfilerAutoLockShared;
23 public:
24 // Aliases to data accessors (removing the ThreadRegistration prefix).
26 using UnlockedConstReader = ThreadRegistrationUnlockedConstReader;
27 using UnlockedConstReaderAndAtomicRW =
28 ThreadRegistrationUnlockedConstReaderAndAtomicRW;
29 using UnlockedRWForLockedProfiler =
30 ThreadRegistrationUnlockedRWForLockedProfiler;
31 using UnlockedReaderAndAtomicRWOnThread =
32 ThreadRegistrationUnlockedReaderAndAtomicRWOnThread;
33 using LockedRWFromAnyThread = ThreadRegistrationLockedRWFromAnyThread;
34 using LockedRWOnThread = ThreadRegistrationLockedRWOnThread;
36 // Off-thread access through the registry, providing the following data
37 // accessors: UnlockedConstReader, UnlockedConstReaderAndAtomicRW,
38 // UnlockedRWForLockedProfiler, and LockedRWFromAnyThread.
39 // (See ThreadRegistration class for ON-thread access.)
41 // Reference-like class pointing at a ThreadRegistration.
42 // It should only exist while sRegistryMutex is locked.
43 class OffThreadRef {
44 public:
45 // const UnlockedConstReader
47 [[nodiscard]] const UnlockedConstReader& UnlockedConstReaderCRef() const {
48 return mThreadRegistration->mData;
51 template <typename F>
52 auto WithUnlockedConstReader(F&& aF) const {
53 return std::forward<F>(aF)(UnlockedConstReaderCRef());
56 // const UnlockedConstReaderAndAtomicRW
58 [[nodiscard]] const UnlockedConstReaderAndAtomicRW&
59 UnlockedConstReaderAndAtomicRWCRef() const {
60 return mThreadRegistration->mData;
63 template <typename F>
64 auto WithUnlockedConstReaderAndAtomicRW(F&& aF) const {
65 return std::forward<F>(aF)(UnlockedConstReaderAndAtomicRWCRef());
68 // UnlockedConstReaderAndAtomicRW
70 [[nodiscard]] UnlockedConstReaderAndAtomicRW&
71 UnlockedConstReaderAndAtomicRWRef() {
72 return mThreadRegistration->mData;
75 template <typename F>
76 auto WithUnlockedConstReaderAndAtomicRW(F&& aF) {
77 return std::forward<F>(aF)(UnlockedConstReaderAndAtomicRWRef());
80 // const UnlockedRWForLockedProfiler
82 [[nodiscard]] const UnlockedRWForLockedProfiler&
83 UnlockedRWForLockedProfilerCRef() const {
84 return mThreadRegistration->mData;
87 template <typename F>
88 auto WithUnlockedRWForLockedProfiler(F&& aF) const {
89 return std::forward<F>(aF)(UnlockedRWForLockedProfilerCRef());
92 // UnlockedRWForLockedProfiler
94 [[nodiscard]] UnlockedRWForLockedProfiler&
95 UnlockedRWForLockedProfilerRef() {
96 return mThreadRegistration->mData;
99 template <typename F>
100 auto WithUnlockedRWForLockedProfiler(F&& aF) {
101 return std::forward<F>(aF)(UnlockedRWForLockedProfilerRef());
104 // const LockedRWFromAnyThread through ConstRWFromAnyThreadWithLock
106 class ConstRWFromAnyThreadWithLock {
107 public:
108 [[nodiscard]] const LockedRWFromAnyThread& DataCRef() const {
109 return mLockedRWFromAnyThread;
111 [[nodiscard]] const LockedRWFromAnyThread* operator->() const {
112 return &mLockedRWFromAnyThread;
115 ConstRWFromAnyThreadWithLock(
116 const LockedRWFromAnyThread& aLockedRWFromAnyThread,
117 ThreadRegistration::DataMutex& aDataMutex)
118 : mLockedRWFromAnyThread(aLockedRWFromAnyThread),
119 mDataLock(aDataMutex) {}
121 private:
122 const LockedRWFromAnyThread& mLockedRWFromAnyThread;
123 ThreadRegistration::DataLock mDataLock;
126 [[nodiscard]] ConstRWFromAnyThreadWithLock ConstLockedRWFromAnyThread()
127 const {
128 return ConstRWFromAnyThreadWithLock{mThreadRegistration->mData,
129 mThreadRegistration->mDataMutex};
132 template <typename F>
133 auto WithConstLockedRWFromAnyThread(F&& aF) const {
134 ConstRWFromAnyThreadWithLock lockedData = ConstLockedRWFromAnyThread();
135 return std::forward<F>(aF)(lockedData.DataCRef());
138 // LockedRWFromAnyThread through RWFromAnyThreadWithLock
140 class RWFromAnyThreadWithLock {
141 public:
142 [[nodiscard]] const LockedRWFromAnyThread& DataCRef() const {
143 return mLockedRWFromAnyThread;
145 [[nodiscard]] LockedRWFromAnyThread& DataRef() {
146 return mLockedRWFromAnyThread;
148 [[nodiscard]] const LockedRWFromAnyThread* operator->() const {
149 return &mLockedRWFromAnyThread;
151 [[nodiscard]] LockedRWFromAnyThread* operator->() {
152 return &mLockedRWFromAnyThread;
155 // In some situations, it may be useful to do some on-thread operations if
156 // we are indeed on this thread now. The lock is still held here; caller
157 // should not use this pointer longer than this RWFromAnyThreadWithLock.
158 [[nodiscard]] LockedRWOnThread* GetLockedRWOnThread() {
159 if (mLockedRWFromAnyThread.Info().ThreadId() ==
160 profiler_current_thread_id()) {
161 // mLockedRWFromAnyThread references a subclass of the
162 // ThreadRegistration's mData, so it's safe to downcast it to another
163 // hierarchy level of the object.
164 return &static_cast<LockedRWOnThread&>(mLockedRWFromAnyThread);
166 return nullptr;
169 private:
170 friend class OffThreadRef;
171 RWFromAnyThreadWithLock(LockedRWFromAnyThread& aLockedRWFromAnyThread,
172 ThreadRegistration::DataMutex& aDataMutex)
173 : mLockedRWFromAnyThread(aLockedRWFromAnyThread),
174 mDataLock(aDataMutex) {}
176 LockedRWFromAnyThread& mLockedRWFromAnyThread;
177 ThreadRegistration::DataLock mDataLock;
180 [[nodiscard]] RWFromAnyThreadWithLock GetLockedRWFromAnyThread() {
181 return RWFromAnyThreadWithLock{mThreadRegistration->mData,
182 mThreadRegistration->mDataMutex};
185 template <typename F>
186 auto WithLockedRWFromAnyThread(F&& aF) {
187 RWFromAnyThreadWithLock lockedData = GetLockedRWFromAnyThread();
188 return std::forward<F>(aF)(lockedData.DataRef());
191 private:
192 // Only ThreadRegistry should construct an OnThreadRef.
193 friend class ThreadRegistry;
194 explicit OffThreadRef(ThreadRegistration& aThreadRegistration)
195 : mThreadRegistration(&aThreadRegistration) {}
197 // If we have an ON-thread ref, it's safe to convert to an OFF-thread ref.
198 explicit OffThreadRef(ThreadRegistration::OnThreadRef aOnThreadRef)
199 : mThreadRegistration(aOnThreadRef.mThreadRegistration) {}
201 [[nodiscard]] bool IsPointingAt(
202 ThreadRegistration& aThreadRegistration) const {
203 return mThreadRegistration == &aThreadRegistration;
206 // Guaranted to be non-null by construction.
207 ThreadRegistration* mThreadRegistration;
210 // Lock the registry non-exclusively and allow iteration. E.g.:
211 // `for (OffThreadRef thread : LockedRegistry{}) { ... }`
212 // Do *not* export copies/references, as they could become dangling.
213 // Locking order: Profiler, ThreadRegistry, ThreadRegistration.
214 class LockedRegistry {
215 public:
216 LockedRegistry()
217 : mRegistryLock([]() -> RegistryMutex& {
218 MOZ_ASSERT(!IsRegistryMutexLockedOnCurrentThread(),
219 "Recursive locking detected");
220 // In DEBUG builds, *before* we attempt to lock sRegistryMutex, we
221 // want to check that the ThreadRegistration mutex is *not* locked
222 // on this thread, to avoid inversion deadlocks.
223 MOZ_ASSERT(!ThreadRegistration::IsDataMutexLockedOnCurrentThread());
224 return sRegistryMutex;
225 }()) {
226 ThreadRegistration::WithOnThreadRef(
227 [](ThreadRegistration::OnThreadRef aOnThreadRef) {
228 aOnThreadRef.mThreadRegistration
229 ->mIsRegistryLockedSharedOnThisThread = true;
233 ~LockedRegistry() {
234 ThreadRegistration::WithOnThreadRef(
235 [](ThreadRegistration::OnThreadRef aOnThreadRef) {
236 aOnThreadRef.mThreadRegistration
237 ->mIsRegistryLockedSharedOnThisThread = false;
241 [[nodiscard]] const OffThreadRef* begin() const {
242 return sRegistryContainer.begin();
244 [[nodiscard]] OffThreadRef* begin() { return sRegistryContainer.begin(); }
245 [[nodiscard]] const OffThreadRef* end() const {
246 return sRegistryContainer.end();
248 [[nodiscard]] OffThreadRef* end() { return sRegistryContainer.end(); }
250 private:
251 RegistryLockShared mRegistryLock;
254 // Call `F(OffThreadRef)` for the given aThreadId.
255 template <typename F>
256 static void WithOffThreadRef(ProfilerThreadId aThreadId, F&& aF) {
257 for (OffThreadRef thread : LockedRegistry{}) {
258 if (thread.UnlockedConstReaderCRef().Info().ThreadId() == aThreadId) {
259 std::forward<F>(aF)(thread);
260 break;
265 template <typename F, typename FallbackReturn>
266 [[nodiscard]] static auto WithOffThreadRefOr(ProfilerThreadId aThreadId,
267 F&& aF,
268 FallbackReturn&& aFallbackReturn)
269 -> decltype(std::forward<F>(aF)(std::declval<OffThreadRef>())) {
270 for (OffThreadRef thread : LockedRegistry{}) {
271 if (thread.UnlockedConstReaderCRef().Info().ThreadId() == aThreadId) {
272 return std::forward<F>(aF)(thread);
275 return std::forward<FallbackReturn>(aFallbackReturn);
278 static size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) {
279 LockedRegistry lockedRegistry;
280 // "Ex" because we don't count static objects, but we count whatever they
281 // allocated on the heap.
282 size_t bytes = sRegistryContainer.sizeOfExcludingThis(aMallocSizeOf);
283 for (const OffThreadRef& offThreadRef : lockedRegistry) {
284 bytes +=
285 offThreadRef.mThreadRegistration->SizeOfExcludingThis(aMallocSizeOf);
287 return bytes;
290 static size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
291 return SizeOfExcludingThis(aMallocSizeOf);
294 [[nodiscard]] static bool IsRegistryMutexLockedOnCurrentThread() {
295 return sRegistryMutex.IsLockedExclusiveOnCurrentThread() ||
296 ThreadRegistration::WithOnThreadRefOr(
297 [](ThreadRegistration::OnThreadRef aOnThreadRef) {
298 return aOnThreadRef.mThreadRegistration
299 ->mIsRegistryLockedSharedOnThisThread;
301 false);
304 private:
305 using RegistryContainer = Vector<OffThreadRef>;
307 static RegistryContainer sRegistryContainer;
309 // Mutex protecting the registry.
310 // Locking order: Profiler, ThreadRegistry, ThreadRegistration.
311 static RegistryMutex sRegistryMutex;
313 // Only allow ThreadRegistration to (un)register itself.
314 friend class ThreadRegistration;
315 static void Register(ThreadRegistration::OnThreadRef aOnThreadRef);
316 static void Unregister(ThreadRegistration::OnThreadRef aOnThreadRef);
319 } // namespace mozilla::profiler
321 #endif // ProfilerThreadRegistry_h