зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1666116 - Pre-create the fake audio thread for timing sensitive tests. r=padenot
It has been observed on try to take a long time to start the fake audio thread. On Mac it would regularly take around half a second. This patch mitigates that start-up time being long by making sure the thread is already started. Differential Revision: https://phabricator.services.mozilla.com/D97416
This commit is contained in:
Родитель
9a48d0a5f6
Коммит
49399b7ed3
|
@ -325,6 +325,19 @@ void MockCubeb::SetStreamStartFreezeEnabled(bool aEnabled) {
|
|||
mStreamStartFreezeEnabled = aEnabled;
|
||||
}
|
||||
|
||||
auto MockCubeb::ForceAudioThread() -> RefPtr<ForcedAudioThreadPromise> {
|
||||
RefPtr<ForcedAudioThreadPromise> p =
|
||||
mForcedAudioThreadPromise.Ensure(__func__);
|
||||
mForcedAudioThread = true;
|
||||
StartStream(nullptr);
|
||||
return p;
|
||||
}
|
||||
|
||||
void MockCubeb::UnforceAudioThread() {
|
||||
mForcedAudioThread = false;
|
||||
StopStream(nullptr);
|
||||
}
|
||||
|
||||
int MockCubeb::StreamInit(cubeb* aContext, cubeb_stream** aStream,
|
||||
cubeb_devid aInputDevice,
|
||||
cubeb_stream_params* aInputStreamParams,
|
||||
|
@ -363,8 +376,13 @@ MediaEventSource<void>& MockCubeb::StreamDestroyEvent() {
|
|||
|
||||
void MockCubeb::StartStream(MockCubebStream* aStream) {
|
||||
auto streams = mLiveStreams.Lock();
|
||||
MOZ_ASSERT(!streams->Contains(aStream->mSelf));
|
||||
streams->AppendElement(aStream->mSelf);
|
||||
MOZ_ASSERT_IF(!aStream, mForcedAudioThread);
|
||||
// Forcing an audio thread must happen before starting streams
|
||||
MOZ_ASSERT_IF(!aStream, streams->IsEmpty());
|
||||
if (aStream) {
|
||||
MOZ_ASSERT(!streams->Contains(aStream->mSelf));
|
||||
streams->AppendElement(aStream->mSelf);
|
||||
}
|
||||
if (!mFakeAudioThread) {
|
||||
mFakeAudioThread = WrapUnique(new std::thread(ThreadFunction_s, this));
|
||||
}
|
||||
|
@ -374,12 +392,14 @@ int MockCubeb::StopStream(MockCubebStream* aStream) {
|
|||
UniquePtr<std::thread> audioThread;
|
||||
{
|
||||
auto streams = mLiveStreams.Lock();
|
||||
if (!streams->Contains(aStream->mSelf)) {
|
||||
return CUBEB_ERROR;
|
||||
if (aStream) {
|
||||
if (!streams->Contains(aStream->mSelf)) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
streams->RemoveElement(aStream->mSelf);
|
||||
}
|
||||
streams->RemoveElement(aStream->mSelf);
|
||||
MOZ_ASSERT(mFakeAudioThread);
|
||||
if (streams->IsEmpty()) {
|
||||
if (streams->IsEmpty() && !mForcedAudioThread) {
|
||||
audioThread = std::move(mFakeAudioThread);
|
||||
}
|
||||
}
|
||||
|
@ -390,13 +410,17 @@ int MockCubeb::StopStream(MockCubebStream* aStream) {
|
|||
}
|
||||
|
||||
void MockCubeb::ThreadFunction() {
|
||||
if (mForcedAudioThread) {
|
||||
mForcedAudioThreadPromise.Resolve(MakeRefPtr<AudioThreadAutoUnforcer>(this),
|
||||
__func__);
|
||||
}
|
||||
while (true) {
|
||||
{
|
||||
auto streams = mLiveStreams.Lock();
|
||||
for (auto& stream : *streams) {
|
||||
stream->Process10Ms();
|
||||
}
|
||||
if (streams->IsEmpty()) {
|
||||
if (streams->IsEmpty() && !mForcedAudioThread) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -269,6 +269,31 @@ class MockCubeb {
|
|||
// to be able to start them.
|
||||
void SetStreamStartFreezeEnabled(bool aEnabled);
|
||||
|
||||
// Helper class that automatically unforces a forced audio thread on release.
|
||||
class AudioThreadAutoUnforcer {
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioThreadAutoUnforcer)
|
||||
|
||||
public:
|
||||
explicit AudioThreadAutoUnforcer(MockCubeb* aContext)
|
||||
: mContext(aContext) {}
|
||||
|
||||
protected:
|
||||
virtual ~AudioThreadAutoUnforcer() { mContext->UnforceAudioThread(); }
|
||||
MockCubeb* mContext;
|
||||
};
|
||||
|
||||
// Creates the audio thread if one is not available. The audio thread remains
|
||||
// forced until UnforceAudioThread is called. The returned promise is resolved
|
||||
// when the audio thread is running. With this, a test can ensure starting
|
||||
// audio streams is deterministically fast across platforms for more accurate
|
||||
// results.
|
||||
using ForcedAudioThreadPromise =
|
||||
MozPromise<RefPtr<AudioThreadAutoUnforcer>, nsresult, false>;
|
||||
RefPtr<ForcedAudioThreadPromise> ForceAudioThread();
|
||||
|
||||
// Allows a forced audio thread to stop.
|
||||
void UnforceAudioThread();
|
||||
|
||||
int StreamInit(cubeb* aContext, cubeb_stream** aStream,
|
||||
cubeb_devid aInputDevice,
|
||||
cubeb_stream_params* aInputStreamParams,
|
||||
|
@ -318,6 +343,10 @@ class MockCubeb {
|
|||
bool mSupportsDeviceCollectionChangedCallback = true;
|
||||
// Whether new MockCubebStreams should be frozen on start.
|
||||
Atomic<bool> mStreamStartFreezeEnabled{false};
|
||||
// Whether the audio thread is forced, i.e., whether it remains active even
|
||||
// with no live streams.
|
||||
Atomic<bool> mForcedAudioThread{false};
|
||||
MozPromiseHolder<ForcedAudioThreadPromise> mForcedAudioThreadPromise;
|
||||
// Our input and output devices.
|
||||
nsTArray<cubeb_device_info> mInputDevices;
|
||||
nsTArray<cubeb_device_info> mOutputDevices;
|
||||
|
|
|
@ -133,6 +133,8 @@ void TestSlowStart(const TrackRate aRate) MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
|
|||
|
||||
MockCubeb* cubeb = new MockCubeb();
|
||||
cubeb->SetStreamStartFreezeEnabled(true);
|
||||
auto unforcer = WaitFor(cubeb->ForceAudioThread()).unwrap();
|
||||
Unused << unforcer;
|
||||
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
|
||||
|
||||
RefPtr<AudioCallbackDriver> driver;
|
||||
|
|
|
@ -271,6 +271,8 @@ TEST(TestAudioTrackGraph, AudioInputTrack)
|
|||
{
|
||||
MockCubeb* cubeb = new MockCubeb();
|
||||
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
|
||||
auto unforcer = WaitFor(cubeb->ForceAudioThread()).unwrap();
|
||||
Unused << unforcer;
|
||||
|
||||
// Start on a system clock driver, then switch to full-duplex in one go. If we
|
||||
// did output-then-full-duplex we'd risk a second NotifyWhenDeviceStarted
|
||||
|
@ -603,6 +605,8 @@ void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate,
|
|||
|
||||
MockCubeb* cubeb = new MockCubeb();
|
||||
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
|
||||
auto unforcer = WaitFor(cubeb->ForceAudioThread()).unwrap();
|
||||
Unused << unforcer;
|
||||
|
||||
cubeb->SetStreamStartFreezeEnabled(true);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче