Bug 1145405 - Add shutdown checks to main thread dispatches in ClearKey CDM - r=cpearce
authorEdwin Flores <edwin@mozilla.com>
Mon, 23 Mar 2015 07:59:42 +1300
changeset 263840 6e36e76aa85537c2d141902c68f7058c43ea972a
parent 263839 ae395a4d961ac759442094904fc31754a4f62c41
child 263841 e0f47fcda5cde133697378cf7a2753a2d9538053
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1145405
milestone39.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 1145405 - Add shutdown checks to main thread dispatches in ClearKey CDM - r=cpearce
media/gmp-clearkey/0.1/AudioDecoder.cpp
media/gmp-clearkey/0.1/AudioDecoder.h
media/gmp-clearkey/0.1/VideoDecoder.cpp
media/gmp-clearkey/0.1/VideoDecoder.h
--- a/media/gmp-clearkey/0.1/AudioDecoder.cpp
+++ b/media/gmp-clearkey/0.1/AudioDecoder.cpp
@@ -25,16 +25,17 @@
 using namespace wmf;
 
 AudioDecoder::AudioDecoder(GMPAudioHost *aHostAPI)
   : mHostAPI(aHostAPI)
   , mCallback(nullptr)
   , mWorkerThread(nullptr)
   , mMutex(nullptr)
   , mNumInputTasks(0)
+  , mHasShutdown(false)
 {
 }
 
 AudioDecoder::~AudioDecoder()
 {
   mMutex->Destroy();
 }
 
@@ -113,17 +114,17 @@ AudioDecoder::DecodeTask(GMPAudioSamples
   if (crypto) {
     // Plugin host should have set up its decryptor/key sessions
     // before trying to decode!
     GMPErr rv =
       ClearKeyDecryptionManager::Get()->Decrypt(&buffer[0], buffer.size(), crypto);
 
     if (GMP_FAILED(rv)) {
       CK_LOGE("Failed to decrypt with key id %08x...", *(uint32_t*)crypto->KeyId());
-      GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::Error, rv));
+      MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::Error, rv));
       return;
     }
   }
 
   hr = mDecoder->Input(&buffer[0],
                        buffer.size(),
                        aInput->TimeStamp());
 
@@ -145,17 +146,17 @@ AudioDecoder::DecodeTask(GMPAudioSamples
     if (hr == S_OK) {
       ReturnOutput(output);
     }
     if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
       AutoLock lock(mMutex);
       if (mNumInputTasks == 0) {
         // We have run all input tasks. We *must* notify Gecko so that it will
         // send us more data.
-        GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::InputDataExhausted));
+        MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::InputDataExhausted));
       }
     } else if (FAILED(hr)) {
       LOG("AudioDecoder::DecodeTask() output failed hr=0x%x\n", hr);
     }
   }
 }
 
 void
@@ -176,17 +177,17 @@ AudioDecoder::ReturnOutput(IMFSample* aS
   hr = MFToGMPSample(aSample, samples);
   if (FAILED(hr)) {
     samples->Destroy();
     LOG("Failed to prepare output sample!");
     return;
   }
   ENSURE(SUCCEEDED(hr), /*void*/);
 
-  GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::Decoded, samples));
+  MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::Decoded, samples));
 }
 
 HRESULT
 AudioDecoder::MFToGMPSample(IMFSample* aInput,
                             GMPAudioSamples* aOutput)
 {
   ENSURE(aInput != nullptr, E_POINTER);
   ENSURE(aOutput != nullptr, E_POINTER);
@@ -236,27 +237,70 @@ AudioDecoder::DrainTask()
   while (hr == S_OK) {
     CComPtr<IMFSample> output;
     hr = mDecoder->Output(&output);
     SAMPLE_LOG("AudioDecoder::DrainTask() output ret=0x%x\n", hr);
     if (hr == S_OK) {
       ReturnOutput(output);
     }
   }
-  GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
+  MaybeRunOnMainThread(WrapTask(mCallback, &GMPAudioDecoderCallback::DrainComplete));
 }
 
 void
 AudioDecoder::Drain()
 {
   EnsureWorker();
   mWorkerThread->Post(WrapTask(this,
                                &AudioDecoder::DrainTask));
 }
 
 void
 AudioDecoder::DecodingComplete()
 {
   if (mWorkerThread) {
     mWorkerThread->Join();
   }
+  mHasShutdown = true;
+
+  // Worker thread might have dispatched more tasks to the main thread that need this object.
+  // Append another task to delete |this|.
+  GetPlatform()->runonmainthread(WrapTask(this, &AudioDecoder::Destroy));
+}
+
+void
+AudioDecoder::Destroy()
+{
   delete this;
 }
