Skip to content

Commit 6f56f52

Browse files
committed
Bug 1576925: Make AgileReference copyable; r=Jamie
Until now, AgileReference was not copyable because it could not handle the copying of global interface table cookies. By moving the cookie into its own reference counted object, we can enable copying on the AgileReference. Differential Revision: https://phabricator.services.mozilla.com/D43616 --HG-- extra : moz-landing-system : lando
1 parent 95ca91b commit 6f56f52

File tree

2 files changed

+142
-78
lines changed

2 files changed

+142
-78
lines changed

ipc/mscom/AgileReference.cpp

Lines changed: 100 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,89 @@ HRESULT WINAPI RoGetAgileReference(AgileReferenceOptions options, REFIID riid,
3434

3535
namespace mozilla {
3636
namespace mscom {
37+
namespace detail {
3738

38-
AgileReference::AgileReference() : mIid(), mGitCookie(0) {}
39+
GlobalInterfaceTableCookie::GlobalInterfaceTableCookie(IUnknown* aObject,
40+
REFIID aIid,
41+
HRESULT& aOutHResult)
42+
: mCookie(0) {
43+
IGlobalInterfaceTable* git = ObtainGit();
44+
MOZ_ASSERT(git);
45+
if (!git) {
46+
aOutHResult = E_POINTER;
47+
return;
48+
}
49+
50+
aOutHResult = git->RegisterInterfaceInGlobal(aObject, aIid, &mCookie);
51+
MOZ_ASSERT(SUCCEEDED(aOutHResult));
52+
}
53+
54+
GlobalInterfaceTableCookie::~GlobalInterfaceTableCookie() {
55+
IGlobalInterfaceTable* git = ObtainGit();
56+
MOZ_ASSERT(git);
57+
if (!git) {
58+
return;
59+
}
60+
61+
DebugOnly<HRESULT> hr = git->RevokeInterfaceFromGlobal(mCookie);
62+
#if defined(MOZILLA_INTERNAL_API)
63+
NS_WARNING_ASSERTION(
64+
SUCCEEDED(hr),
65+
nsPrintfCString("IGlobalInterfaceTable::RevokeInterfaceFromGlobal failed "
66+
"with HRESULT 0x%08lX",
67+
((HRESULT)hr))
68+
.get());
69+
#else
70+
MOZ_ASSERT(SUCCEEDED(hr));
71+
#endif // defined(MOZILLA_INTERNAL_API)
72+
mCookie = 0;
73+
}
74+
75+
HRESULT GlobalInterfaceTableCookie::GetInterface(REFIID aIid,
76+
void** aOutInterface) const {
77+
IGlobalInterfaceTable* git = ObtainGit();
78+
MOZ_ASSERT(git);
79+
if (!git) {
80+
return E_UNEXPECTED;
81+
}
82+
83+
MOZ_ASSERT(IsValid());
84+
return git->GetInterfaceFromGlobal(mCookie, aIid, aOutInterface);
85+
}
86+
87+
/* static */
88+
IGlobalInterfaceTable* GlobalInterfaceTableCookie::ObtainGit() {
89+
// Internally to COM, the Global Interface Table is a singleton, therefore we
90+
// don't worry about holding onto this reference indefinitely.
91+
static IGlobalInterfaceTable* sGit = []() -> IGlobalInterfaceTable* {
92+
IGlobalInterfaceTable* result = nullptr;
93+
DebugOnly<HRESULT> hr = ::CoCreateInstance(
94+
CLSID_StdGlobalInterfaceTable, nullptr, CLSCTX_INPROC_SERVER,
95+
IID_IGlobalInterfaceTable, reinterpret_cast<void**>(&result));
96+
MOZ_ASSERT(SUCCEEDED(hr));
97+
return result;
98+
}();
99+
100+
return sGit;
101+
}
102+
103+
} // namespace detail
104+
105+
AgileReference::AgileReference() : mIid(), mHResult(E_NOINTERFACE) {}
39106

40107
AgileReference::AgileReference(REFIID aIid, IUnknown* aObject)
41-
: mIid(aIid), mGitCookie(0) {
108+
: mIid(aIid), mHResult(E_UNEXPECTED) {
42109
AssignInternal(aObject);
43110
}
44111

112+
AgileReference::AgileReference(AgileReference&& aOther)
113+
: mIid(aOther.mIid),
114+
mAgileRef(std::move(aOther.mAgileRef)),
115+
mGitCookie(std::move(aOther.mGitCookie)),
116+
mHResult(aOther.mHResult) {
117+
aOther.mHResult = CO_E_RELEASED;
118+
}
119+
45120
void AgileReference::Assign(REFIID aIid, IUnknown* aObject) {
46121
Clear();
47122
mIid = aIid;
@@ -66,74 +141,49 @@ void AgileReference::AssignInternal(IUnknown* aObject) {
66141
MOZ_ASSERT(aObject);
67142

68143
if (pRoGetAgileReference &&
69-
SUCCEEDED(pRoGetAgileReference(AGILEREFERENCE_DEFAULT, mIid, aObject,
70-
getter_AddRefs(mAgileRef)))) {
71-
return;
72-
}
73-
74-
IGlobalInterfaceTable* git = ObtainGit();
75-
MOZ_ASSERT(git);
76-
if (!git) {
144+
SUCCEEDED(mHResult =
145+
pRoGetAgileReference(AGILEREFERENCE_DEFAULT, mIid, aObject,
146+
getter_AddRefs(mAgileRef)))) {
77147
return;
78148
}
79149

80-
DebugOnly<HRESULT> hr =
81-
git->RegisterInterfaceInGlobal(aObject, mIid, &mGitCookie);
82-
MOZ_ASSERT(SUCCEEDED(hr));
83-
}
84-
85-
AgileReference::AgileReference(AgileReference&& aOther)
86-
: mIid(aOther.mIid),
87-
mAgileRef(std::move(aOther.mAgileRef)),
88-
mGitCookie(aOther.mGitCookie) {
89-
aOther.mGitCookie = 0;
150+
mGitCookie = new detail::GlobalInterfaceTableCookie(aObject, mIid, mHResult);
151+
MOZ_ASSERT(mGitCookie->IsValid());
90152
}
91153

92154
AgileReference::~AgileReference() { Clear(); }
93155

94156
void AgileReference::Clear() {
95157
mIid = {};
158+
mAgileRef = nullptr;
159+
mGitCookie = nullptr;
160+
mHResult = E_NOINTERFACE;
161+
}
96162

97-
if (!mGitCookie) {
98-
mAgileRef = nullptr;
99-
return;
100-
}
101-
102-
IGlobalInterfaceTable* git = ObtainGit();
103-
MOZ_ASSERT(git);
104-
if (!git) {
105-
return;
106-
}
107-
108-
DebugOnly<HRESULT> hr = git->RevokeInterfaceFromGlobal(mGitCookie);
109-
#if defined(MOZILLA_INTERNAL_API)
110-
NS_WARNING_ASSERTION(
111-
SUCCEEDED(hr),
112-
nsPrintfCString("IGlobalInterfaceTable::RevokeInterfaceFromGlobal failed "
113-
"with HRESULT "
114-
"0x%08lX",
115-
((HRESULT)hr))
116-
.get());
117-
#else
118-
MOZ_ASSERT(SUCCEEDED(hr));
119-
#endif // defined(MOZILLA_INTERNAL_API)
120-
mGitCookie = 0;
163+
AgileReference& AgileReference::operator=(const AgileReference& aOther) {
164+
Clear();
165+
mIid = aOther.mIid;
166+
mAgileRef = aOther.mAgileRef;
167+
mGitCookie = aOther.mGitCookie;
168+
mHResult = aOther.mHResult;
169+
return *this;
121170
}
122171

123172
AgileReference& AgileReference::operator=(AgileReference&& aOther) {
124173
Clear();
125174
mIid = aOther.mIid;
126-
aOther.mIid = {};
127175
mAgileRef = std::move(aOther.mAgileRef);
128-
mGitCookie = aOther.mGitCookie;
129-
aOther.mGitCookie = 0;
176+
mGitCookie = std::move(aOther.mGitCookie);
177+
mHResult = aOther.mHResult;
178+
aOther.mHResult = CO_E_RELEASED;
130179
return *this;
131180
}
132181

133182
HRESULT
134183
AgileReference::Resolve(REFIID aIid, void** aOutInterface) const {
135184
MOZ_ASSERT(aOutInterface);
136-
MOZ_ASSERT(mAgileRef || mGitCookie);
185+
// This check is exclusive-OR; we should have one or the other, but not both
186+
MOZ_ASSERT((mAgileRef || mGitCookie) && !(mAgileRef && mGitCookie));
137187
MOZ_ASSERT(IsCOMInitializedOnCurrentThread());
138188

139189
if (!aOutInterface) {
@@ -151,15 +201,9 @@ AgileReference::Resolve(REFIID aIid, void** aOutInterface) const {
151201
return E_UNEXPECTED;
152202
}
153203

154-
IGlobalInterfaceTable* git = ObtainGit();
155-
MOZ_ASSERT(git);
156-
if (!git) {
157-
return E_UNEXPECTED;
158-
}
159-
160204
RefPtr<IUnknown> originalInterface;
161-
HRESULT hr = git->GetInterfaceFromGlobal(mGitCookie, mIid,
162-
getter_AddRefs(originalInterface));
205+
HRESULT hr =
206+
mGitCookie->GetInterface(mIid, getter_AddRefs(originalInterface));
163207
if (FAILED(hr)) {
164208
return hr;
165209
}
@@ -174,21 +218,5 @@ AgileReference::Resolve(REFIID aIid, void** aOutInterface) const {
174218
return originalInterface->QueryInterface(aIid, aOutInterface);
175219
}
176220

177-
/* static */
178-
IGlobalInterfaceTable* AgileReference::ObtainGit() {
179-
// Internally to COM, the Global Interface Table is a singleton, therefore we
180-
// don't worry about holding onto this reference indefinitely.
181-
static IGlobalInterfaceTable* sGit = []() -> IGlobalInterfaceTable* {
182-
IGlobalInterfaceTable* result = nullptr;
183-
DebugOnly<HRESULT> hr = ::CoCreateInstance(
184-
CLSID_StdGlobalInterfaceTable, nullptr, CLSCTX_INPROC_SERVER,
185-
IID_IGlobalInterfaceTable, reinterpret_cast<void**>(&result));
186-
MOZ_ASSERT(SUCCEEDED(hr));
187-
return result;
188-
}();
189-
190-
return sGit;
191-
}
192-
193221
} // namespace mscom
194222
} // namespace mozilla

ipc/mscom/AgileReference.h

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,44 @@
77
#ifndef mozilla_mscom_AgileReference_h
88
#define mozilla_mscom_AgileReference_h
99

10+
#include "mozilla/Attributes.h"
1011
#include "mozilla/RefPtr.h"
12+
#include "nsISupportsImpl.h"
1113

1214
#include <objidl.h>
1315

1416
namespace mozilla {
1517
namespace mscom {
18+
namespace detail {
19+
20+
class MOZ_HEAP_CLASS GlobalInterfaceTableCookie final {
21+
public:
22+
GlobalInterfaceTableCookie(IUnknown* aObject, REFIID aIid,
23+
HRESULT& aOutHResult);
24+
25+
bool IsValid() const { return !!mCookie; }
26+
HRESULT GetInterface(REFIID aIid, void** aOutInterface) const;
27+
28+
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GlobalInterfaceTableCookie)
29+
30+
GlobalInterfaceTableCookie(const GlobalInterfaceTableCookie&) = delete;
31+
GlobalInterfaceTableCookie(GlobalInterfaceTableCookie&&) = delete;
32+
33+
GlobalInterfaceTableCookie& operator=(const GlobalInterfaceTableCookie&) =
34+
delete;
35+
GlobalInterfaceTableCookie& operator=(GlobalInterfaceTableCookie&&) = delete;
36+
37+
private:
38+
~GlobalInterfaceTableCookie();
39+
40+
private:
41+
DWORD mCookie;
42+
43+
private:
44+
static IGlobalInterfaceTable* ObtainGit();
45+
};
46+
47+
} // namespace detail
1648

1749
/**
1850
* This class encapsulates an "agile reference." These are references that
@@ -42,11 +74,17 @@ class AgileReference final {
4274
: AgileReference(__uuidof(InterfaceT), aObject) {}
4375

4476
AgileReference(REFIID aIid, IUnknown* aObject);
77+
78+
AgileReference(const AgileReference& aOther) = default;
4579
AgileReference(AgileReference&& aOther);
4680

4781
~AgileReference();
4882

49-
explicit operator bool() const { return mAgileRef || mGitCookie; }
83+
explicit operator bool() const {
84+
return mAgileRef || (mGitCookie && mGitCookie->IsValid());
85+
}
86+
87+
HRESULT GetHResult() const { return mHResult; }
5088

5189
template <typename T>
5290
void Assign(const RefPtr<T>& aOther) {
@@ -61,9 +99,7 @@ class AgileReference final {
6199

62100
HRESULT Resolve(REFIID aIid, void** aOutInterface) const;
63101

64-
AgileReference(const AgileReference& aOther) = delete;
65-
AgileReference& operator=(const AgileReference& aOther) = delete;
66-
102+
AgileReference& operator=(const AgileReference& aOther);
67103
AgileReference& operator=(AgileReference&& aOther);
68104

69105
AgileReference& operator=(decltype(nullptr)) {
@@ -76,12 +112,12 @@ class AgileReference final {
76112
private:
77113
void Assign(REFIID aIid, IUnknown* aObject);
78114
void AssignInternal(IUnknown* aObject);
79-
static IGlobalInterfaceTable* ObtainGit();
80115

81116
private:
82117
IID mIid;
83118
RefPtr<IAgileReference> mAgileRef;
84-
DWORD mGitCookie;
119+
RefPtr<detail::GlobalInterfaceTableCookie> mGitCookie;
120+
HRESULT mHResult;
85121
};
86122

87123
} // namespace mscom

0 commit comments

Comments
 (0)