Bug 910171 - Add a general means to keep active nodes alive from the AudioContext. r=ehsan, a=akeybl
authorKarl Tomlinson <karlt+@karlt.net>
Tue, 17 Sep 2013 11:53:40 +1200
changeset 154097 a077f41345466e02df1ca20b7f90b80fa3e4e7d0
parent 154096 7caeec1e5f7badd0447657a2cdb89ed81959ee8b
child 154098 acee2cb7f50725cea5b55d07c6cdb0c40e42887b
push id1
push usersledru@mozilla.com
push dateThu, 04 Dec 2014 17:57:20 +0000
reviewersehsan, akeybl
bugs910171, 914033
milestone25.0
Bug 910171 - Add a general means to keep active nodes alive from the AudioContext. r=ehsan, a=akeybl AudioNodes that keep playing or tail-time references need to have these references cleared when an AudioContext has completed or is shut down by the window. Storing references on the AudioContext instead of on the AudioNodes will allow the AudioContext to report playing references to the cycle collector until offline rendering starts for bug 914033. This is not necessary for tail-time references, but it is tidier to use the same code for playing and tail-time references.
content/media/webaudio/AudioContext.cpp
content/media/webaudio/AudioContext.h
content/media/webaudio/AudioNode.h
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -438,16 +438,28 @@ AudioContext::DecodeAudioData(const Arra
 
 void
 AudioContext::RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob)
 {
   mDecodeJobs.RemoveElement(aDecodeJob);
 }
 
 void
+AudioContext::RegisterActiveNode(AudioNode* aNode)
+{
+  mActiveNodes.PutEntry(aNode);
+}
+
+void
+AudioContext::UnregisterActiveNode(AudioNode* aNode)
+{
+  mActiveNodes.RemoveEntry(aNode);
+}
+
+void
 AudioContext::UnregisterAudioBufferSourceNode(AudioBufferSourceNode* aNode)
 {
   mAudioBufferSourceNodes.RemoveEntry(aNode);
   UpdatePannerSource();
 }
 
 void
 AudioContext::UnregisterPannerNode(PannerNode* aNode)
@@ -523,16 +535,21 @@ GetHashtableElements(nsTHashtable<nsPtrH
   aHashtable.EnumerateEntries(&GetHashtableEntry<T>, &aArray);
 }
 
 void
 AudioContext::Shutdown()
 {
   Suspend();
 
+  // Release references to active nodes.
+  // Active AudioNodes don't unregister in destructors, at which point the
+  // Node is already unregistered.
+  mActiveNodes.Clear();
+
   // Stop all audio buffer source nodes, to make sure that they release
   // their self-references.
   // We first gather an array of the nodes and then call Stop on each one,
   // since Stop may delete the object and therefore trigger a re-entrant
   // hashtable call to remove the pointer from the hashtable, which is
   // not safe.
   nsTArray<AudioBufferSourceNode*> sourceNodes;
   GetHashtableElements(mAudioBufferSourceNodes, sourceNodes);
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -39,16 +39,17 @@ struct WebAudioDecodeJob;
 
 namespace dom {
 
 class AnalyserNode;
 class AudioBuffer;
 class AudioBufferSourceNode;
 class AudioDestinationNode;
 class AudioListener;
+class AudioNode;
 class BiquadFilterNode;
 class ChannelMergerNode;
 class ChannelSplitterNode;
 class ConvolverNode;
 class DelayNode;
 class DynamicsCompressorNode;
 class GainNode;
 class GlobalObject;
@@ -211,16 +212,30 @@ public:
   // OfflineAudioContext methods
   void StartRendering();
   IMPL_EVENT_HANDLER(complete)
 
   bool IsOffline() const { return mIsOffline; }
 
   MediaStreamGraph* Graph() const;
   MediaStream* DestinationStream() const;
+
+  // Nodes register here if they will produce sound even if they have silent
+  // or no input connections.  The AudioContext will keep registered nodes
+  // alive until the context is collected.  This takes care of "playing"
+  // references and "tail-time" references.
+  void RegisterActiveNode(AudioNode* aNode);
+  // Nodes unregister when they have finished producing sound for the
+  // foreseeable future.
+  // Do NOT call UnregisterActiveNode from an AudioNode destructor.
+  // If the destructor is called, then the Node has already been unregistered.
+  // The destructor may be called during hashtable enumeration, during which
+  // unregistering would not be safe.
+  void UnregisterActiveNode(AudioNode* aNode);
+
   void UnregisterAudioBufferSourceNode(AudioBufferSourceNode* aNode);
   void UnregisterPannerNode(PannerNode* aNode);
   void UnregisterOscillatorNode(OscillatorNode* aNode);
   void UnregisterScriptProcessorNode(ScriptProcessorNode* aNode);
   void UpdatePannerSource();
 
   uint32_t MaxChannelCount() const;
 
@@ -238,16 +253,19 @@ private:
 private:
   // Note that it's important for mSampleRate to be initialized before
   // mDestination, as mDestination's constructor needs to access it!
   const float mSampleRate;
   nsRefPtr<AudioDestinationNode> mDestination;
   nsRefPtr<AudioListener> mListener;
   MediaBufferDecoder mDecoder;
   nsTArray<nsRefPtr<WebAudioDecodeJob> > mDecodeJobs;
+  // See RegisterActiveNode.  These will keep the AudioContext alive while it
+  // is rendering and the window remains alive.
+  nsTHashtable<nsRefPtrHashKey<AudioNode> > mActiveNodes;
   // 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.
--- a/content/media/webaudio/AudioNode.h
+++ b/content/media/webaudio/AudioNode.h
@@ -100,19 +100,19 @@ private:
 
 /**
  * The DOM object representing a Web Audio AudioNode.
  *
  * Each AudioNode has a MediaStream representing the actual
  * real-time processing and output of this AudioNode.
  *
  * We track the incoming and outgoing connections to other AudioNodes.
- * Outgoing connections have strong ownership.  Also, AudioNodes add self
- * references if they produce sound on their output even when they have silent
- * or no input.
+ * Outgoing connections have strong ownership.  Also, AudioNodes that will
+ * produce sound on their output even when they have silent or no input ask
+ * the AudioContext to keep them alive until the context is finished.
  */
 class AudioNode : public nsDOMEventTargetHelper,
                   public EnableWebAudioCheck
 {
 protected:
   // You can only use refcounting to delete this object
   virtual ~AudioNode();
 
@@ -211,16 +211,27 @@ public:
   {
     return mInputNodes;
   }
 
   void RemoveOutputParam(AudioParam* aParam);
 
   virtual void NotifyInputConnected() {}
 
+  // MarkActive() asks the context to keep the AudioNode alive until the
+  // context is finished.  This takes care of "playing" references and
+  // "tail-time" references.
+  void MarkActive() { Context()->RegisterActiveNode(this); }
+  // Active nodes call MarkInactive() when they have finished producing sound
+  // for the foreseeable future.
+  // Do not call MarkInactive from a node destructor.  If the destructor is
+  // called, then the node is already inactive.
+  // MarkInactive() may delete |this|.
+  void MarkInactive() { Context()->UnregisterActiveNode(this); }
+
 private:
   friend class AudioBufferSourceNode;
   // This could possibly delete 'this'.
   void DisconnectFromGraph();
 
 protected:
   static void Callback(AudioNode* aNode) { /* not implemented */ }