Bug 1539039 finish worklet before destination stream is destroyed r=padenot
authorKarl Tomlinson <karlt+@karlt.net>
Fri, 29 Mar 2019 16:12:08 +0000
changeset 526327 973f69d0a81036de9a73b4980755b9bfaa1873f1
parent 526326 76b3bde564ff2dcc6b7facb17e15eeae056e68c8
child 526328 f3098fb60d80e9de86714d5f14f12fb2e55351e8
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs1539039
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1539039 finish worklet before destination stream is destroyed r=padenot AudioDestinationNode, AudioContext, and Worklet are part of the same cycle. Any of these may be unlinked before another. The unlink process for this cycle is the notification that the worklet will no longer be used, but the worklet uses the destination stream to send a message to release graph thread objects and so must do this before the stream is destroyed. Differential Revision: https://phabricator.services.mozilla.com/D25351
dom/media/webaudio/AudioContext.cpp
dom/media/webaudio/AudioContext.h
dom/media/webaudio/AudioDestinationNode.cpp
dom/worklet/Worklet.h
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -87,16 +87,20 @@ namespace dom {
 
 // 0 is a special value that MediaStreams use to denote they are not part of a
 // AudioContext.
 static dom::AudioContext::AudioContextId gAudioContextId = 1;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioContext)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioContext)
+  // The destination node and AudioContext form a cycle and so the destination
+  // stream will be destroyed.  mWorklet must be shut down before the stream
+  // is destroyed.  Do this before clearing mWorklet.
+  tmp->ShutdownWorklet();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDestination)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWorklet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPromiseGripArray)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingResumePromises)
   if (!tmp->mIsStarted) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveNodes)
   }
@@ -627,16 +631,22 @@ MediaStreamGraph* AudioContext::Graph() 
 
 AudioNodeStream* AudioContext::DestinationStream() const {
   if (Destination()) {
     return Destination()->Stream();
   }
   return nullptr;
 }
 
+void AudioContext::ShutdownWorklet() {
+  if (mWorklet) {
+    mWorklet->Impl()->NotifyWorkletFinished();
+  }
+}
+
 double AudioContext::CurrentTime() {
   MediaStream* stream = Destination()->Stream();
 
   double rawTime = stream->StreamTimeToSeconds(stream->GetCurrentTime());
 
   // CurrentTime increments in intervals of 128/sampleRate. If the Timer
   // Precision Reduction is smaller than this interval, the jittered time
   // can always be reversed to the raw step of the interval. In that case
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -305,16 +305,17 @@ class AudioContext final : public DOMEve
 
   void RegisterNode(AudioNode* aNode);
   void UnregisterNode(AudioNode* aNode);
 
   void OnStateChanged(void* aPromise, AudioContextState aNewState);
 
   BasicWaveFormCache* GetBasicWaveFormCache();
 
+  void ShutdownWorklet();
   // Steals from |aParamMap|
   void SetParamMapForWorkletName(const nsAString& aName,
                                  AudioParamDescriptorMap* aParamMap);
   const AudioParamDescriptorMap* GetParamMapForWorkletName(
       const nsAString& aName) {
     return mWorkletParamDescriptors.GetValue(aName);
   }
 
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -396,16 +396,18 @@ void AudioDestinationNode::DestroyAudioC
   }
 }
 
 void AudioDestinationNode::DestroyMediaStream() {
   DestroyAudioChannelAgent();
 
   if (!mStream) return;
 
+  Context()->ShutdownWorklet();
+
   mStream->RemoveMainThreadListener(this);
   MediaStreamGraph* graph = mStream->Graph();
   if (graph->IsNonRealtime()) {
     MediaStreamGraph::DestroyNonRealtimeInstance(graph);
   }
   AudioNode::DestroyMediaStream();
 }
 
--- a/dom/worklet/Worklet.h
+++ b/dom/worklet/Worklet.h
@@ -41,16 +41,18 @@ class Worklet final : public nsISupports
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   already_AddRefed<Promise> AddModule(const nsAString& aModuleURL,
                                       const WorkletOptions& aOptions,
                                       CallerType aCallerType, ErrorResult& aRv);
 
+  WorkletImpl* Impl() const { return mImpl; }
+
  private:
   ~Worklet();
 
   WorkletFetchHandler* GetImportFetchHandler(const nsACString& aURI);
 
   void AddImportFetchHandler(const nsACString& aURI,
                              WorkletFetchHandler* aHandler);