Bug 1508434 - p3: assert members are only accessed on task queue. r=jya
authorJohn Lin <jolin@mozilla.com>
Fri, 11 Jan 2019 17:08:03 +0000
changeset 510609 d568d717aa5bb4b4dedd696a5e5b0a127fdf7a1a
parent 510608 dcfc482c09693d7289fa275c850ccb4105caa95a
child 510610 b6a0a003d609b4d67490367ef717a66fa48dfada
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya
bugs1508434
milestone66.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 1508434 - p3: assert members are only accessed on task queue. r=jya Differential Revision: https://phabricator.services.mozilla.com/D15740
dom/media/platforms/android/RemoteDataDecoder.cpp
dom/media/platforms/android/RemoteDataDecoder.h
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -506,67 +506,69 @@ RemoteDataDecoder::RemoteDataDecoder(Med
 
 RefPtr<MediaDataDecoder::FlushPromise> RemoteDataDecoder::Flush() {
   RefPtr<RemoteDataDecoder> self = this;
   return InvokeAsync(mTaskQueue, this, __func__,
                      &RemoteDataDecoder::ProcessFlush);
 }
 
 RefPtr<MediaDataDecoder::FlushPromise> RemoteDataDecoder::ProcessFlush() {
+  AssertOnTaskQueue();
+
   mDecodedData = DecodedData();
-  mNumPendingInputs = 0;
+  UpdatePendingInputStatus(PendingOp::CLEAR);
   mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
-  mDrainStatus = DrainStatus::DRAINED;
+  SetState(State::DRAINED);
   mJavaDecoder->Flush();
   return FlushPromise::CreateAndResolve(true, __func__);
 }
 
 RefPtr<MediaDataDecoder::DecodePromise> RemoteDataDecoder::Drain() {
   RefPtr<RemoteDataDecoder> self = this;
   return InvokeAsync(mTaskQueue, __func__, [self, this]() {
-    if (mShutdown) {
+    if (GetState() == State::SHUTDOWN) {
       return DecodePromise::CreateAndReject(NS_ERROR_DOM_MEDIA_CANCELED,
                                             __func__);
     }
     RefPtr<DecodePromise> p = mDrainPromise.Ensure(__func__);
-    if (mDrainStatus == DrainStatus::DRAINED) {
+    if (GetState() == State::DRAINED) {
       // There's no operation to perform other than returning any already
       // decoded data.
       ReturnDecodedData();
       return p;
     }
 
-    if (mDrainStatus == DrainStatus::DRAINING) {
+    if (GetState() == State::DRAINING) {
       // Draining operation already pending, let it complete its course.
       return p;
     }
 
     BufferInfo::LocalRef bufferInfo;
     nsresult rv = BufferInfo::New(&bufferInfo);
     if (NS_FAILED(rv)) {
       return DecodePromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
     }
-    mDrainStatus = DrainStatus::DRAINING;
+    SetState(State::DRAINING);
     bufferInfo->Set(0, 0, -1, MediaCodec::BUFFER_FLAG_END_OF_STREAM);
     mJavaDecoder->Input(nullptr, bufferInfo, nullptr);
     return p;
   });
 }
 
 RefPtr<ShutdownPromise> RemoteDataDecoder::Shutdown() {
   LOG("");
   RefPtr<RemoteDataDecoder> self = this;
   return InvokeAsync(mTaskQueue, this, __func__,
                      &RemoteDataDecoder::ProcessShutdown);
 }
 
 RefPtr<ShutdownPromise> RemoteDataDecoder::ProcessShutdown() {
   AssertOnTaskQueue();
-  mShutdown = true;
+  SetState(State::SHUTDOWN);
   if (mJavaDecoder) {
     mJavaDecoder->Release();
     mJavaDecoder = nullptr;
   }
 
   if (mJavaCallbacks) {
     JavaCallbacksSupport::GetNative(mJavaCallbacks)->Cancel();
     JavaCallbacksSupport::DisposeNative(mJavaCallbacks);
@@ -649,107 +651,120 @@ RefPtr<MediaDataDecoder::DecodePromise> 
     BufferInfo::LocalRef bufferInfo;
     nsresult rv = BufferInfo::New(&bufferInfo);
     if (NS_FAILED(rv)) {
       return DecodePromise::CreateAndReject(
           MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
     }
     bufferInfo->Set(0, sample->Size(), sample->mTime.ToMicroseconds(), 0);
 
-    self->mDrainStatus = DrainStatus::DRAINABLE;
+    self->SetState(State::DRAINABLE);
     return self->mJavaDecoder->Input(bytes, bufferInfo,
                                      GetCryptoInfoFromSample(sample))
                ? self->mDecodePromise.Ensure(__func__)
                : DecodePromise::CreateAndReject(
                      MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
   });
 }
 
+void RemoteDataDecoder::UpdatePendingInputStatus(PendingOp aOp) {
+  AssertOnTaskQueue();
+  switch (aOp) {
+    case PendingOp::INCREASE:
+      mNumPendingInputs++;
+      break;
+    case PendingOp::DECREASE:
+      mNumPendingInputs--;
+      break;
+    case PendingOp::CLEAR:
+      mNumPendingInputs = 0;
+      break;
+  }
+}
+
 void RemoteDataDecoder::UpdateInputStatus(int64_t aTimestamp, bool aProcessed) {
   if (!mTaskQueue->IsCurrentThreadIn()) {
     nsresult rv = mTaskQueue->Dispatch(NewRunnableMethod<int64_t, bool>(
         "RemoteDataDecoder::UpdateInputStatus", this,
         &RemoteDataDecoder::UpdateInputStatus, aTimestamp, aProcessed));
     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
     Unused << rv;
     return;
   }
   AssertOnTaskQueue();
-  if (mShutdown) {
+  if (GetState() == State::SHUTDOWN) {
     return;
   }
 
   if (!aProcessed) {
-    mNumPendingInputs++;
-  } else if (mNumPendingInputs > 0) {
-    mNumPendingInputs--;
+    UpdatePendingInputStatus(PendingOp::INCREASE);
+  } else if (HasPendingInputs()) {
+    UpdatePendingInputStatus(PendingOp::DECREASE);
   }
 
-  if (mNumPendingInputs ==
-          0 ||  // Input has been processed, request the next one.
+  if (!HasPendingInputs() ||  // Input has been processed, request the next one.
       !mDecodedData.IsEmpty()) {  // Previous output arrived before Decode().
     ReturnDecodedData();
   }
 }
 
 void RemoteDataDecoder::UpdateOutputStatus(RefPtr<MediaData>&& aSample) {
   AssertOnTaskQueue();
-  if (mShutdown) {
+  if (GetState() == State::SHUTDOWN) {
     return;
   }
   if (IsUsefulData(aSample)) {
     mDecodedData.AppendElement(std::move(aSample));
   }
   ReturnDecodedData();
 }
 
 void RemoteDataDecoder::ReturnDecodedData() {
   AssertOnTaskQueue();
-  MOZ_ASSERT(!mShutdown);
+  MOZ_ASSERT(GetState() != State::SHUTDOWN);
 
   // We only want to clear mDecodedData when we have resolved the promises.
   if (!mDecodePromise.IsEmpty()) {
     mDecodePromise.Resolve(std::move(mDecodedData), __func__);
     mDecodedData = DecodedData();
   } else if (!mDrainPromise.IsEmpty() &&
-             (!mDecodedData.IsEmpty() ||
-              mDrainStatus == DrainStatus::DRAINED)) {
+             (!mDecodedData.IsEmpty() || GetState() == State::DRAINED)) {
     mDrainPromise.Resolve(std::move(mDecodedData), __func__);
     mDecodedData = DecodedData();
   }
 }
 
 void RemoteDataDecoder::DrainComplete() {
   if (!mTaskQueue->IsCurrentThreadIn()) {
     nsresult rv = mTaskQueue->Dispatch(
         NewRunnableMethod("RemoteDataDecoder::DrainComplete", this,
                           &RemoteDataDecoder::DrainComplete));
     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
     Unused << rv;
     return;
   }
   AssertOnTaskQueue();
-  if (mShutdown) {
+  if (GetState() == State::SHUTDOWN) {
     return;
   }
-  mDrainStatus = DrainStatus::DRAINED;
+  SetState(State::DRAINED);
   ReturnDecodedData();
   // Make decoder accept input again.
   mJavaDecoder->Flush();
 }
 
 void RemoteDataDecoder::Error(const MediaResult& aError) {
   if (!mTaskQueue->IsCurrentThreadIn()) {
     nsresult rv = mTaskQueue->Dispatch(NewRunnableMethod<MediaResult>(
         "RemoteDataDecoder::Error", this, &RemoteDataDecoder::Error, aError));
     MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
     Unused << rv;
     return;
   }
   AssertOnTaskQueue();
-  if (mShutdown) {
+  if (GetState() == State::SHUTDOWN) {
     return;
   }
   mDecodePromise.RejectIfExists(aError, __func__);
   mDrainPromise.RejectIfExists(aError, __func__);
 }
 
 }  // namespace mozilla
--- a/dom/media/platforms/android/RemoteDataDecoder.h
+++ b/dom/media/platforms/android/RemoteDataDecoder.h
@@ -46,38 +46,51 @@ class RemoteDataDecoder : public MediaDa
   RefPtr<ShutdownPromise> ProcessShutdown();
   void UpdateInputStatus(int64_t aTimestamp, bool aProcessed);
   void UpdateOutputStatus(RefPtr<MediaData>&& aSample);
   void ReturnDecodedData();
   void DrainComplete();
   void Error(const MediaResult& aError);
   void AssertOnTaskQueue() { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); }
 
+  enum class State { DRAINED, DRAINABLE, DRAINING, SHUTDOWN };
+  void SetState(State aState) {
+    AssertOnTaskQueue();
+    mState = aState;
+  }
+  State GetState() {
+    AssertOnTaskQueue();
+    return mState;
+  }
+
   // Whether the sample will be used.
   virtual bool IsUsefulData(const RefPtr<MediaData>& aSample) { return true; }
 
   MediaData::Type mType;
 
   nsAutoCString mMimeType;
   java::sdk::MediaFormat::GlobalRef mFormat;
 
   java::CodecProxy::GlobalRef mJavaDecoder;
   java::CodecProxy::NativeCallbacks::GlobalRef mJavaCallbacks;
   nsString mDrmStubId;
 
   RefPtr<TaskQueue> mTaskQueue;
-  // Only ever accessed on mTaskqueue.
-  bool mShutdown = false;
+
+ private:
+  enum class PendingOp { INCREASE, DECREASE, CLEAR };
+  void UpdatePendingInputStatus(PendingOp aOp);
+  size_t HasPendingInputs() {
+    AssertOnTaskQueue();
+    return mNumPendingInputs > 0;
+  }
+
+  // The following members must only be accessed on mTaskqueue.
   MozPromiseHolder<DecodePromise> mDecodePromise;
   MozPromiseHolder<DecodePromise> mDrainPromise;
-  enum class DrainStatus {
-    DRAINED,
-    DRAINABLE,
-    DRAINING,
-  };
-  DrainStatus mDrainStatus = DrainStatus::DRAINED;
   DecodedData mDecodedData;
+  State mState = State::DRAINED;
   size_t mNumPendingInputs;
 };
 
 }  // namespace mozilla
 
 #endif