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 file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef AUDIOTHREADREGISTRY_H
8 #define AUDIOTHREADREGISTRY_H
9 
10 #include <cstdint>
11 #include <mozilla/DataMutex.h>
12 #include <nsTArray.h>
13 #include <thread>
14 #include <GeckoProfiler.h>
15 
16 namespace mozilla {
17 
18 // This class is a singleton that tracks all audio callback threads and makes
19 // sure they are registered or unregistered to the profiler safely and
20 // consistently.
21 //
22 // Register and Unregister are fairly expensive and shouldn't be used in a hot
23 // path.
24 class AudioThreadRegistry final {
25  public:
AudioThreadRegistry()26   AudioThreadRegistry() : mThreadIds("AudioThreadId") {}
27 
~AudioThreadRegistry()28   ~AudioThreadRegistry() {
29     // It would be nice to be able to assert that all threads have be
30     // unregistered, but we can't: it's legal to suspend an audio stream, so
31     // that the callback isn't called, and then immediately destroy it.
32   }
33 
34   // This is intended to be called when an object starts an audio callback
35   // thread.
Register(ProfilerThreadId aThreadId)36   void Register(ProfilerThreadId aThreadId) {
37     if (!aThreadId.IsSpecified()) {
38       // profiler_current_thread_id is unspecified on unsupported platforms.
39       return;
40     }
41 
42     auto threadIds = mThreadIds.Lock();
43     for (uint32_t i = 0; i < threadIds->Length(); i++) {
44       if ((*threadIds)[i].mId == aThreadId) {
45         (*threadIds)[i].mUserCount++;
46         return;
47       }
48     }
49     ThreadUserCount tuc;
50     tuc.mId = aThreadId;
51     tuc.mUserCount = 1;
52     threadIds->AppendElement(tuc);
53     PROFILER_REGISTER_THREAD("NativeAudioCallback");
54   }
55 
56   // This is intended to be called when an object stops an audio callback thread
Unregister(ProfilerThreadId aThreadId)57   void Unregister(ProfilerThreadId aThreadId) {
58     if (!aThreadId.IsSpecified()) {
59       // profiler_current_thread_id is unspedified on unsupported platforms.
60       return;
61     }
62 
63     auto threadIds = mThreadIds.Lock();
64     for (uint32_t i = 0; i < threadIds->Length(); i++) {
65       if ((*threadIds)[i].mId == aThreadId) {
66         MOZ_ASSERT((*threadIds)[i].mUserCount > 0);
67         (*threadIds)[i].mUserCount--;
68 
69         if ((*threadIds)[i].mUserCount == 0) {
70           PROFILER_UNREGISTER_THREAD();
71           threadIds->RemoveElementAt(i);
72         }
73         return;
74       }
75     }
76     MOZ_ASSERT(false);
77   }
78 
79  private:
80   AudioThreadRegistry(const AudioThreadRegistry&) = delete;
81   AudioThreadRegistry& operator=(const AudioThreadRegistry&) = delete;
82   AudioThreadRegistry(AudioThreadRegistry&&) = delete;
83   AudioThreadRegistry& operator=(AudioThreadRegistry&&) = delete;
84 
85   struct ThreadUserCount {
86     ProfilerThreadId mId;  // from profiler_current_thread_id
87     int mUserCount;
88   };
89   DataMutex<nsTArray<ThreadUserCount>> mThreadIds;
90 };
91 
92 }  // namespace mozilla
93 
94 #endif  // AUDIOTHREADREGISTRY_H
95