Bug 1434822 part 2: mscom: Add a function to disconnect all remote clients associated with a given target. r=aklotz

Because Interceptors disable COM garbage collection to improve performance, they never receive Release calls from remote clients.
If the object can be shut down while clients still hold a reference, this function can be used to force COM to disconnect all remote connections (using CoDisconnectObject) and thus release the associated references to the Interceptor, its target and any objects associated with the HandlerProvider.
A HandlerProvider::DisconnectHandlerRemotes method also had to be added to allow HandlerProviders to disconnect clients for their own objects.

MozReview-Commit-ID: JaxEkOtrP1M

--HG--
extra : rebase_source : 2262af8fc3cb1aec8d9c8fc2762f3d61e188cb37
This commit is contained in:
James Teh 2018-02-19 16:08:57 +10:00
Родитель 5f27445039
Коммит 6fc08ce5ed
5 изменённых файлов: 58 добавлений и 0 удалений

Просмотреть файл

@ -23,6 +23,7 @@ struct HandlerProvider
virtual STDMETHODIMP GetHandlerPayloadSize(NotNull<IInterceptor*> aInterceptor, NotNull<DWORD*> aOutPayloadSize) = 0; virtual STDMETHODIMP GetHandlerPayloadSize(NotNull<IInterceptor*> aInterceptor, NotNull<DWORD*> aOutPayloadSize) = 0;
virtual STDMETHODIMP WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor, NotNull<IStream*> aStream) = 0; virtual STDMETHODIMP WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor, NotNull<IStream*> aStream) = 0;
virtual STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) = 0; virtual STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) = 0;
virtual STDMETHODIMP DisconnectHandlerRemotes() = 0;
}; };
struct IHandlerProvider : public IUnknown struct IHandlerProvider : public IUnknown

Просмотреть файл

@ -428,6 +428,7 @@ Interceptor::ReleaseMarshalData(IStream* pStm)
HRESULT HRESULT
Interceptor::DisconnectObject(DWORD dwReserved) Interceptor::DisconnectObject(DWORD dwReserved)
{ {
mEventSink->DisconnectHandlerRemotes();
return mStdMarshal->DisconnectObject(dwReserved); return mStdMarshal->DisconnectObject(dwReserved);
} }
@ -830,5 +831,30 @@ Interceptor::Release()
return WeakReferenceSupport::Release(); return WeakReferenceSupport::Release();
} }
/* static */ HRESULT
Interceptor::DisconnectRemotesForTarget(IUnknown* aTarget)
{
MOZ_ASSERT(aTarget);
detail::LiveSetAutoLock lock(GetLiveSet());
// It is not an error if the interceptor doesn't exist, so we return
// S_FALSE instead of an error in that case.
RefPtr<IWeakReference> existingWeak(Move(GetLiveSet().Get(aTarget)));
if (!existingWeak) {
return S_FALSE;
}
RefPtr<IWeakReferenceSource> existingStrong;
if (FAILED(existingWeak->ToStrongRef(getter_AddRefs(existingStrong)))) {
return S_FALSE;
}
// Since we now hold a strong ref on the interceptor, we may now release the
// lock.
lock.Unlock();
return ::CoDisconnectObject(existingStrong, 0);
}
} // namespace mscom } // namespace mscom
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -74,6 +74,26 @@ public:
static HRESULT Create(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink, static HRESULT Create(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink,
REFIID aInitialIid, void** aOutInterface); REFIID aInitialIid, void** aOutInterface);
/**
* Disconnect all remote clients for a given target.
* Because Interceptors disable COM garbage collection to improve
* performance, they never receive Release calls from remote clients. If
* the object can be shut down while clients still hold a reference, this
* function can be used to force COM to disconnect all remote connections
* (using CoDisconnectObject) and thus release the associated references to
* the Interceptor, its target and any objects associated with the
* HandlerProvider.
* Note that the specified target must be the same IUnknown pointer used to
* create the Interceptor. Where there is multiple inheritance, querying for
* IID_IUnknown and calling this function with that pointer alone will not
* disconnect remotes for all interfaces. If you expect that the same object
* may be fetched with different initial interfaces, you should call this
* function once for each possible IUnknown pointer.
* @return S_OK if there was an Interceptor for the given target,
* S_FALSE if there was not.
*/
static HRESULT DisconnectRemotesForTarget(IUnknown* aTarget);
// IUnknown // IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override; STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
STDMETHODIMP_(ULONG) AddRef() override; STDMETHODIMP_(ULONG) AddRef() override;

Просмотреть файл

@ -589,6 +589,16 @@ MainThreadHandoff::MarshalAs(REFIID aIid)
return mHandlerProvider->MarshalAs(aIid); return mHandlerProvider->MarshalAs(aIid);
} }
HRESULT
MainThreadHandoff::DisconnectHandlerRemotes()
{
if (!mHandlerProvider) {
return E_NOTIMPL;
}
return mHandlerProvider->DisconnectHandlerRemotes();
}
HRESULT HRESULT
MainThreadHandoff::OnWalkInterface(REFIID aIid, PVOID* aInterface, MainThreadHandoff::OnWalkInterface(REFIID aIid, PVOID* aInterface,
BOOL aIsInParam, BOOL aIsOutParam) BOOL aIsInParam, BOOL aIsOutParam)

Просмотреть файл

@ -66,6 +66,7 @@ public:
STDMETHODIMP WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor, STDMETHODIMP WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor,
NotNull<IStream*> aStream) override; NotNull<IStream*> aStream) override;
STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override; STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override;
STDMETHODIMP DisconnectHandlerRemotes() override;
// ICallFrameWalker // ICallFrameWalker
STDMETHODIMP OnWalkInterface(REFIID aIid, PVOID* aInterface, BOOL aIsInParam, STDMETHODIMP OnWalkInterface(REFIID aIid, PVOID* aInterface, BOOL aIsInParam,