Bug 1042884 - Wait until AudioInitTask finished before proceeding to Shutdown(). r=kinetik, a=sledru
authorJW Wang <jwwang@mozilla.com>
Sun, 17 Aug 2014 19:18:00 -0400
changeset 216401 39a821f3aac9915d07fed487c099c6a517f3ec0b
parent 216400 afbb49c2e22c4cd474949f6b519d24e2da76741d
child 216402 b0b59289683f0a89fc89f6bc790ae85a50963113
push id3857
push userraliiev@mozilla.com
push dateTue, 02 Sep 2014 16:39:23 +0000
treeherdermozilla-beta@5638b907b505 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik, sledru
bugs1042884
milestone33.0a2
Bug 1042884 - Wait until AudioInitTask finished before proceeding to Shutdown(). r=kinetik, a=sledru
content/media/AudioStream.cpp
content/media/AudioStream.h
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -11,16 +11,17 @@
 #include "VideoUtils.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Mutex.h"
 #include <algorithm>
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "soundtouch/SoundTouch.h"
 #include "Latency.h"
+#include "nsPrintfCString.h"
 
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
 
 #ifdef PR_LOGGING
@@ -245,16 +246,17 @@ AudioStream::AudioStream()
   , mAudioClock(MOZ_THIS_IN_INITIALIZER_LIST())
   , mLatencyRequest(HighLatency)
   , mReadPoint(0)
   , mDumpFile(nullptr)
   , mVolume(1.0)
   , mBytesPerFrame(0)
   , mState(INITIALIZED)
   , mNeedsStart(false)
