Bug 1859200 add MediaTrack methods to queue a graph control message to execute a provided lambda expression r=pehrsons

and use them in MediaTrack.

Having the public API on the MediaTrack ensures that the caller has a track
to keep the graph alive.

The only behavior change is that "MediaTrack::DispatchToMainThreadStableState
ControlMessage" is now traced even if in shutdown.

Strong MediaTrack references are now held for refcounted-inside-lambda
static analysis.

Differential Revision: https://phabricator.services.mozilla.com/D191062
This commit is contained in:
Karl Tomlinson 2023-10-17 01:40:42 +00:00
parent e17c4ef075
commit ce169c07d1
2 changed files with 164 additions and 169 deletions

View file

@ -2219,26 +2219,21 @@ void MediaTrack::DestroyImpl() {
void MediaTrack::Destroy() {
// Keep this track alive until we leave this method
RefPtr<MediaTrack> kungFuDeathGrip = this;
class Message : public ControlMessage {
public:
explicit Message(MediaTrack* aTrack) : ControlMessage(aTrack) {}
void RunDuringShutdown() override {
TRACE("MediaTrack::Destroy ControlMessage");
mTrack->RemoveAllResourcesAndListenersImpl();
auto graph = mTrack->GraphImpl();
mTrack->DestroyImpl();
graph->RemoveTrackGraphThread(mTrack);
}
void Run() override {
mTrack->OnGraphThreadDone();
RunDuringShutdown();
}
};
// Keep a reference to the graph, since Message might RunDuringShutdown()
// synchronously and make GraphImpl() invalid.
RefPtr<MediaTrackGraphImpl> graph = GraphImpl();
graph->AppendMessage(MakeUnique<Message>(this));
QueueControlOrShutdownMessage(
[self = RefPtr{this}, this](IsInShutdown aInShutdown) {
if (aInShutdown == IsInShutdown::No) {
OnGraphThreadDone();
}
TRACE("MediaTrack::Destroy ControlMessage");
RemoveAllResourcesAndListenersImpl();
auto* graph = GraphImpl();
DestroyImpl();
graph->RemoveTrackGraphThread(this);
});
graph->RemoveTrack(this);
// Message::RunDuringShutdown may have removed this track from the graph,
// but our kungFuDeathGrip above will have kept this track alive if
@ -2251,20 +2246,13 @@ TrackTime MediaTrack::GetEnd() const {
}
void MediaTrack::AddAudioOutput(void* aKey) {
class Message : public ControlMessage {
public:
Message(MediaTrack* aTrack, void* aKey)
: ControlMessage(aTrack), mKey(aKey) {}
void Run() override {
TRACE("MediaTrack::AddAudioOutputImpl ControlMessage");
mTrack->AddAudioOutputImpl(mKey);
}
void* mKey;
};
if (mMainThreadDestroyed) {
return;
}
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aKey));
QueueControlMessageWithNoShutdown([self = RefPtr{this}, this, aKey] {
TRACE("MediaTrack::AddAudioOutputImpl ControlMessage");
AddAudioOutputImpl(aKey);
});
}
void MediaTrackGraphImpl::SetAudioOutputVolume(MediaTrack* aTrack, void* aKey,
@ -2284,21 +2272,13 @@ void MediaTrack::SetAudioOutputVolumeImpl(void* aKey, float aVolume) {
}
void MediaTrack::SetAudioOutputVolume(void* aKey, float aVolume) {
class Message : public ControlMessage {
public:
Message(MediaTrack* aTrack, void* aKey, float aVolume)
: ControlMessage(aTrack), mKey(aKey), mVolume(aVolume) {}
void Run() override {
TRACE("MediaTrack::SetAudioOutputVolumeImpl ControlMessage");
mTrack->SetAudioOutputVolumeImpl(mKey, mVolume);
}
void* mKey;
float mVolume;
};
if (mMainThreadDestroyed) {
return;
}
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aKey, aVolume));
QueueControlMessageWithNoShutdown([self = RefPtr{this}, this, aKey, aVolume] {
TRACE("MediaTrack::SetAudioOutputVolumeImpl ControlMessage");
SetAudioOutputVolumeImpl(aKey, aVolume);
});
}
void MediaTrack::AddAudioOutputImpl(void* aKey) {
@ -2312,56 +2292,37 @@ void MediaTrack::RemoveAudioOutputImpl(void* aKey) {
}
void MediaTrack::RemoveAudioOutput(void* aKey) {
class Message : public ControlMessage {
public:
explicit Message(MediaTrack* aTrack, void* aKey)
: ControlMessage(aTrack), mKey(aKey) {}
void Run() override {
TRACE("MediaTrack::RemoveAudioOutputImpl ControlMessage");
mTrack->RemoveAudioOutputImpl(mKey);
}
void* mKey;
};
if (mMainThreadDestroyed) {
return;
}
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aKey));
QueueControlMessageWithNoShutdown([self = RefPtr{this}, this, aKey] {
TRACE("MediaTrack::RemoveAudioOutputImpl ControlMessage");
RemoveAudioOutputImpl(aKey);
});
}
void MediaTrack::Suspend() {
class Message : public ControlMessage {
public:
explicit Message(MediaTrack* aTrack) : ControlMessage(aTrack) {}
void Run() override {
TRACE("MediaTrack::IncrementSuspendCount ControlMessage");
mTrack->IncrementSuspendCount();
}
};
// This can happen if this method has been called asynchronously, and the
// track has been destroyed since then.
if (mMainThreadDestroyed) {
return;
}
GraphImpl()->AppendMessage(MakeUnique<Message>(this));
QueueControlMessageWithNoShutdown([self = RefPtr{this}, this] {
TRACE("MediaTrack::IncrementSuspendCount ControlMessage");
IncrementSuspendCount();
});
}
void MediaTrack::Resume() {
class Message : public ControlMessage {
public:
explicit Message(MediaTrack* aTrack) : ControlMessage(aTrack) {}
void Run() override {
TRACE("MediaTrack::DecrementSuspendCount ControlMessage");
mTrack->DecrementSuspendCount();
}
};
// This can happen if this method has been called asynchronously, and the
// track has been destroyed since then.
if (mMainThreadDestroyed) {
return;
}
GraphImpl()->AppendMessage(MakeUnique<Message>(this));
QueueControlMessageWithNoShutdown([self = RefPtr{this}, this] {
TRACE("MediaTrack::DecrementSuspendCount ControlMessage");
DecrementSuspendCount();
});
}
void MediaTrack::AddListenerImpl(
@ -2381,21 +2342,15 @@ void MediaTrack::AddListenerImpl(
}
void MediaTrack::AddListener(MediaTrackListener* aListener) {
class Message : public ControlMessage {
public:
Message(MediaTrack* aTrack, MediaTrackListener* aListener)
: ControlMessage(aTrack), mListener(aListener) {}
void Run() override {
TRACE("MediaTrack::AddListenerImpl ControlMessage");
mTrack->AddListenerImpl(mListener.forget());
}
RefPtr<MediaTrackListener> mListener;
};
MOZ_ASSERT(mSegment, "Segment-less tracks do not support listeners");
if (mMainThreadDestroyed) {
return;
}
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener));
QueueControlMessageWithNoShutdown(
[self = RefPtr{this}, this, listener = RefPtr{aListener}]() mutable {
TRACE("MediaTrack::AddListenerImpl ControlMessage");
AddListenerImpl(listener.forget());
});
}
void MediaTrack::RemoveListenerImpl(MediaTrackListener* aListener) {
@ -2410,31 +2365,21 @@ void MediaTrack::RemoveListenerImpl(MediaTrackListener* aListener) {
RefPtr<GenericPromise> MediaTrack::RemoveListener(
MediaTrackListener* aListener) {
class Message : public ControlMessage {
public:
Message(MediaTrack* aTrack, MediaTrackListener* aListener)
: ControlMessage(aTrack), mListener(aListener) {}
void Run() override {
TRACE("MediaTrack::RemoveListenerImpl ControlMessage");
mTrack->RemoveListenerImpl(mListener);
mRemovedPromise.Resolve(true, __func__);
}
void RunDuringShutdown() override {
// During shutdown we still want the listener's NotifyRemoved to be
// called, since not doing that might block shutdown of other modules.
Run();
}
RefPtr<MediaTrackListener> mListener;
MozPromiseHolder<GenericPromise> mRemovedPromise;
};
UniquePtr<Message> message = MakeUnique<Message>(this, aListener);
RefPtr<GenericPromise> p = message->mRemovedPromise.Ensure(__func__);
MozPromiseHolder<GenericPromise> promiseHolder;
RefPtr<GenericPromise> p = promiseHolder.Ensure(__func__);
if (mMainThreadDestroyed) {
message->mRemovedPromise.Reject(NS_ERROR_FAILURE, __func__);
promiseHolder.Reject(NS_ERROR_FAILURE, __func__);
return p;
}
GraphImpl()->AppendMessage(std::move(message));
QueueControlOrShutdownMessage(
[self = RefPtr{this}, this, listener = RefPtr{aListener},
promiseHolder = std::move(promiseHolder)](IsInShutdown) mutable {
TRACE("MediaTrack::RemoveListenerImpl ControlMessage");
// During shutdown we still want the listener's NotifyRemoved to be
// called, since not doing that might block shutdown of other modules.
RemoveListenerImpl(listener);
promiseHolder.Resolve(true, __func__);
});
return p;
}
@ -2448,20 +2393,14 @@ void MediaTrack::AddDirectListenerImpl(
}
void MediaTrack::AddDirectListener(DirectMediaTrackListener* aListener) {
class Message : public ControlMessage {
public:
Message(MediaTrack* aTrack, DirectMediaTrackListener* aListener)
: ControlMessage(aTrack), mListener(aListener) {}
void Run() override {
TRACE("MediaTrack::AddDirectListenerImpl ControlMessage");
mTrack->AddDirectListenerImpl(mListener.forget());
}
RefPtr<DirectMediaTrackListener> mListener;
};
if (mMainThreadDestroyed) {
return;
}
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener));
QueueControlMessageWithNoShutdown(
[self = RefPtr{this}, this, listener = RefPtr{aListener}]() mutable {
TRACE("MediaTrack::AddDirectListenerImpl ControlMessage");
AddDirectListenerImpl(listener.forget());
});
}
void MediaTrack::RemoveDirectListenerImpl(DirectMediaTrackListener* aListener) {
@ -2469,57 +2408,39 @@ void MediaTrack::RemoveDirectListenerImpl(DirectMediaTrackListener* aListener) {
}
void MediaTrack::RemoveDirectListener(DirectMediaTrackListener* aListener) {
class Message : public ControlMessage {
public:
Message(MediaTrack* aTrack, DirectMediaTrackListener* aListener)
: ControlMessage(aTrack), mListener(aListener) {}
void Run() override {
TRACE("MediaTrack::RemoveDirectListenerImpl ControlMessage");
mTrack->RemoveDirectListenerImpl(mListener);
}
void RunDuringShutdown() override {
// During shutdown we still want the listener's
// NotifyDirectListenerUninstalled to be called, since not doing that
// might block shutdown of other modules.
Run();
}
RefPtr<DirectMediaTrackListener> mListener;
};
if (mMainThreadDestroyed) {
return;
}
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener));
QueueControlOrShutdownMessage(
[self = RefPtr{this}, this, listener = RefPtr{aListener}](IsInShutdown) {
TRACE("MediaTrack::RemoveDirectListenerImpl ControlMessage");
// During shutdown we still want the listener's
// NotifyDirectListenerUninstalled to be called, since not doing that
// might block shutdown of other modules.
RemoveDirectListenerImpl(listener);
});
}
void MediaTrack::RunAfterPendingUpdates(
already_AddRefed<nsIRunnable> aRunnable) {
MOZ_ASSERT(NS_IsMainThread());
MediaTrackGraphImpl* graph = GraphImpl();
nsCOMPtr<nsIRunnable> runnable(aRunnable);
class Message : public ControlMessage {
public:
Message(MediaTrack* aTrack, already_AddRefed<nsIRunnable> aRunnable)
: ControlMessage(aTrack), mRunnable(aRunnable) {}
void Run() override {
TRACE("MediaTrack::DispatchToMainThreadStableState ControlMessage");
mTrack->Graph()->DispatchToMainThreadStableState(mRunnable.forget());
}
void RunDuringShutdown() override {
// Don't run mRunnable now as it may call AppendMessage() which would
// assume that there are no remaining controlMessagesToRunDuringShutdown.
MOZ_ASSERT(NS_IsMainThread());
mTrack->GraphImpl()->Dispatch(mRunnable.forget());
}
private:
nsCOMPtr<nsIRunnable> mRunnable;
};
if (mMainThreadDestroyed) {
return;
}
graph->AppendMessage(MakeUnique<Message>(this, runnable.forget()));
QueueControlOrShutdownMessage(
[self = RefPtr{this}, this,
runnable = nsCOMPtr{aRunnable}](IsInShutdown aInShutdown) mutable {
TRACE("MediaTrack::DispatchToMainThreadStableState ControlMessage");
if (aInShutdown == IsInShutdown::No) {
Graph()->DispatchToMainThreadStableState(runnable.forget());
} else {
// Don't run mRunnable now as it may call AppendMessage() which would
// assume that there are no remaining
// controlMessagesToRunDuringShutdown.
MOZ_ASSERT(NS_IsMainThread());
GraphImpl()->Dispatch(runnable.forget());
}
});
}
void MediaTrack::SetDisabledTrackModeImpl(DisabledTrackMode aMode) {
@ -2534,20 +2455,13 @@ void MediaTrack::SetDisabledTrackModeImpl(DisabledTrackMode aMode) {
}
void MediaTrack::SetDisabledTrackMode(DisabledTrackMode aMode) {
class Message : public ControlMessage {
public:
Message(MediaTrack* aTrack, DisabledTrackMode aMode)
: ControlMessage(aTrack), mMode(aMode) {}
void Run() override {
TRACE("MediaTrack::SetDisabledTrackModeImpl ControlMessage");
mTrack->SetDisabledTrackModeImpl(mMode);
}
DisabledTrackMode mMode;
};
if (mMainThreadDestroyed) {
return;
}
GraphImpl()->AppendMessage(MakeUnique<Message>(this, aMode));
QueueControlMessageWithNoShutdown([self = RefPtr{this}, this, aMode]() {
TRACE("MediaTrack::SetDisabledTrackModeImpl ControlMessage");
SetDisabledTrackModeImpl(aMode);
});
}
void MediaTrack::ApplyTrackDisabling(MediaSegment* aSegment,
@ -2630,6 +2544,12 @@ void MediaTrack::NotifyIfDisabledModeChangedFrom(DisabledTrackMode aOldMode) {
}
}
void MediaTrack::QueueMessage(UniquePtr<ControlMessageInterface> aMessage) {
MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
MOZ_RELEASE_ASSERT(!IsDestroyed());
GraphImpl()->AppendMessage(std::move(aMessage));
}
SourceMediaTrack::SourceMediaTrack(MediaSegment::Type aType,
TrackRate aSampleRate)
: MediaTrack(aSampleRate, aType,

View file

@ -315,6 +315,33 @@ class MediaTrack : public mozilla::LinkedListElement<MediaTrack> {
mMainThreadListeners.RemoveElement(aListener);
}
/**
* Append to the message queue a control message to execute a given lambda
* function with no parameters. The queue is drained during
* RunInStableState(). The lambda will be executed on the graph thread.
* The lambda will not be executed if the graph has been forced to shut
* down.
**/
template <typename Function>
void QueueControlMessageWithNoShutdown(Function&& aFunction) {
QueueMessage(WrapUnique(
new ControlMessageWithNoShutdown(std::forward<Function>(aFunction))));
}
enum class IsInShutdown { No, Yes };
/**
* Append to the message queue a control message to execute a given lambda
* function with a single IsInShutdown parameter. A No argument indicates
* execution on the thread of a graph that is still running. A Yes argument
* indicates execution on the main thread when the graph has been forced to
* shut down.
**/
template <typename Function>
void QueueControlOrShutdownMessage(Function&& aFunction) {
QueueMessage(WrapUnique(
new ControlOrShutdownMessage(std::forward<Function>(aFunction))));
}
/**
* Ensure a runnable will run on the main thread after running all pending
* updates that were sent from the graph thread or will be sent before the
@ -498,6 +525,14 @@ class MediaTrack : public mozilla::LinkedListElement<MediaTrack> {
virtual void AdvanceTimeVaryingValuesToCurrentTime(GraphTime aCurrentTime,
GraphTime aBlockedTime);
private:
template <typename Function>
class ControlMessageWithNoShutdown;
template <typename Function>
class ControlOrShutdownMessage;
void QueueMessage(UniquePtr<ControlMessageInterface> aMessage);
void NotifyMainThreadListeners() {
NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
@ -517,6 +552,7 @@ class MediaTrack : public mozilla::LinkedListElement<MediaTrack> {
return true;
}
protected:
// Notifies listeners and consumers of the change in disabled mode when the
// current combined mode is different from aMode.
void NotifyIfDisabledModeChangedFrom(DisabledTrackMode aOldMode);
@ -1178,8 +1214,9 @@ class MediaTrackGraph {
/**
* This represents a message run on the graph thread to modify track or graph
* state. These are passed from main thread to graph thread through
* AppendMessage(), or scheduled on the graph thread with
* state. These are passed from main thread to graph thread by
* QueueControlMessageWithNoShutdown() or QueueControlOrShutdownMessage()
* through AppendMessage(), or scheduled on the graph thread with
* RunMessageAfterProcessing().
*/
class MediaTrack::ControlMessageInterface {
@ -1194,13 +1231,51 @@ class MediaTrack::ControlMessageInterface {
// computed.
virtual void Run() = 0;
// RunDuringShutdown() is only relevant to messages generated on the main
// thread (for AppendMessage()).
// thread by QueueControlOrShutdownMessage() or for AppendMessage().
// When we're shutting down the application, most messages are ignored but
// some cleanup messages should still be processed (on the main thread).
// This must not add new control messages to the graph.
virtual void RunDuringShutdown() {}
};
template <typename Function>
class MediaTrack::ControlMessageWithNoShutdown
: public ControlMessageInterface {
public:
explicit ControlMessageWithNoShutdown(Function&& aFunction)
: mFunction(std::forward<Function>(aFunction)) {}
void Run() override {
static_assert(std::is_void_v<decltype(mFunction())>,
"The lambda must return void!");
mFunction();
}
private:
using StoredFunction = std::decay_t<Function>;
StoredFunction mFunction;
};
template <typename Function>
class MediaTrack::ControlOrShutdownMessage : public ControlMessageInterface {
public:
explicit ControlOrShutdownMessage(Function&& aFunction)
: mFunction(std::forward<Function>(aFunction)) {}
void Run() override {
static_assert(std::is_void_v<decltype(mFunction(IsInShutdown()))>,
"The lambda must return void!");
mFunction(IsInShutdown::No);
}
void RunDuringShutdown() override { mFunction(IsInShutdown::Yes); }
private:
// The same lambda is used whether or not in shutdown so that captured
// variables are available in both cases.
using StoredFunction = std::decay_t<Function>;
StoredFunction mFunction;
};
} // namespace mozilla
#endif /* MOZILLA_MEDIATRACKGRAPH_H_ */