+
+void
+AudioDecoder::MaybeRunOnMainThread(gmp_task_args_base* aTask)
+{
+  class MaybeRunTask : public GMPTask
+  {
+  public:
+    MaybeRunTask(AudioDecoder* aDecoder, gmp_task_args_base* aTask)
+      : mDecoder(aDecoder), mTask(aTask)
+    { }
+
+    virtual void Run(void) {
+      if (mDecoder->HasShutdown()) {
+        CK_LOGD("Trying to dispatch to main thread after AudioDecoder has shut down");
+        return;
+      }
+
+      mTask->Run();
+    }
+
+    virtual void Destroy()
+    {
+      mTask->Destroy();
+      delete this;
+    }
+
+  private:
+    AudioDecoder* mDecoder;
+    gmp_task_args_base* mTask;
+  };
+
+  GetPlatform()->runonmainthread(new MaybeRunTask(this, aTask));
+}
--- a/media/gmp-clearkey/0.1/AudioDecoder.h
+++ b/media/gmp-clearkey/0.1/AudioDecoder.h
@@ -14,16 +14,17 @@
  * limitations under the License.
  */
 
 #ifndef __AudioDecoder_h__
 #define __AudioDecoder_h__
 
 #include "gmp-audio-decode.h"
 #include "gmp-audio-host.h"
+#include "gmp-task-utils.h"
 #include "WMFAACDecoder.h"
 
 #include "mfobjects.h"
 
 class AudioDecoder : public GMPAudioDecoder
 {
 public:
   AudioDecoder(GMPAudioHost *aHostAPI);
@@ -36,30 +37,37 @@ public:
   virtual void Decode(GMPAudioSamples* aEncodedSamples);
 
   virtual void Reset() override;
 
   virtual void Drain() override;
 
   virtual void DecodingComplete() override;
 
+  bool HasShutdown() { return mHasShutdown; }
+
 private:
 
   void EnsureWorker();
 
   void DecodeTask(GMPAudioSamples* aEncodedSamples);
   void DrainTask();
 
   void ReturnOutput(IMFSample* aSample);
 
   HRESULT MFToGMPSample(IMFSample* aSample,
                         GMPAudioSamples* aAudioFrame);
 
+  void MaybeRunOnMainThread(gmp_task_args_base* aTask);
+  void Destroy();
+
   GMPAudioHost *mHostAPI; // host-owned, invalid at DecodingComplete
   GMPAudioDecoderCallback* mCallback; // host-owned, invalid at DecodingComplete
   GMPThread* mWorkerThread;
   GMPMutex* mMutex;
   wmf::AutoPtr<wmf::WMFAACDecoder> mDecoder;
 
   int32_t mNumInputTasks;
+
+  bool mHasShutdown;
 };
 
 #endif // __AudioDecoder_h__
--- a/media/gmp-clearkey/0.1/VideoDecoder.cpp
+++ b/media/gmp-clearkey/0.1/VideoDecoder.cpp
@@ -29,16 +29,17 @@ using namespace wmf;
 
 VideoDecoder::VideoDecoder(GMPVideoHost *aHostAPI)
   : mHostAPI(aHostAPI)
   , mCallback(nullptr)
   , mWorkerThread(nullptr)
   , mMutex(nullptr)
   , mNumInputTasks(0)
   , mSentExtraData(false)
+  , mHasShutdown(false)
 {
 }
 
 VideoDecoder::~VideoDecoder()
 {
   mMutex->Destroy();
 }
 
