1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_mscom_AsyncInvoker_h
8 #define mozilla_mscom_AsyncInvoker_h
15 #include "mozilla/Assertions.h"
16 #include "mozilla/Attributes.h"
17 #include "mozilla/DebugOnly.h"
18 #include "mozilla/Maybe.h"
19 #include "mozilla/Mutex.h"
20 #include "mozilla/mscom/Aggregation.h"
21 #include "mozilla/mscom/Utils.h"
22 #include "nsISerialEventTarget.h"
23 #include "nsISupportsImpl.h"
24 #include "nsThreadUtils.h"
30 template <typename AsyncInterface
>
31 class ForgettableAsyncCall
: public ISynchronize
{
33 explicit ForgettableAsyncCall(ICallFactory
* aCallFactory
)
34 : mRefCnt(0), mAsyncCall(nullptr) {
35 StabilizedRefCount
<Atomic
<ULONG
>> stabilizer(mRefCnt
);
38 aCallFactory
->CreateCall(__uuidof(AsyncInterface
), this, IID_IUnknown
,
39 getter_AddRefs(mInnerUnk
));
44 hr
= mInnerUnk
->QueryInterface(__uuidof(AsyncInterface
),
45 reinterpret_cast<void**>(&mAsyncCall
));
47 // Don't hang onto a ref. Because mAsyncCall is aggregated, its refcount
48 // is this->mRefCnt, so we'd create a cycle!
49 mAsyncCall
->Release();
53 AsyncInterface
* GetInterface() const { return mAsyncCall
; }
56 STDMETHODIMP
QueryInterface(REFIID aIid
, void** aOutInterface
) final
{
57 if (aIid
== IID_ISynchronize
|| aIid
== IID_IUnknown
) {
58 RefPtr
<ISynchronize
> ptr(this);
59 ptr
.forget(aOutInterface
);
63 return mInnerUnk
->QueryInterface(aIid
, aOutInterface
);
66 STDMETHODIMP_(ULONG
) AddRef() final
{
67 ULONG result
= ++mRefCnt
;
68 NS_LOG_ADDREF(this, result
, "ForgettableAsyncCall", sizeof(*this));
72 STDMETHODIMP_(ULONG
) Release() final
{
73 ULONG result
= --mRefCnt
;
74 NS_LOG_RELEASE(this, result
, "ForgettableAsyncCall");
82 STDMETHODIMP
Wait(DWORD aFlags
, DWORD aTimeoutMilliseconds
) override
{
86 STDMETHODIMP
Signal() override
{
87 // Even though this function is a no-op, we must return S_OK as opposed to
88 // E_NOTIMPL or else COM will consider the async call to have failed.
92 STDMETHODIMP
Reset() override
{
93 // Even though this function is a no-op, we must return S_OK as opposed to
94 // E_NOTIMPL or else COM will consider the async call to have failed.
99 virtual ~ForgettableAsyncCall() = default;
102 Atomic
<ULONG
> mRefCnt
;
103 RefPtr
<IUnknown
> mInnerUnk
;
104 AsyncInterface
* mAsyncCall
; // weak reference
107 template <typename AsyncInterface
>
108 class WaitableAsyncCall
: public ForgettableAsyncCall
<AsyncInterface
> {
110 explicit WaitableAsyncCall(ICallFactory
* aCallFactory
)
111 : ForgettableAsyncCall
<AsyncInterface
>(aCallFactory
),
112 mEvent(::CreateEventW(nullptr, FALSE
, FALSE
, nullptr)) {}
114 STDMETHODIMP
Wait(DWORD aFlags
, DWORD aTimeoutMilliseconds
) override
{
115 const DWORD waitStart
=
116 aTimeoutMilliseconds
== INFINITE
? 0 : ::GetTickCount();
117 DWORD flags
= aFlags
;
118 if (XRE_IsContentProcess() && NS_IsMainThread()) {
119 flags
|= COWAIT_ALERTABLE
;
128 if (aTimeoutMilliseconds
!= INFINITE
) {
129 elapsed
= ::GetTickCount() - waitStart
;
131 if (elapsed
>= aTimeoutMilliseconds
) {
132 return RPC_S_CALLPENDING
;
135 ::SetLastError(ERROR_SUCCESS
);
137 hr
= ::CoWaitForMultipleHandles(flags
, aTimeoutMilliseconds
- elapsed
, 1,
138 &mEvent
, &signaledIdx
);
139 if (hr
== RPC_S_CALLPENDING
|| FAILED(hr
)) {
143 if (hr
== S_OK
&& signaledIdx
== 0) {
149 STDMETHODIMP
Signal() override
{
150 if (!::SetEvent(mEvent
)) {
151 return HRESULT_FROM_WIN32(::GetLastError());
157 ~WaitableAsyncCall() {
159 ::CloseHandle(mEvent
);
167 template <typename AsyncInterface
>
168 class EventDrivenAsyncCall
: public ForgettableAsyncCall
<AsyncInterface
> {
170 explicit EventDrivenAsyncCall(ICallFactory
* aCallFactory
)
171 : ForgettableAsyncCall
<AsyncInterface
>(aCallFactory
) {}
173 bool HasCompletionRunnable() const { return !!mCompletionRunnable
; }
175 void ClearCompletionRunnable() { mCompletionRunnable
= nullptr; }
177 void SetCompletionRunnable(already_AddRefed
<nsIRunnable
> aRunnable
) {
178 nsCOMPtr
<nsIRunnable
> innerRunnable(aRunnable
);
179 MOZ_ASSERT(!!innerRunnable
);
180 if (!innerRunnable
) {
184 // We need to retain a ref to ourselves to outlive the AsyncInvoker
185 // such that our callback can execute.
186 RefPtr
<EventDrivenAsyncCall
<AsyncInterface
>> self(this);
188 mCompletionRunnable
= NS_NewRunnableFunction(
189 "EventDrivenAsyncCall outer completion Runnable",
190 [innerRunnable
= std::move(innerRunnable
), self
= std::move(self
)]() {
191 innerRunnable
->Run();
195 void SetEventTarget(nsISerialEventTarget
* aTarget
) { mEventTarget
= aTarget
; }
197 STDMETHODIMP
Signal() override
{
198 MOZ_ASSERT(!!mCompletionRunnable
);
199 if (!mCompletionRunnable
) {
203 nsCOMPtr
<nsISerialEventTarget
> eventTarget(mEventTarget
.forget());
205 eventTarget
= GetMainThreadSerialEventTarget();
208 DebugOnly
<nsresult
> rv
=
209 eventTarget
->Dispatch(mCompletionRunnable
.forget(), NS_DISPATCH_NORMAL
);
210 MOZ_ASSERT(NS_SUCCEEDED(rv
));
215 nsCOMPtr
<nsIRunnable
> mCompletionRunnable
;
216 nsCOMPtr
<nsISerialEventTarget
> mEventTarget
;
219 template <typename AsyncInterface
>
220 class FireAndForgetInvoker
{
222 void OnBeginInvoke() {}
223 void OnSyncInvoke(HRESULT aHr
) {}
224 void OnAsyncInvokeFailed() {}
226 typedef ForgettableAsyncCall
<AsyncInterface
> AsyncCallType
;
228 RefPtr
<ForgettableAsyncCall
<AsyncInterface
>> mAsyncCall
;
231 template <typename AsyncInterface
>
232 class WaitableInvoker
{
234 HRESULT
Wait(DWORD aTimeout
= INFINITE
) const {
236 // Nothing to wait for
240 return mAsyncCall
->Wait(0, aTimeout
);
244 void OnBeginInvoke() {}
245 void OnSyncInvoke(HRESULT aHr
) {}
246 void OnAsyncInvokeFailed() {}
248 typedef WaitableAsyncCall
<AsyncInterface
> AsyncCallType
;
250 RefPtr
<WaitableAsyncCall
<AsyncInterface
>> mAsyncCall
;
253 template <typename AsyncInterface
>
254 class EventDrivenInvoker
{
256 void SetCompletionRunnable(already_AddRefed
<nsIRunnable
> aRunnable
) {
258 mAsyncCall
->SetCompletionRunnable(std::move(aRunnable
));
262 mCompletionRunnable
= aRunnable
;
265 void SetAsyncEventTarget(nsISerialEventTarget
* aTarget
) {
267 mAsyncCall
->SetEventTarget(aTarget
);
272 void OnBeginInvoke() {
274 mCompletionRunnable
||
275 (mAsyncCall
&& mAsyncCall
->HasCompletionRunnable()),
276 "You should have called SetCompletionRunnable before invoking!");
279 void OnSyncInvoke(HRESULT aHr
) {
280 nsCOMPtr
<nsIRunnable
> completionRunnable(mCompletionRunnable
.forget());
285 completionRunnable
->Run();
288 void OnAsyncInvokeFailed() {
289 MOZ_ASSERT(!!mAsyncCall
);
290 mAsyncCall
->ClearCompletionRunnable();
293 typedef EventDrivenAsyncCall
<AsyncInterface
> AsyncCallType
;
295 RefPtr
<EventDrivenAsyncCall
<AsyncInterface
>> mAsyncCall
;
296 nsCOMPtr
<nsIRunnable
> mCompletionRunnable
;
299 } // namespace detail
302 * This class is intended for "fire-and-forget" asynchronous invocations of COM
303 * interfaces. This requires that an interface be annotated with the
304 * |async_uuid| attribute in midl. We also require that there be no outparams
305 * in the desired asynchronous interface (otherwise that would break the
306 * desired "fire-and-forget" semantics).
308 * For example, let us suppose we have some IDL as such:
309 * [object, uuid(...), async_uuid(...)]
310 * interface IFoo : IUnknown
312 * HRESULT Bar([in] long baz);
315 * Then, given an IFoo, we may construct an AsyncInvoker<IFoo, AsyncIFoo>:
318 * AsyncInvoker<IFoo, AsyncIFoo> myInvoker(foo);
319 * HRESULT hr = myInvoker.Invoke(&IFoo::Bar, &AsyncIFoo::Begin_Bar, 7);
321 * Alternatively you may use the ASYNC_INVOKER_FOR and ASYNC_INVOKE macros,
322 * which automatically deduce the name of the asynchronous interface from the
323 * name of the synchronous interface:
325 * ASYNC_INVOKER_FOR(IFoo) myInvoker(foo);
326 * HRESULT hr = ASYNC_INVOKE(myInvoker, Bar, 7);
328 * This class may also be used when a synchronous COM call must be made that
329 * might reenter the content process. In this case, use the WaitableAsyncInvoker
330 * variant, or the WAITABLE_ASYNC_INVOKER_FOR macro:
332 * WAITABLE_ASYNC_INVOKER_FOR(Ifoo) myInvoker(foo);
333 * HRESULT hr = ASYNC_INVOKE(myInvoker, Bar, 7);
334 * if (SUCCEEDED(hr)) {
335 * myInvoker.Wait(); // <-- Wait for the COM call to complete.
338 * In general you should avoid using the waitable version, but in some corner
339 * cases it is absolutely necessary in order to preserve correctness while
342 * Finally, it is also possible to have the async invoker enqueue a runnable
343 * to the main thread when the async operation completes:
345 * EVENT_DRIVEN_ASYNC_INVOKER_FOR(Ifoo) myInvoker(foo);
346 * // myRunnable will be invoked on the main thread once the async operation
347 * // has completed. Note that we set this *before* we do the ASYNC_INVOKE!
348 * myInvoker.SetCompletionRunnable(myRunnable.forget());
349 * HRESULT hr = ASYNC_INVOKE(myInvoker, Bar, 7);
352 template <typename SyncInterface
, typename AsyncInterface
,
353 template <typename Iface
> class WaitPolicy
=
354 detail::FireAndForgetInvoker
>
355 class MOZ_RAII AsyncInvoker final
: public WaitPolicy
<AsyncInterface
> {
356 using Base
= WaitPolicy
<AsyncInterface
>;
359 typedef SyncInterface SyncInterfaceT
;
360 typedef AsyncInterface AsyncInterfaceT
;
363 * @param aSyncObj The COM object on which to invoke the asynchronous event.
364 * If this object is not a proxy to the synchronous variant
365 * of AsyncInterface, then it will be invoked synchronously
366 * instead (because it is an in-process virtual method call).
367 * @param aIsProxy An optional hint as to whether or not aSyncObj is a proxy.
368 * If not specified, AsyncInvoker will automatically detect
369 * whether aSyncObj is a proxy, however there may be a
370 * performance penalty associated with that.
372 explicit AsyncInvoker(SyncInterface
* aSyncObj
,
373 const Maybe
<bool>& aIsProxy
= Nothing()) {
374 MOZ_ASSERT(aSyncObj
);
376 RefPtr
<ICallFactory
> callFactory
;
377 if ((aIsProxy
.isSome() && !aIsProxy
.value()) ||
378 FAILED(aSyncObj
->QueryInterface(IID_ICallFactory
,
379 getter_AddRefs(callFactory
)))) {
384 this->mAsyncCall
= new typename
Base::AsyncCallType(callFactory
);
388 * @brief Invoke a method on the object. Member function pointers are provided
389 * for both the sychronous and asynchronous variants of the interface.
390 * If this invoker's encapsulated COM object is a proxy, then Invoke
391 * will call the asynchronous member function. Otherwise the
392 * synchronous version must be used, as the invocation will simply be a
393 * virtual function call that executes in-process.
394 * @param aSyncMethod Pointer to the method that we would like to invoke on
395 * the synchronous interface.
396 * @param aAsyncMethod Pointer to the method that we would like to invoke on
397 * the asynchronous interface.
399 template <typename SyncMethod
, typename AsyncMethod
, typename
... Args
>
400 HRESULT
Invoke(SyncMethod aSyncMethod
, AsyncMethod aAsyncMethod
,
402 this->OnBeginInvoke();
404 HRESULT hr
= (mSyncObj
->*aSyncMethod
)(std::forward
<Args
>(aArgs
)...);
405 this->OnSyncInvoke(hr
);
409 MOZ_ASSERT(this->mAsyncCall
);
410 if (!this->mAsyncCall
) {
411 this->OnAsyncInvokeFailed();
415 AsyncInterface
* asyncInterface
= this->mAsyncCall
->GetInterface();
416 MOZ_ASSERT(asyncInterface
);
417 if (!asyncInterface
) {
418 this->OnAsyncInvokeFailed();
422 HRESULT hr
= (asyncInterface
->*aAsyncMethod
)(std::forward
<Args
>(aArgs
)...);
424 this->OnAsyncInvokeFailed();
430 AsyncInvoker(const AsyncInvoker
& aOther
) = delete;
431 AsyncInvoker(AsyncInvoker
&& aOther
) = delete;
432 AsyncInvoker
& operator=(const AsyncInvoker
& aOther
) = delete;
433 AsyncInvoker
& operator=(AsyncInvoker
&& aOther
) = delete;
436 RefPtr
<SyncInterface
> mSyncObj
;
439 template <typename SyncInterface
, typename AsyncInterface
>
440 using WaitableAsyncInvoker
=
441 AsyncInvoker
<SyncInterface
, AsyncInterface
, detail::WaitableInvoker
>;
443 template <typename SyncInterface
, typename AsyncInterface
>
444 using EventDrivenAsyncInvoker
=
445 AsyncInvoker
<SyncInterface
, AsyncInterface
, detail::EventDrivenInvoker
>;
448 } // namespace mozilla
450 #define ASYNC_INVOKER_FOR(SyncIface) \
451 mozilla::mscom::AsyncInvoker<SyncIface, Async##SyncIface>
453 #define WAITABLE_ASYNC_INVOKER_FOR(SyncIface) \
454 mozilla::mscom::WaitableAsyncInvoker<SyncIface, Async##SyncIface>
456 #define EVENT_DRIVEN_ASYNC_INVOKER_FOR(SyncIface) \
457 mozilla::mscom::EventDrivenAsyncInvoker<SyncIface, Async##SyncIface>
459 #define ASYNC_INVOKE(InvokerObj, SyncMethodName, ...) \
461 &decltype(InvokerObj)::SyncInterfaceT::SyncMethodName, \
462 &decltype(InvokerObj)::AsyncInterfaceT::Begin_##SyncMethodName, \
465 #endif // mozilla_mscom_AsyncInvoker_h