Bug 1426291 - Use DataMutex<T> to synchronize CDMCaps. r=jwwang draft
authorChris Pearce <cpearce@mozilla.com>
Wed, 20 Dec 2017 16:16:50 +1300
changeset 713317 16b0a9abd172d3ac3a93d7d69a2defcaaef0b380
parent 713316 42512e7b7a061bcf70a8e03ab2181c928d507a83
child 744323 8f5f71a563b4246308e673403b5b6c94ea1f3a1a
push id93630
push userbmo:cpearce@mozilla.com
push dateWed, 20 Dec 2017 08:38:24 +0000
reviewersjwwang
bugs1426291
milestone59.0a1
Bug 1426291 - Use DataMutex<T> to synchronize CDMCaps. r=jwwang MozReview-Commit-ID: NLnoItNZKP
dom/media/eme/CDMCaps.cpp
dom/media/eme/CDMCaps.h
dom/media/eme/CDMProxy.h
dom/media/eme/MediaKeySession.cpp
dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.cpp
dom/media/gmp/ChromiumCDMCallbackProxy.cpp
dom/media/gmp/ChromiumCDMProxy.cpp
dom/media/gmp/ChromiumCDMProxy.h
dom/media/platforms/agnostic/eme/SamplesWaitingForKey.cpp
--- a/dom/media/eme/CDMCaps.cpp
+++ b/dom/media/eme/CDMCaps.cpp
@@ -7,159 +7,130 @@
 #include "mozilla/CDMCaps.h"
 #include "mozilla/EMEUtils.h"
 #include "nsThreadUtils.h"
 #include "SamplesWaitingForKey.h"
 
 namespace mozilla {
 
 CDMCaps::CDMCaps()
-  : mMonitor("CDMCaps")
 {
 }
 
 CDMCaps::~CDMCaps()
 {
 }
 
-void
-CDMCaps::Lock()
-{
-  mMonitor.Lock();
-}
-
-void
-CDMCaps::Unlock()
-{
-  mMonitor.Unlock();
-}
-
-CDMCaps::AutoLock::AutoLock(CDMCaps& aInstance)
-  : mData(aInstance)
-{
-  mData.Lock();
-}
-
-CDMCaps::AutoLock::~AutoLock()
-{
-  mData.Unlock();
-}
-
 // Keys with MediaKeyStatus::Usable, MediaKeyStatus::Output_downscaled,
 // or MediaKeyStatus::Output_restricted status can be used by the CDM
 // to decrypt or decrypt-and-decode samples.
 static bool
 IsUsableStatus(dom::MediaKeyStatus aStatus)
 {
   return aStatus == dom::MediaKeyStatus::Usable ||
          aStatus == dom::MediaKeyStatus::Output_restricted ||
          aStatus == dom::MediaKeyStatus::Output_downscaled;
 }
 
 bool
-CDMCaps::AutoLock::IsKeyUsable(const CencKeyId& aKeyId)
+CDMCaps::IsKeyUsable(const CencKeyId& aKeyId)
 {
-  mData.mMonitor.AssertCurrentThreadOwns();
-  for (const KeyStatus& keyStatus : mData.mKeyStatuses) {
+  for (const KeyStatus& keyStatus : mKeyStatuses) {
     if (keyStatus.mId == aKeyId) {
       return IsUsableStatus(keyStatus.mStatus);
     }
   }
   return false;
 }
 
 bool
-CDMCaps::AutoLock::SetKeyStatus(const CencKeyId& aKeyId,
-                                const nsString& aSessionId,
-                                const dom::Optional<dom::MediaKeyStatus>& aStatus)
+CDMCaps::SetKeyStatus(const CencKeyId& aKeyId,
+                      const nsString& aSessionId,
+                      const dom::Optional<dom::MediaKeyStatus>& aStatus)
 {
-  mData.mMonitor.AssertCurrentThreadOwns();
-
   if (!aStatus.WasPassed()) {
     // Called from ForgetKeyStatus.
     // Return true if the element is found to notify key changes.
-    return mData.mKeyStatuses.RemoveElement(KeyStatus(aKeyId,
-                                                      aSessionId,
-                                                      dom::MediaKeyStatus::Internal_error));
+    return mKeyStatuses.RemoveElement(
+      KeyStatus(aKeyId, aSessionId, dom::MediaKeyStatus::Internal_error));
   }
 
   KeyStatus key(aKeyId, aSessionId, aStatus.Value());
-  auto index = mData.mKeyStatuses.IndexOf(key);
-  if (index != mData.mKeyStatuses.NoIndex) {
-    if (mData.mKeyStatuses[index].mStatus == aStatus.Value()) {
+  auto index = mKeyStatuses.IndexOf(key);
+  if (index != mKeyStatuses.NoIndex) {
+    if (mKeyStatuses[index].mStatus == aStatus.Value()) {
       // No change.
       return false;
     }
-    auto oldStatus = mData.mKeyStatuses[index].mStatus;
-    mData.mKeyStatuses[index].mStatus = aStatus.Value();
+    auto oldStatus = mKeyStatuses[index].mStatus;
+    mKeyStatuses[index].mStatus = aStatus.Value();
     // The old key status was one for which we can decrypt media. We don't
     // need to do the "notify usable" step below, as it should be impossible
     // for us to have anything waiting on this key to become usable, since it
     // was already usable.
     if (IsUsableStatus(oldStatus)) {
       return true;
     }
   } else {
-    mData.mKeyStatuses.AppendElement(key);
+    mKeyStatuses.AppendElement(key);
   }
 
   // Only call NotifyUsable() for a key when we are going from non-usable
   // to usable state.
   if (!IsUsableStatus(aStatus.Value())) {
     return true;
   }
 
-  auto& waiters = mData.mWaitForKeys;
+  auto& waiters = mWaitForKeys;
   size_t i = 0;
   while (i < waiters.Length()) {
     auto& w = waiters[i];
     if (w.mKeyId == aKeyId) {
       w.mListener->NotifyUsable(aKeyId);
       waiters.RemoveElementAt(i);
     } else {
       i++;
     }
   }
   return true;
 }
 
 void
-CDMCaps::AutoLock::NotifyWhenKeyIdUsable(const CencKeyId& aKey,
-                                         SamplesWaitingForKey* aListener)
+CDMCaps::NotifyWhenKeyIdUsable(const CencKeyId& aKey,
+                               SamplesWaitingForKey* aListener)
 {
-  mData.mMonitor.AssertCurrentThreadOwns();
   MOZ_ASSERT(!IsKeyUsable(aKey));
   MOZ_ASSERT(aListener);
-  mData.mWaitForKeys.AppendElement(WaitForKeys(aKey, aListener));
+  mWaitForKeys.AppendElement(WaitForKeys(aKey, aListener));
 }
 
 void
-CDMCaps::AutoLock::GetKeyStatusesForSession(const nsAString& aSessionId,
-                                            nsTArray<KeyStatus>& aOutKeyStatuses)
+CDMCaps::GetKeyStatusesForSession(const nsAString& aSessionId,
+                                  nsTArray<KeyStatus>& aOutKeyStatuses)
 {
-  for (const KeyStatus& keyStatus : mData.mKeyStatuses) {
+  for (const KeyStatus& keyStatus : mKeyStatuses) {
     if (keyStatus.mSessionId.Equals(aSessionId)) {
       aOutKeyStatuses.AppendElement(keyStatus);
     }
   }
 }
 
 void
-CDMCaps::AutoLock::GetSessionIdsForKeyId(const CencKeyId& aKeyId,
-                                         nsTArray<nsCString>& aOutSessionIds)
+CDMCaps::GetSessionIdsForKeyId(const CencKeyId& aKeyId,
+                               nsTArray<nsCString>& aOutSessionIds)
 {
-  for (const KeyStatus& keyStatus : mData.mKeyStatuses) {
+  for (const KeyStatus& keyStatus : mKeyStatuses) {
     if (keyStatus.mId == aKeyId) {
       aOutSessionIds.AppendElement(NS_ConvertUTF16toUTF8(keyStatus.mSessionId));
     }
   }
 }
 
 bool
-CDMCaps::AutoLock::RemoveKeysForSession(const nsString& aSessionId)
+CDMCaps::RemoveKeysForSession(const nsString& aSessionId)
 {
   bool changed = false;
   nsTArray<KeyStatus> statuses;
   GetKeyStatusesForSession(aSessionId, statuses);
   for (const KeyStatus& status : statuses) {
     changed |= SetKeyStatus(status.mId,
                             aSessionId,
                             dom::Optional<dom::MediaKeyStatus>());
--- a/dom/media/eme/CDMCaps.h
+++ b/dom/media/eme/CDMCaps.h
@@ -44,65 +44,50 @@ public:
              mSessionId == aOther.mSessionId;
     };
 
     CencKeyId mId;
     nsString mSessionId;
     dom::MediaKeyStatus mStatus;
   };
 
-  // Locks the CDMCaps. It must be locked to access its shared state.
-  // Threadsafe when locked.
-  class MOZ_STACK_CLASS AutoLock {
-  public:
-    explicit AutoLock(CDMCaps& aKeyCaps);
-    ~AutoLock();
+  bool IsKeyUsable(const CencKeyId& aKeyId);
 
-    bool IsKeyUsable(const CencKeyId& aKeyId);
+  // Returns true if key status changed,
+  // i.e. the key status changed from usable to expired.
+  bool SetKeyStatus(const CencKeyId& aKeyId,
+                    const nsString& aSessionId,
+                    const dom::Optional<dom::MediaKeyStatus>& aStatus);
 
-    // Returns true if key status changed,
-    // i.e. the key status changed from usable to expired.
-    bool SetKeyStatus(const CencKeyId& aKeyId,
-                      const nsString& aSessionId,
-                      const dom::Optional<dom::MediaKeyStatus>& aStatus);
+  void GetKeyStatusesForSession(const nsAString& aSessionId,
+                                nsTArray<KeyStatus>& aOutKeyStatuses);
 
-    void GetKeyStatusesForSession(const nsAString& aSessionId,
-                                  nsTArray<KeyStatus>& aOutKeyStatuses);
-
-    void GetSessionIdsForKeyId(const CencKeyId& aKeyId,
-                               nsTArray<nsCString>& aOutSessionIds);
+  void GetSessionIdsForKeyId(const CencKeyId& aKeyId,
+                             nsTArray<nsCString>& aOutSessionIds);
 
-    // Ensures all keys for a session are marked as 'unknown', i.e. removed.
-    // Returns true if a key status was changed.
-    bool RemoveKeysForSession(const nsString& aSessionId);
+  // Ensures all keys for a session are marked as 'unknown', i.e. removed.
+  // Returns true if a key status was changed.
+  bool RemoveKeysForSession(const nsString& aSessionId);
 
-    // Notifies the SamplesWaitingForKey when key become usable.
-    void NotifyWhenKeyIdUsable(const CencKeyId& aKey,
-                               SamplesWaitingForKey* aSamplesWaiting);
-  private:
-    // Not taking a strong ref, since this should be allocated on the stack.
-    CDMCaps& mData;
-  };
+  // Notifies the SamplesWaitingForKey when key become usable.
+  void NotifyWhenKeyIdUsable(const CencKeyId& aKey,
+                             SamplesWaitingForKey* aSamplesWaiting);
 
 private:
-  void Lock();
-  void Unlock();
 
   struct WaitForKeys {
     WaitForKeys(const CencKeyId& aKeyId,
                 SamplesWaitingForKey* aListener)
       : mKeyId(aKeyId)
       , mListener(aListener)
     {}
     CencKeyId mKeyId;
     RefPtr<SamplesWaitingForKey> mListener;
   };
 
-  Monitor mMonitor;
-
   nsTArray<KeyStatus> mKeyStatuses;
 
   nsTArray<WaitForKeys> mWaitForKeys;
 
   // It is not safe to copy this object.
   CDMCaps(const CDMCaps&) = delete;
   CDMCaps& operator=(const CDMCaps&) = delete;
 };
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef CDMProxy_h_
 #define CDMProxy_h_
 
 #include "mozilla/CDMCaps.h"
+#include "mozilla/DataMutex.h"
 #include "mozilla/MozPromise.h"
 
 #include "mozilla/dom/MediaKeyMessageEvent.h"
 #include "mozilla/dom/MediaKeys.h"
 
 #include "nsIThread.h"
 
 namespace mozilla {
@@ -87,16 +88,17 @@ public:
   // Main thread only.
   CDMProxy(dom::MediaKeys* aKeys,
            const nsAString& aKeySystem,
            bool aDistinctiveIdentifierRequired,
            bool aPersistentStateRequired,
            nsIEventTarget* aMainThread)
     : mKeys(aKeys)
     , mKeySystem(aKeySystem)
+    , mCapabilites("CDMProxy::mCDMCaps")
     , mDistinctiveIdentifierRequired(aDistinctiveIdentifierRequired)
     , mPersistentStateRequired(aPersistentStateRequired)
     , mMainThread(aMainThread)
   {}
 
   // Main thread only.
   // Loads the CDM corresponding to mKeySystem.
   // Calls MediaKeys::OnCDMCreated() when the CDM is created.
@@ -212,17 +214,17 @@ public:
 
   // Resolves promise with "undefined".
   // Can be called from any thread.
   virtual void ResolvePromise(PromiseId aId) = 0;
 
   // Threadsafe.
   virtual const nsString& KeySystem() const = 0;
 
-  virtual  CDMCaps& Capabilites() = 0;
+  virtual DataMutex<CDMCaps>& Capabilites() = 0;
 
   // Main thread only.
   virtual void OnKeyStatusesChange(const nsAString& aSessionId) = 0;
 
   virtual void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                                      nsTArray<nsCString>& aSessionIds) = 0;
 
   // Main thread only.
@@ -278,17 +280,17 @@ protected:
   const nsString mKeySystem;
 
   // Onwer specified thread. e.g. Gecko Media Plugin thread.
   // All interactions with the out-of-process EME plugin must come from this thread.
   RefPtr<nsIThread> mOwnerThread;
 
   nsCString mNodeId;
 
-  CDMCaps mCapabilites;
+  DataMutex<CDMCaps> mCapabilites;
 
   const bool mDistinctiveIdentifierRequired;
   const bool mPersistentStateRequired;
 
   // The main thread associated with the root document.
   const nsCOMPtr<nsIEventTarget> mMainThread;
 };
 
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -134,18 +134,18 @@ MediaKeySession::UpdateKeyStatusMap()
 {
   MOZ_ASSERT(!IsClosed());
   if (!mKeys->GetCDMProxy()) {
     return;
   }
 
   nsTArray<CDMCaps::KeyStatus> keyStatuses;
   {
-    CDMCaps::AutoLock caps(mKeys->GetCDMProxy()->Capabilites());
-    caps.GetKeyStatusesForSession(mSessionId, keyStatuses);
+    auto caps = mKeys->GetCDMProxy()->Capabilites().Lock();
+    caps->GetKeyStatusesForSession(mSessionId, keyStatuses);
   }
 
   mKeyStatusMap->Update(keyStatuses);
 
   if (EME_LOG_ENABLED()) {
     nsAutoCString message(
       nsPrintfCString("MediaKeySession[%p,'%s'] key statuses change {",
                       this, NS_ConvertUTF16toUTF8(mSessionId).get()));
--- a/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.cpp
+++ b/dom/media/eme/mediadrm/MediaDrmCDMCallbackProxy.cpp
@@ -75,18 +75,19 @@ MediaDrmCDMCallbackProxy::ExpirationChan
 }
 
 void
 MediaDrmCDMCallbackProxy::SessionClosed(const nsCString& aSessionId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   bool keyStatusesChange = false;
   {
-    CDMCaps::AutoLock caps(mProxy->Capabilites());
-    keyStatusesChange = caps.RemoveKeysForSession(NS_ConvertUTF8toUTF16(aSessionId));
+    auto caps = mProxy->Capabilites().Lock();
+    keyStatusesChange =
+      caps->RemoveKeysForSession(NS_ConvertUTF8toUTF16(aSessionId));
   }
   if (keyStatusesChange) {
     mProxy->OnKeyStatusesChange(NS_ConvertUTF8toUTF16(aSessionId));
   }
   mProxy->OnSessionClosed(NS_ConvertUTF8toUTF16(aSessionId));
 }
 
 void
@@ -111,22 +112,21 @@ MediaDrmCDMCallbackProxy::BatchedKeyStat
 }
 
 void
 MediaDrmCDMCallbackProxy::BatchedKeyStatusChangedInternal(const nsCString& aSessionId,
                                                           const nsTArray<CDMKeyInfo>& aKeyInfos)
 {
   bool keyStatusesChange = false;
   {
-    CDMCaps::AutoLock caps(mProxy->Capabilites());
+    auto caps = mProxy->Capabilites().Lock();
     for (size_t i = 0; i < aKeyInfos.Length(); i++) {
-      keyStatusesChange |=
-        caps.SetKeyStatus(aKeyInfos[i].mKeyId,
-                          NS_ConvertUTF8toUTF16(aSessionId),
-                          aKeyInfos[i].mStatus);
+      keyStatusesChange |= caps->SetKeyStatus(aKeyInfos[i].mKeyId,
+                                              NS_ConvertUTF8toUTF16(aSessionId),
+                                              aKeyInfos[i].mStatus);
     }
   }
   if (keyStatusesChange) {
     mProxy->OnKeyStatusesChange(NS_ConvertUTF8toUTF16(aSessionId));
   }
 }
 
 void
--- a/dom/media/gmp/ChromiumCDMCallbackProxy.cpp
+++ b/dom/media/gmp/ChromiumCDMCallbackProxy.cpp
@@ -127,23 +127,23 @@ ChromiumCDMCallbackProxy::ResolvePromise
 }
 
 void
 ChromiumCDMCallbackProxy::SessionKeysChange(const nsCString& aSessionId,
                                             nsTArray<mozilla::gmp::CDMKeyInformation> && aKeysInfo)
 {
   bool keyStatusesChange = false;
   {
-    CDMCaps::AutoLock caps(mProxy->Capabilites());
+    auto caps = mProxy->Capabilites().Lock();
     for (const auto& keyInfo : aKeysInfo) {
       keyStatusesChange |=
-        caps.SetKeyStatus(keyInfo.mKeyId(),
-                          NS_ConvertUTF8toUTF16(aSessionId),
-                          dom::Optional<dom::MediaKeyStatus>(
-                            ToDOMMediaKeyStatus(keyInfo.mStatus())));
+        caps->SetKeyStatus(keyInfo.mKeyId(),
+                           NS_ConvertUTF8toUTF16(aSessionId),
+                           dom::Optional<dom::MediaKeyStatus>(
+                             ToDOMMediaKeyStatus(keyInfo.mStatus())));
     }
   }
   if (keyStatusesChange) {
     DispatchToMainThread("ChromiumCDMProxy::OnKeyStatusesChange",
                          &ChromiumCDMProxy::OnKeyStatusesChange,
                          NS_ConvertUTF8toUTF16(aSessionId));
   }
 }
--- a/dom/media/gmp/ChromiumCDMProxy.cpp
+++ b/dom/media/gmp/ChromiumCDMProxy.cpp
@@ -523,18 +523,18 @@ ChromiumCDMProxy::OnExpirationChange(con
 
 void
 ChromiumCDMProxy::OnSessionClosed(const nsAString& aSessionId)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   bool keyStatusesChange = false;
   {
-    CDMCaps::AutoLock caps(Capabilites());
-    keyStatusesChange = caps.RemoveKeysForSession(nsString(aSessionId));
+    auto caps = Capabilites().Lock();
+    keyStatusesChange = caps->RemoveKeysForSession(nsString(aSessionId));
   }
   if (keyStatusesChange) {
     OnKeyStatusesChange(aSessionId);
   }
   if (mKeys.IsNull()) {
     return;
   }
   RefPtr<dom::MediaKeySession> session(mKeys->GetSession(aSessionId));
@@ -577,17 +577,17 @@ ChromiumCDMProxy::OnRejectPromise(uint32
 }
 
 const nsString&
 ChromiumCDMProxy::KeySystem() const
 {
   return mKeySystem;
 }
 
-CDMCaps&
+DataMutex<CDMCaps>&
 ChromiumCDMProxy::Capabilites()
 {
   return mCapabilites;
 }
 
 RefPtr<DecryptPromise>
 ChromiumCDMProxy::Decrypt(MediaRawData* aSample)
 {
@@ -600,18 +600,18 @@ ChromiumCDMProxy::Decrypt(MediaRawData* 
   return InvokeAsync(
     mGMPThread, __func__, [cdm, sample]() { return cdm->Decrypt(sample); });
 }
 
 void
 ChromiumCDMProxy::GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                                         nsTArray<nsCString>& aSessionIds)
 {
-  CDMCaps::AutoLock caps(Capabilites());
-  caps.GetSessionIdsForKeyId(aKeyId, aSessionIds);
+  auto caps = Capabilites().Lock();
+  caps->GetSessionIdsForKeyId(aKeyId, aSessionIds);
 }
 
 void
 ChromiumCDMProxy::GetStatusForPolicy(PromiseId aPromiseId,
                                      const nsAString& aMinHdcpVersion)
 {
   MOZ_ASSERT(NS_IsMainThread());
   EME_LOG("ChromiumCDMProxy::GetStatusForPolicy(pid=%u) minHdcpVersion=%s",
--- a/dom/media/gmp/ChromiumCDMProxy.h
+++ b/dom/media/gmp/ChromiumCDMProxy.h
@@ -93,17 +93,17 @@ public:
   void RejectPromise(PromiseId aId,
                      nsresult aExceptionCode,
                      const nsCString& aReason) override;
 
   void ResolvePromise(PromiseId aId) override;
 
   const nsString& KeySystem() const override;
 
-  CDMCaps& Capabilites() override;
+  DataMutex<CDMCaps>& Capabilites() override;
 
   void OnKeyStatusesChange(const nsAString& aSessionId) override;
 
   void GetSessionIdsForKeyId(const nsTArray<uint8_t>& aKeyId,
                              nsTArray<nsCString>& aSessionIds) override;
 
   void GetStatusForPolicy(PromiseId aPromiseId,
                           const nsAString& aMinHdcpVersion) override;
--- a/dom/media/platforms/agnostic/eme/SamplesWaitingForKey.cpp
+++ b/dom/media/platforms/agnostic/eme/SamplesWaitingForKey.cpp
@@ -29,32 +29,32 @@ SamplesWaitingForKey::~SamplesWaitingFor
 }
 
 RefPtr<SamplesWaitingForKey::WaitForKeyPromise>
 SamplesWaitingForKey::WaitIfKeyNotUsable(MediaRawData* aSample)
 {
   if (!aSample || !aSample->mCrypto.mValid || !mProxy) {
     return WaitForKeyPromise::CreateAndResolve(aSample, __func__);
   }
-  CDMCaps::AutoLock caps(mProxy->Capabilites());
+  auto caps = mProxy->Capabilites().Lock();
   const auto& keyid = aSample->mCrypto.mKeyId;
-  if (caps.IsKeyUsable(keyid)) {
+  if (caps->IsKeyUsable(keyid)) {
     return WaitForKeyPromise::CreateAndResolve(aSample, __func__);
   }
   SampleEntry entry;
   entry.mSample = aSample;
   RefPtr<WaitForKeyPromise> p = entry.mPromise.Ensure(__func__);
   {
     MutexAutoLock lock(mMutex);
     mSamples.AppendElement(Move(entry));
   }
   if (mOnWaitingForKeyEvent) {
     mOnWaitingForKeyEvent->Notify(mType);
   }
-  caps.NotifyWhenKeyIdUsable(aSample->mCrypto.mKeyId, this);
+  caps->NotifyWhenKeyIdUsable(aSample->mCrypto.mKeyId, this);
   return p;
 }
 
 void
 SamplesWaitingForKey::NotifyUsable(const CencKeyId& aKeyId)
 {
   MutexAutoLock lock(mMutex);
   size_t i = 0;