+  , mPendingAudioInitTask(false)
 {
   // keep a ref in case we shut down later than nsLayoutStatics
   mLatencyLog = AsyncLatencyLogger::Get(true);
 }
 
 AudioStream::~AudioStream()
 {
   LOG(("AudioStream: delete %p, state %d", this, mState));
@@ -524,46 +526,45 @@ AudioStream::Init(int32_t aNumChannels, 
   mBuffer.SetCapacity(bufferLimit);
 
   if (aLatencyRequest == LowLatency) {
     // Don't block this thread to initialize a cubeb stream.
     // When this is done, it will start callbacks from Cubeb.  Those will
     // cause us to move from INITIALIZED to RUNNING.  Until then, we
     // can't access any cubeb functions.
     // Use a RefPtr to avoid leaks if Dispatch fails
+    mPendingAudioInitTask = true;
     RefPtr<AudioInitTask> init = new AudioInitTask(this, aLatencyRequest, params);
-    init->Dispatch();
-    return NS_OK;
+    nsresult rv = init->Dispatch();
+    if (NS_FAILED(rv)) {
+      mPendingAudioInitTask = false;
+    }
+    return rv;
   }
   // High latency - open synchronously
   nsresult rv = OpenCubeb(params, aLatencyRequest);
+  NS_ENSURE_SUCCESS(rv, rv);
   // See if we need to start() the stream, since we must do that from this
   // thread for now (cubeb API issue)
   {
     MonitorAutoLock mon(mMonitor);
     CheckForStart();
   }
-  return rv;
+  return NS_OK;
 }
 
 // This code used to live inside AudioStream::Init(), but on Mac (others?)
 // it has been known to take 300-800 (or even 8500) ms to execute(!)
 nsresult
 AudioStream::OpenCubeb(cubeb_stream_params &aParams,
                        LatencyRequest aLatencyRequest)
 {
-  {
-    MonitorAutoLock mon(mMonitor);
-    if (mState == AudioStream::SHUTDOWN) {
-      return NS_ERROR_FAILURE;
-    }
-  }
-
   cubeb* cubebContext = GetCubebContext();
   if (!cubebContext) {
+    NS_WARNING("Can't get cubeb context!");
     MonitorAutoLock mon(mMonitor);
     mState = AudioStream::ERRORED;
     return NS_ERROR_FAILURE;
   }
 
   // If the latency pref is set, use it. Otherwise, if this stream is intended
   // for low latency playback, try to get the lowest latency possible.
   // Otherwise, for normal streams, use 100ms.
@@ -576,47 +577,50 @@ AudioStream::OpenCubeb(cubeb_stream_para
     latency = GetCubebLatency();
   }
 
   {
     cubeb_stream* stream;
     if (cubeb_stream_init(cubebContext, &stream, "AudioStream", aParams,
                           latency, DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
       MonitorAutoLock mon(mMonitor);
+      MOZ_ASSERT(mState != SHUTDOWN);
       mCubebStream.own(stream);
-      // Make sure we weren't shut down while in flight!
-      if (mState == SHUTDOWN) {
-        mCubebStream.reset();
-        LOG(("AudioStream::OpenCubeb() %p Shutdown while opening cubeb", this));
-        return NS_ERROR_FAILURE;
-      }
 
       // We can't cubeb_stream_start() the thread from a transient thread due to
       // cubeb API requirements (init can be called from another thread, but
       // not start/stop/destroy/etc)
     } else {
       MonitorAutoLock mon(mMonitor);
       mState = ERRORED;
-      LOG(("AudioStream::OpenCubeb() %p failed to init cubeb", this));
+      NS_WARNING(nsPrintfCString("AudioStream::OpenCubeb() %p failed to init cubeb", this).get());
       return NS_ERROR_FAILURE;
     }
   }
 
   if (!mStartTime.IsNull()) {
 		TimeDuration timeDelta = TimeStamp::Now() - mStartTime;
     LOG(("AudioStream creation time %sfirst: %u ms", mIsFirst ? "" : "not ",
          (uint32_t) timeDelta.ToMilliseconds()));
 		Telemetry::Accumulate(mIsFirst ? Telemetry::AUDIOSTREAM_FIRST_OPEN_MS :
                           Telemetry::AUDIOSTREAM_LATER_OPEN_MS, timeDelta.ToMilliseconds());
   }
 
   return NS_OK;
 }
 
 void
+AudioStream::AudioInitTaskFinished()
+{
+  MonitorAutoLock mon(mMonitor);
+  mPendingAudioInitTask = false;
+  mon.NotifyAll();
+}
+
+void
 AudioStream::CheckForStart()
 {
   mMonitor.AssertCurrentThreadOwns();
   if (mState == INITIALIZED) {
     // Start the stream right away when low latency has been requested. This means
     // that the DataCallback will feed silence to cubeb, until the first frames
     // are written to this AudioStream.  Also start if a start has been queued.
     if (mLatencyRequest == LowLatency || mNeedsStart) {
@@ -644,16 +648,17 @@ AudioInitTask::Run()
     // See bug 999104.  We must hold a ref to the thread across Dispatch()
     // since the internal mThread ref could be released while processing
     // the Dispatch(), and Dispatch/PutEvent itself doesn't hold a ref; it
     // assumes the caller does.
     return NS_OK;
   }
 
   nsresult rv = mAudioStream->OpenCubeb(mParams, mLatencyRequest);
+  mAudioStream->AudioInitTaskFinished();
 
   // and now kill this thread
   NS_DispatchToMainThread(this);
   return rv;
 }
 
 // aTime is the time in ms the samples were inserted into MediaStreamGraph
 nsresult
@@ -844,16 +849,20 @@ AudioStream::Resume()
 }
 
 void
 AudioStream::Shutdown()
 {
   MonitorAutoLock mon(mMonitor);
   LOG(("AudioStream: Shutdown %p, state %d", this, mState));
 
+  while (mPendingAudioInitTask) {
+    mon.Wait();
+  }
+
   if (mCubebStream) {
     MonitorAutoUnlock mon(mMonitor);
     // Force stop to put the cubeb stream in a stable state before deletion.
     cubeb_stream_stop(mCubebStream);
     // Must not try to shut down cubeb from within the lock!  wasapi may still
     // call our callback after Pause()/stop()!?! Bug 996162
     mCubebStream.reset();
   }
--- a/content/media/AudioStream.h
+++ b/content/media/AudioStream.h
@@ -278,16 +278,17 @@ protected:
   int64_t GetPositionInFramesUnlocked();
 
 private:
   friend class AudioInitTask;
 
   // So we can call it asynchronously from AudioInitTask
   nsresult OpenCubeb(cubeb_stream_params &aParams,
                      LatencyRequest aLatencyRequest);
+  void AudioInitTaskFinished();
 
   void CheckForStart();
 
   static void PrefChanged(const char* aPref, void* aClosure);
   static double GetVolumeScale();
   static bool GetFirstStream();
   static cubeb* GetCubebContext();
   static cubeb* GetCubebContextUnlocked();
@@ -393,16 +394,20 @@ private:
     ERRORED,     // Stream disabled due to an internal error.
     SHUTDOWN     // Shutdown has been called
   };
 
   StreamState mState;
   bool mNeedsStart; // needed in case Start() is called before cubeb is open
   bool mIsFirst;
 
+  // True if there is a pending AudioInitTask. Shutdown() will wait until the
+  // pending AudioInitTask is finished.
+  bool mPendingAudioInitTask;
+
   // This mutex protects the static members below.
   static StaticMutex sMutex;
   static cubeb* sCubebContext;
 
   // Prefered samplerate, in Hz (characteristic of the
   // hardware/mixer/platform/API used).
   static uint32_t sPreferredSampleRate;