Bug 865253 - Part 4: Stop OscillatorNodes on context shutdown. r=ehsan, a=webaudio
authorRalph Giles <giles@mozilla.com>
Thu, 04 Jul 2013 17:34:41 -0700
changeset 153802 78a602878032be2dacf76c7bf2c1fcf9cbf6647a
parent 153801 cfeac11731aea8f41a57fab2f2a6ff50697ccf36
child 153803 a152af6e080ea29e776d83ea4afc481fe3003bbf
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, webaudio
bugs865253
milestone25.0a2
Bug 865253 - Part 4: Stop OscillatorNodes on context shutdown. r=ehsan, a=webaudio Like AudioBufferSourceNode and ScriptProcessorNode, OscillatorNode holds a self-reference in mPlaying. Keep track of all osc nodes in the AudioContext and explicitly stop them to make sure this reference is dropped.
content/media/webaudio/AudioContext.cpp
content/media/webaudio/AudioContext.h
content/media/webaudio/OscillatorNode.cpp
content/media/webaudio/OscillatorNode.h
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -58,16 +58,17 @@ AudioContext::AudioContext(nsPIDOMWindow
 {
   // Actually play audio
   mDestination->Stream()->AddAudioOutput(&gWebAudioOutputKey);
   nsDOMEventTargetHelper::BindToOwner(aWindow);
   SetIsDOMBinding();
 
   mPannerNodes.Init();
   mAudioBufferSourceNodes.Init();
+  mOscillatorNodes.Init();
   mScriptProcessorNodes.Init();
 }
 
 AudioContext::~AudioContext()
 {
 }
 
 JSObject*
@@ -338,16 +339,17 @@ AudioContext::CreateBiquadFilter()
   return filterNode.forget();
 }
 
 already_AddRefed<OscillatorNode>
 AudioContext::CreateOscillator()
 {
   nsRefPtr<OscillatorNode> oscillatorNode =
     new OscillatorNode(this);
+  mOscillatorNodes.PutEntry(oscillatorNode);
   return oscillatorNode.forget();
 }
 
 already_AddRefed<PeriodicWave>
 AudioContext::CreatePeriodicWave(const Float32Array& aRealData,
                                  const Float32Array& aImagData,
                                  ErrorResult& aRv)
 {
@@ -416,16 +418,22 @@ AudioContext::UnregisterPannerNode(Panne
 {
   mPannerNodes.RemoveEntry(aNode);
   if (mListener) {
     mListener->UnregisterPannerNode(aNode);
   }
 }
 
 void
+AudioContext::UnregisterOscillatorNode(OscillatorNode* aNode)
+{
+  mOscillatorNodes.RemoveEntry(aNode);
+}
+
+void
 AudioContext::UnregisterScriptProcessorNode(ScriptProcessorNode* aNode)
 {
   mScriptProcessorNodes.RemoveEntry(aNode);
 }
 
 static PLDHashOperator
 FindConnectedSourcesOn(nsPtrHashKey<PannerNode>* aEntry, void* aData)
 {
@@ -506,16 +514,24 @@ AudioContext::Shutdown()
   // hashtable call to remove the pointer from the hashtable, which is
   // not safe.
   nsTArray<AudioBufferSourceNode*> sourceNodes;
   GetHashtableElements(mAudioBufferSourceNodes, sourceNodes);
   for (uint32_t i = 0; i < sourceNodes.Length(); ++i) {
     ErrorResult rv;
     sourceNodes[i]->Stop(0.0, rv, true);
   }
+  // Stop all Oscillator nodes to make sure they release their
+  // playing reference.
+  nsTArray<OscillatorNode*> oscNodes;
+  GetHashtableElements(mOscillatorNodes, oscNodes);
+  for (uint32_t i = 0; i < oscNodes.Length(); ++i) {
+    ErrorResult rv;
+    oscNodes[i]->Stop(0.0, rv);
+  }
   // Stop all script processor nodes, to make sure that they release
   // their self-references.
   nsTArray<ScriptProcessorNode*> spNodes;
   GetHashtableElements(mScriptProcessorNodes, spNodes);
   for (uint32_t i = 0; i < spNodes.Length(); ++i) {
     spNodes[i]->Stop();
   }
 
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -206,16 +206,17 @@ public:
   IMPL_EVENT_HANDLER(complete)
 
   bool IsOffline() const { return mIsOffline; }
 
   MediaStreamGraph* Graph() const;
   MediaStream* DestinationStream() const;
   void UnregisterAudioBufferSourceNode(AudioBufferSourceNode* aNode);
   void UnregisterPannerNode(PannerNode* aNode);
+  void UnregisterOscillatorNode(OscillatorNode* aNode);
   void UnregisterScriptProcessorNode(ScriptProcessorNode* aNode);
   void UpdatePannerSource();
 
   uint32_t MaxChannelCount() const;
 
   void Mute() const;
   void Unmute() const;
 
@@ -235,16 +236,17 @@ private:
   nsRefPtr<AudioListener> mListener;
   MediaBufferDecoder mDecoder;
   nsTArray<nsRefPtr<WebAudioDecodeJob> > mDecodeJobs;
   // Two hashsets containing all the PannerNodes and AudioBufferSourceNodes,
   // to compute the doppler shift, and also to stop AudioBufferSourceNodes.
   // These are all weak pointers.
   nsTHashtable<nsPtrHashKey<PannerNode> > mPannerNodes;
   nsTHashtable<nsPtrHashKey<AudioBufferSourceNode> > mAudioBufferSourceNodes;
+  nsTHashtable<nsPtrHashKey<OscillatorNode> > mOscillatorNodes;
   // Hashset containing all ScriptProcessorNodes in order to stop them.
   // These are all weak pointers.
   nsTHashtable<nsPtrHashKey<ScriptProcessorNode> > mScriptProcessorNodes;
   // Number of channels passed in the OfflineAudioContext ctor.
   uint32_t mNumberOfChannels;
   bool mIsOffline;
 };
 
--- a/content/media/webaudio/OscillatorNode.cpp
+++ b/content/media/webaudio/OscillatorNode.cpp
@@ -8,18 +8,32 @@
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "AudioDestinationNode.h"
 #include "WebAudioUtils.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED_3(OscillatorNode, AudioNode,
-                                     mPeriodicWave, mFrequency, mDetune)
+NS_IMPL_CYCLE_COLLECTION_CLASS(OscillatorNode)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(OscillatorNode)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPeriodicWave)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrequency)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDetune)
+  if (tmp->Context()) {
+    tmp->Context()->UnregisterOscillatorNode(tmp);
+  }
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode);
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(OscillatorNode, AudioNode)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPeriodicWave)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrequency)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDetune)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OscillatorNode)
 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
 
 NS_IMPL_ADDREF_INHERITED(OscillatorNode, AudioNode)
 NS_IMPL_RELEASE_INHERITED(OscillatorNode, AudioNode)
 
 class OscillatorNodeEngine : public AudioNodeEngine
