Skip to content

Commit

Permalink
Bug 1846191 ref-count MockCubeb to ensure that it lives as long as th…
Browse files Browse the repository at this point in the history
…e audio thread might access it r=padenot

Differential Revision: https://phabricator.services.mozilla.com/D185027
  • Loading branch information
karlt committed Aug 23, 2023
1 parent d5e671a commit 8e564d2
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 14 deletions.
34 changes: 26 additions & 8 deletions dom/media/gtest/MockCubeb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,9 @@ void AddDevices(MockCubeb* mock, uint32_t device_count,
}
}

void cubeb_mock_destroy(cubeb* context) { delete MockCubeb::AsMock(context); }
void cubeb_mock_destroy(cubeb* context) {
MockCubeb::AsMock(context)->Destroy();
}

MockCubebStream::MockCubebStream(cubeb* aContext, cubeb_devid aInputDevice,
cubeb_stream_params* aInputStreamParams,
Expand Down Expand Up @@ -405,16 +407,30 @@ void MockCubebStream::NotifyState(cubeb_state aState) {

MockCubeb::MockCubeb() : ops(&mock_ops) {}

MockCubeb::~MockCubeb() {
auto streams = mLiveStreams.Lock();
MOZ_ASSERT(streams->IsEmpty());
MOZ_ASSERT(!mFakeAudioThread);
};
MockCubeb::~MockCubeb() { MOZ_ASSERT(!mFakeAudioThread); };

cubeb* MockCubeb::AsCubebContext() { return reinterpret_cast<cubeb*>(this); }
void MockCubeb::Destroy() {
MOZ_ASSERT(mHasCubebContext);
{
auto streams = mLiveStreams.Lock();
MOZ_ASSERT(streams->IsEmpty());
}
mDestroyed = true;
Release();
}

cubeb* MockCubeb::AsCubebContext() {
MOZ_ASSERT(!mDestroyed);
if (mHasCubebContext.compareExchange(false, true)) {
AddRef();
}
return reinterpret_cast<cubeb*>(this);
}

MockCubeb* MockCubeb::AsMock(cubeb* aContext) {
return reinterpret_cast<MockCubeb*>(aContext);
auto* mockCubeb = reinterpret_cast<MockCubeb*>(aContext);
MOZ_ASSERT(!mockCubeb->mDestroyed);
return mockCubeb;
}

int MockCubeb::EnumerateDevices(cubeb_device_type aType,
Expand Down Expand Up @@ -623,6 +639,7 @@ void MockCubeb::StartStream(MockCubebStream* aStream) {
streams->AppendElement(aStream->mSelf);
}
if (!mFakeAudioThread) {
AddRef(); // released when the thread exits
mFakeAudioThread = WrapUnique(new std::thread(ThreadFunction_s, this));
}
}
Expand Down Expand Up @@ -665,6 +682,7 @@ void MockCubeb::ThreadFunction() {
std::this_thread::sleep_for(
std::chrono::microseconds(mFastMode ? 0 : 10 * PR_USEC_PER_MSEC));
}
Release();
}

} // namespace mozilla
20 changes: 14 additions & 6 deletions dom/media/gtest/MockCubeb.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,13 +290,23 @@ class SmartMockCubebStream
// backend, but is also controllable by the test code to decide what the backend
// should do, depending on what is being tested.
class MockCubeb {
// This needs to have the exact same memory layout as a real cubeb backend.
// It's very important for the `ops` member to be the very first member of
// the class, and for MockCubeb to not have any virtual members (to avoid
// having a vtable), so that AsMock() returns a pointer to this that can be
// used as a cubeb backend.
const cubeb_ops* ops;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockCubeb);

public:
MockCubeb();
~MockCubeb();
// Cubeb backend implementation
// This allows passing this class as a cubeb* instance.
// cubeb_destroy(context) should eventually be called on the return value
// iff this method is called.
cubeb* AsCubebContext();
static MockCubeb* AsMock(cubeb* aContext);
void Destroy();
// Fill in the collection parameter with all devices of aType.
int EnumerateDevices(cubeb_device_type aType,
cubeb_device_collection* aCollection);
Expand Down Expand Up @@ -389,11 +399,7 @@ class MockCubeb {
void ThreadFunction();

private:
// This needs to have the exact same memory layout as a real cubeb backend.
// It's very important for this `ops` member to be the very first member of
// the class, and to not have any virtual members (to avoid having a
// vtable).
const cubeb_ops* ops;
~MockCubeb();
// The callback to call when the device list has been changed.
cubeb_device_collection_changed_callback
mInputDeviceCollectionChangeCallback = nullptr;
Expand All @@ -413,6 +419,8 @@ class MockCubeb {
// Whether the audio thread is forced, i.e., whether it remains active even
// with no live streams.
Atomic<bool> mForcedAudioThread{false};
Atomic<bool> mHasCubebContext{false};
Atomic<bool> mDestroyed{false};
MozPromiseHolder<ForcedAudioThreadPromise> mForcedAudioThreadPromise;
// Our input and output devices.
nsTArray<cubeb_device_info> mInputDevices;
Expand Down

0 comments on commit 8e564d2

Please sign in to comment.