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:
Andreas Pehrson 2020-11-30 14:17:06 +00:00
Родитель 9a48d0a5f6
Коммит 49399b7ed3
4 изменённых файлов: 66 добавлений и 7 удалений

Просмотреть файл

@ -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);