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