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
{
18 using RegistryMutex
= baseprofiler::detail::BaseProfilerSharedMutex
;
19 using RegistryLockExclusive
=
20 baseprofiler::detail::BaseProfilerAutoLockExclusive
;
21 using RegistryLockShared
= baseprofiler::detail::BaseProfilerAutoLockShared
;
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.
45 // const UnlockedConstReader
47 [[nodiscard
]] const UnlockedConstReader
& UnlockedConstReaderCRef() const {
48 return mThreadRegistration
->mData
;
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
;
64 auto WithUnlockedConstReaderAndAtomicRW(F
&& aF
) const {
65 return std::forward
<F
>(aF
)(UnlockedConstReaderAndAtomicRWCRef());
68 // UnlockedConstReaderAndAtomicRW
70 [[nodiscard
]] UnlockedConstReaderAndAtomicRW
&
71 UnlockedConstReaderAndAtomicRWRef() {
72 return mThreadRegistration
->mData
;
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
;
88 auto WithUnlockedRWForLockedProfiler(F
&& aF
) const {
89 return std::forward
<F
>(aF
)(UnlockedRWForLockedProfilerCRef());
92 // UnlockedRWForLockedProfiler
94 [[nodiscard
]] UnlockedRWForLockedProfiler
&
95 UnlockedRWForLockedProfilerRef() {
96 return mThreadRegistration
->mData
;
100 auto WithUnlockedRWForLockedProfiler(F
&& aF
) {
101 return std::forward
<F
>(aF
)(UnlockedRWForLockedProfilerRef());
104 // const LockedRWFromAnyThread through ConstRWFromAnyThreadWithLock
106 class ConstRWFromAnyThreadWithLock
{
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
) {}
122 const LockedRWFromAnyThread
& mLockedRWFromAnyThread
;
123 ThreadRegistration::DataLock mDataLock
;
126 [[nodiscard
]] ConstRWFromAnyThreadWithLock
ConstLockedRWFromAnyThread()
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
{
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
);
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());
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
{
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
;
226 ThreadRegistration::WithOnThreadRef(
227 [](ThreadRegistration::OnThreadRef aOnThreadRef
) {
228 aOnThreadRef
.mThreadRegistration
229 ->mIsRegistryLockedSharedOnThisThread
= true;
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(); }
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
);
265 template <typename F
, typename FallbackReturn
>
266 [[nodiscard
]] static auto WithOffThreadRefOr(ProfilerThreadId aThreadId
,
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
) {
285 offThreadRef
.mThreadRegistration
->SizeOfExcludingThis(aMallocSizeOf
);
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
;
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