@@ -284,16 +298,23 @@ OscillatorNode::OscillatorNode(AudioCont
   , mStartCalled(false)
   , mStopped(false)
 {
   OscillatorNodeEngine* engine = new OscillatorNodeEngine(this, aContext->Destination());
   mStream = aContext->Graph()->CreateAudioNodeStream(engine, MediaStreamGraph::SOURCE_STREAM);
   engine->SetSourceStream(static_cast<AudioNodeStream*> (mStream.get()));
 }
 
+OscillatorNode::~OscillatorNode()
+{
+  if (Context()) {
+    Context()->UnregisterOscillatorNode(this);
+  }
+}
+
 JSObject*
 OscillatorNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return OscillatorNodeBinding::Wrap(aCx, aScope, this);
 }
 
 void
 OscillatorNode::SendFrequencyToStream(AudioNode* aNode)
--- a/content/media/webaudio/OscillatorNode.h
+++ b/content/media/webaudio/OscillatorNode.h
@@ -18,16 +18,17 @@ namespace dom {
 
 class AudioContext;
 
 class OscillatorNode : public AudioNode,
                        public MainThreadMediaStreamListener
 {
 public:
   explicit OscillatorNode(AudioContext* aContext);
+  virtual ~OscillatorNode();
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OscillatorNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
 
   virtual void DestroyMediaStream() MOZ_OVERRIDE