b=910171 add a general means to keep active nodes alive from the AudioContext r=ehsan
authorKarl Tomlinson <karlt+@karlt.net>
Tue, 17 Sep 2013 11:53:40 +1200
changeset 147668 efdd34cc38afa7cb5fa4ac44a66e040861e72bca
parent 147667 a45b455c7c31862498e1ad5a26538c8209c135dd
child 147669 81d850fca7b0ea73c2dcfa6ae83cdf74d37dc28f
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersehsan
bugs910171, 914033
milestone27.0a1
b=910171 add a general means to keep active nodes alive from the AudioContext r=ehsan 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
@@ -436,16 +436,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)
@@ -521,16 +533,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
@@ -36,16 +36,17 @@ class MediaStreamGraph;
 
 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 HTMLMediaElement;
@@ -207,16 +208,30 @@ public:
   // OfflineAudioContext methods
   void StartRendering(ErrorResult& aRv);
   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;
 
@@ -234,16 +249,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
@@ -97,19 +97,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();
 
@@ -208,16 +208,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 */ }