@@ -155,17 +156,17 @@ VideoDecoder::DecodeTask(GMPVideoEncoded
   std::vector<uint8_t> buffer(inBuffer, inBuffer + aInput->Size());
   if (crypto) {
     // Plugin host should have set up its decryptor/key sessions
     // before trying to decode!
     GMPErr rv =
       ClearKeyDecryptionManager::Get()->Decrypt(&buffer[0], buffer.size(), crypto);
 
     if (GMP_FAILED(rv)) {
-      GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPVideoDecoderCallback::Error, rv));
+      MaybeRunOnMainThread(WrapTask(mCallback, &GMPVideoDecoderCallback::Error, rv));
       return;
     }
   }
 
   AnnexB::ConvertFrameInPlace(buffer);
 
   if (aInput->FrameType() == kGMPKeyFrame) {
     // We must send the SPS and PPS to Windows Media Foundation's decoder.
@@ -187,31 +188,31 @@ VideoDecoder::DecodeTask(GMPVideoEncoded
     return;
   }
 
   while (hr == S_OK) {
     CComPtr<IMFSample> output;
     hr = mDecoder->Output(&output);
     CK_LOGD("VideoDecoder::DecodeTask() output ret=0x%x\n", hr);
     if (hr == S_OK) {
-      GetPlatform()->runonmainthread(
+      MaybeRunOnMainThread(
         WrapTask(this,
                  &VideoDecoder::ReturnOutput,
                  CComPtr<IMFSample>(mozilla::Move(output)),
                  mDecoder->GetFrameWidth(),
                  mDecoder->GetFrameHeight(),
                  mDecoder->GetStride()));
       assert(!output.Get());
     }
     if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
       AutoLock lock(mMutex);
       if (mNumInputTasks == 0) {
         // We have run all input tasks. We *must* notify Gecko so that it will
         // send us more data.
-        GetPlatform()->runonmainthread(
+        MaybeRunOnMainThread(
           WrapTask(mCallback,
                    &GMPVideoDecoderCallback::InputDataExhausted));
       }
     }
     if (FAILED(hr)) {
       CK_LOGE("VideoDecoder::DecodeTask() output failed hr=0x%x\n", hr);
     }
   }
@@ -342,37 +343,80 @@ VideoDecoder::DrainTask()
 
   // Return any pending output.
   HRESULT hr = S_OK;
   while (hr == S_OK) {
     CComPtr<IMFSample> output;
     hr = mDecoder->Output(&output);
     CK_LOGD("VideoDecoder::DrainTask() output ret=0x%x\n", hr);
     if (hr == S_OK) {
-      GetPlatform()->runonmainthread(
+      MaybeRunOnMainThread(
         WrapTask(this,
                  &VideoDecoder::ReturnOutput,
                  CComPtr<IMFSample>(mozilla::Move(output)),
                  mDecoder->GetFrameWidth(),
                  mDecoder->GetFrameHeight(),
                  mDecoder->GetStride()));
       assert(!output.Get());
     }
   }
-  GetPlatform()->runonmainthread(WrapTask(mCallback, &GMPVideoDecoderCallback::DrainComplete));
+  MaybeRunOnMainThread(WrapTask(mCallback, &GMPVideoDecoderCallback::DrainComplete));
 }
 
 void
 VideoDecoder::Drain()
 {
   EnsureWorker();
   mWorkerThread->Post(WrapTask(this,
                                &VideoDecoder::DrainTask));
 }
 
 void
 VideoDecoder::DecodingComplete()
 {
   if (mWorkerThread) {
     mWorkerThread->Join();
   }
+  mHasShutdown = true;
+
+  // Worker thread might have dispatched more tasks to the main thread that need this object.
+  // Append another task to delete |this|.
+  GetPlatform()->runonmainthread(WrapTask(this, &VideoDecoder::Destroy));
+}
+
+void
+VideoDecoder::Destroy()
+{
   delete this;
 }
+
+void
+VideoDecoder::MaybeRunOnMainThread(gmp_task_args_base* aTask)
+{
+  class MaybeRunTask : public GMPTask
+  {
+  public:
+    MaybeRunTask(VideoDecoder* aDecoder, gmp_task_args_base* aTask)
+      : mDecoder(aDecoder), mTask(aTask)
+    { }
+
+    virtual void Run(void) {
+      if (mDecoder->HasShutdown()) {
+        CK_LOGD("Trying to dispatch to main thread after VideoDecoder has shut down");
+        return;
+      }
+
+      mTask->Run();
+    }
+
+    virtual void Destroy()
+    {
+      mTask->Destroy();
+      delete this;
+    }
+
+  private:
+    VideoDecoder* mDecoder;
+    gmp_task_args_base* mTask;
+  };
+
+  GetPlatform()->runonmainthread(new MaybeRunTask(this, aTask));
+}
--- a/media/gmp-clearkey/0.1/VideoDecoder.h
+++ b/media/gmp-clearkey/0.1/VideoDecoder.h
@@ -12,16 +12,17 @@
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef __VideoDecoder_h__
 #define __VideoDecoder_h__
 
+#include "gmp-task-utils.h"
 #include "gmp-video-decode.h"
 #include "gmp-video-host.h"
 #include "WMFH264Decoder.h"
 
 #include "mfobjects.h"
 
 class VideoDecoder : public GMPVideoDecoder
 {
@@ -43,16 +44,18 @@ public:
                       int64_t aRenderTimeMs = -1);
 
   virtual void Reset() override;
 
   virtual void Drain() override;
 
   virtual void DecodingComplete() override;
 
+  bool HasShutdown() { return mHasShutdown; }
+
 private:
 
   void EnsureWorker();
 
   void DrainTask();
 
   void DecodeTask(GMPVideoEncodedFrame* aInputFrame);
 
@@ -62,22 +65,27 @@ private:
                     int32_t aStride);
 
   HRESULT SampleToVideoFrame(IMFSample* aSample,
                              int32_t aWidth,
                              int32_t aHeight,
                              int32_t aStride,
                              GMPVideoi420Frame* aVideoFrame);
 
+  void MaybeRunOnMainThread(gmp_task_args_base* aTask);
+  void Destroy();
+
   GMPVideoHost *mHostAPI; // host-owned, invalid at DecodingComplete
   GMPVideoDecoderCallback* mCallback; // host-owned, invalid at DecodingComplete
   GMPThread* mWorkerThread;
   GMPMutex* mMutex;
   wmf::AutoPtr<wmf::WMFH264Decoder> mDecoder;
 
   std::vector<uint8_t> mExtraData;
   std::vector<uint8_t> mAnnexB;
 
   int32_t mNumInputTasks;
   bool mSentExtraData;
+
+  bool mHasShutdown;
 };
 
 #endif // __VideoDecoder_h__