Bug 1489317: Part 2 - Improvements to mscom::AgileReference; r=froydnj a=pascalc
authorAaron Klotz <aklotz@mozilla.com>
Wed, 12 Sep 2018 18:55:39 +0000
changeset 489847 d9b447bdf5e9
parent 489846 8344af6b480d
child 489848 e7f051b7650e
push id9801
push userncsoregi@mozilla.com
push dateFri, 14 Sep 2018 19:35:49 +0000
treeherdermozilla-beta@fe879d96c14b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj, pascalc
bugs1489317
milestone63.0
Bug 1489317: Part 2 - Improvements to mscom::AgileReference; r=froydnj a=pascalc This patch adds the definitions of the RefPtr constructor and operator=. It also refactors some stuff in AgileReference to make these objects easier to use. Since it's just a bunch of C++ goop, I figured that you'd be fine to review this. Let me know if you want to add a reviewer who is more familiar with the COM nuances. Depends on D5317 Differential Revision: https://phabricator.services.mozilla.com/D5318
ipc/mscom/AgileReference.cpp
ipc/mscom/AgileReference.h
--- a/ipc/mscom/AgileReference.cpp
+++ b/ipc/mscom/AgileReference.cpp
@@ -25,74 +25,120 @@ HRESULT WINAPI RoGetAgileReference(Agile
                                    REFIID riid, IUnknown* pUnk,
                                    IAgileReference** ppAgileReference);
 
 #endif // NTDDI_VERSION < NTDDI_WINBLUE
 
 namespace mozilla {
 namespace mscom {
 
+AgileReference::AgileReference()
+  : mIid()
+  , mGitCookie(0)
+{
+}
+
 AgileReference::AgileReference(REFIID aIid, IUnknown* aObject)
   : mIid(aIid)
   , mGitCookie(0)
 {
+  AssignInternal(aObject);
+}
+
+void
+AgileReference::Assign(REFIID aIid, IUnknown* aObject)
+{
+  Clear();
+  mIid = aIid;
+  AssignInternal(aObject);
+}
+
+void
+AgileReference::AssignInternal(IUnknown* aObject)
+{
+  // We expect mIid to already be set
+  DebugOnly<IID> zeroIid = {};
+  MOZ_ASSERT(mIid != zeroIid);
+
   /*
    * There are two possible techniques for creating agile references. Starting
    * with Windows 8.1, we may use the RoGetAgileReference API, which is faster.
    * If that API is not available, we fall back to using the Global Interface
    * Table.
    */
   static const DynamicallyLinkedFunctionPtr<decltype(&::RoGetAgileReference)>
     pRoGetAgileReference(L"ole32.dll", "RoGetAgileReference");
 
   MOZ_ASSERT(aObject);
 
   if (pRoGetAgileReference &&
-      SUCCEEDED(pRoGetAgileReference(AGILEREFERENCE_DEFAULT, aIid, aObject,
+      SUCCEEDED(pRoGetAgileReference(AGILEREFERENCE_DEFAULT, mIid, aObject,
                                      getter_AddRefs(mAgileRef)))) {
     return;
   }
 
   IGlobalInterfaceTable* git = ObtainGit();
   MOZ_ASSERT(git);
   if (!git) {
     return;
   }
 
-  DebugOnly<HRESULT> hr = git->RegisterInterfaceInGlobal(aObject, aIid,
+  DebugOnly<HRESULT> hr = git->RegisterInterfaceInGlobal(aObject, mIid,
                                                          &mGitCookie);
   MOZ_ASSERT(SUCCEEDED(hr));
 }
 
 AgileReference::AgileReference(AgileReference&& aOther)
   : mIid(aOther.mIid)
   , mAgileRef(std::move(aOther.mAgileRef))
   , mGitCookie(aOther.mGitCookie)
 {
   aOther.mGitCookie = 0;
 }
 
 AgileReference::~AgileReference()
 {
+  Clear();
+}
+
+void
+AgileReference::Clear()
+{
+  mIid = {};
+
   if (!mGitCookie) {
+    mAgileRef = nullptr;
     return;
   }
 
   IGlobalInterfaceTable* git = ObtainGit();
   MOZ_ASSERT(git);
   if (!git) {
     return;
   }
 
   DebugOnly<HRESULT> hr = git->RevokeInterfaceFromGlobal(mGitCookie);
   MOZ_ASSERT(SUCCEEDED(hr));
+  mGitCookie = 0;
+}
+
+AgileReference&
+AgileReference::operator=(AgileReference&& aOther)
+{
+  Clear();
+  mIid = aOther.mIid;
+  aOther.mIid = {};
+  mAgileRef = std::move(aOther.mAgileRef);
+  mGitCookie = aOther.mGitCookie;
+  aOther.mGitCookie = 0;
+  return *this;
 }
 
 HRESULT
-AgileReference::Resolve(REFIID aIid, void** aOutInterface)
+AgileReference::Resolve(REFIID aIid, void** aOutInterface) const
 {
   MOZ_ASSERT(aOutInterface);
   MOZ_ASSERT(mAgileRef || mGitCookie);
 
   if (!aOutInterface) {
     return E_INVALIDARG;
   }
 
@@ -125,17 +171,17 @@ AgileReference::Resolve(REFIID aIid, voi
     return S_OK;
   }
 
   // ...Whereas the GIT requires us to obtain the same interface that we
   // requested and then QI for the desired interface afterward.
   return originalInterface->QueryInterface(aIid, aOutInterface);
 }
 
-IGlobalInterfaceTable*
+/* static */ IGlobalInterfaceTable*
 AgileReference::ObtainGit()
 {
   // Internally to COM, the Global Interface Table is a singleton, therefore we
   // don't worry about holding onto this reference indefinitely.
   static IGlobalInterfaceTable* sGit = []() -> IGlobalInterfaceTable* {
     IGlobalInterfaceTable* result = nullptr;
     DebugOnly<HRESULT> hr =
       ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, nullptr,
--- a/ipc/mscom/AgileReference.h
+++ b/ipc/mscom/AgileReference.h
@@ -28,40 +28,91 @@ namespace mscom {
  *
  * // myAgileRef is passed to our main thread, which runs in a single-threaded
  * // apartment:
  *
  * RefPtr<IFoo> foo;
  * HRESULT hr = myAgileRef->Resolve(IID_IFoo, getter_AddRefs(foo));
  * // Now foo may be called from the main thread
  */
-class AgileReference
+class AgileReference final
 {
 public:
+  AgileReference();
+
+  template <typename InterfaceT>
+  explicit AgileReference(RefPtr<InterfaceT>& aObject)
+    : AgileReference(__uuidof(InterfaceT), aObject)
+  {
+  }
+
   AgileReference(REFIID aIid, IUnknown* aObject);
   AgileReference(AgileReference&& aOther);
 
   ~AgileReference();
 
   explicit operator bool() const
   {
     return mAgileRef || mGitCookie;
   }
 
-  HRESULT Resolve(REFIID aIid, void** aOutInterface);
+  template <typename T>
+  void Assign(const RefPtr<T>& aOther)
+  {
+    Assign(__uuidof(T), aOther);
+  }
+
+  template <typename T>
+  AgileReference& operator=(const RefPtr<T>& aOther)
+  {
+    Assign(aOther);
+    return *this;
+  }
+
+  HRESULT Resolve(REFIID aIid, void** aOutInterface) const;
 
   AgileReference(const AgileReference& aOther) = delete;
   AgileReference& operator=(const AgileReference& aOther) = delete;
-  AgileReference& operator=(AgileReference&& aOther) = delete;
+
+  AgileReference& operator=(AgileReference&& aOther);
+
+  AgileReference& operator=(decltype(nullptr))
+  {
+    Clear();
+    return *this;
+  }
+
+  void Clear();
 
 private:
-  IGlobalInterfaceTable* ObtainGit();
+  void Assign(REFIID aIid, IUnknown* aObject);
+  void AssignInternal(IUnknown* aObject);
+  static IGlobalInterfaceTable* ObtainGit();
 
 private:
   IID                     mIid;
   RefPtr<IAgileReference> mAgileRef;
   DWORD                   mGitCookie;
 };
 
 } // namespace mscom
 } // namespace mozilla
 
+template <typename T>
+RefPtr<T>::RefPtr(const mozilla::mscom::AgileReference& aAgileRef)
+  : mRawPtr(nullptr)
+{
+  (*this) = aAgileRef;
+}
+
+template <typename T>
+RefPtr<T>&
+RefPtr<T>::operator=(const mozilla::mscom::AgileReference& aAgileRef)
+{
+  void* newRawPtr;
+  if (FAILED(aAgileRef.Resolve(__uuidof(T), &newRawPtr))) {
+    newRawPtr = nullptr;
+  }
+  assign_assuming_AddRef(static_cast<T*>(newRawPtr));
+  return *this;
+}
+
 #endif // mozilla_mscom_AgileReference_h