зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1570701: Part 2 - Add asynchronous CreateInstance method to mscom::EnsureMTA; r=Jamie
We'd like to offer a way for developers to safely do asynchronous instantiations of COM objects. This patch adds a static CreateInstance method to mscom::EnsureMTA that facilitates this under certain conditions (as outlined in the comments). To help ensure that this is as safe as possible, we return a MozPromise that, once resolved, produces an AgileReference that may then be passed between threads, and may then be resolved when a specific thread needs to access the interface. Differential Revision: https://phabricator.services.mozilla.com/D44519 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
b36ad40063
Коммит
b1a47abb5e
|
@ -7,6 +7,7 @@
|
|||
#include "mozilla/mscom/EnsureMTA.h"
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "mozilla/StaticLocalPtr.h"
|
||||
#include "mozilla/SystemGroup.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
@ -57,6 +58,67 @@ class BackgroundMTAData {
|
|||
namespace mozilla {
|
||||
namespace mscom {
|
||||
|
||||
/* static */
|
||||
RefPtr<EnsureMTA::CreateInstanceAgileRefPromise>
|
||||
EnsureMTA::CreateInstanceInternal(REFCLSID aClsid, REFIID aIid) {
|
||||
MOZ_ASSERT(IsCurrentThreadExplicitMTA());
|
||||
|
||||
RefPtr<IUnknown> iface;
|
||||
HRESULT hr = ::CoCreateInstance(aClsid, nullptr, CLSCTX_INPROC_SERVER, aIid,
|
||||
getter_AddRefs(iface));
|
||||
if (FAILED(hr)) {
|
||||
return CreateInstanceAgileRefPromise::CreateAndReject(hr, __func__);
|
||||
}
|
||||
|
||||
// We need to use the two argument constructor for AgileReference because our
|
||||
// RefPtr is not parameterized on the specific interface being requested.
|
||||
AgileReference agileRef(aIid, iface);
|
||||
if (!agileRef) {
|
||||
return CreateInstanceAgileRefPromise::CreateAndReject(agileRef.GetHResult(),
|
||||
__func__);
|
||||
}
|
||||
|
||||
return CreateInstanceAgileRefPromise::CreateAndResolve(std::move(agileRef),
|
||||
__func__);
|
||||
}
|
||||
|
||||
/* static */
|
||||
RefPtr<EnsureMTA::CreateInstanceAgileRefPromise> EnsureMTA::CreateInstance(
|
||||
REFCLSID aClsid, REFIID aIid) {
|
||||
MOZ_ASSERT(IsCOMInitializedOnCurrentThread());
|
||||
|
||||
const bool isClassOk = IsClassThreadAwareInprocServer(aClsid);
|
||||
MOZ_ASSERT(isClassOk,
|
||||
"mozilla::mscom::EnsureMTA::CreateInstance is not "
|
||||
"safe/performant/necessary to use with this CLSID. This CLSID "
|
||||
"either does not support creation from within a multithreaded "
|
||||
"apartment, or it is not an in-process server.");
|
||||
if (!isClassOk) {
|
||||
return CreateInstanceAgileRefPromise::CreateAndReject(CO_E_NOT_SUPPORTED,
|
||||
__func__);
|
||||
}
|
||||
|
||||
if (IsCurrentThreadExplicitMTA()) {
|
||||
// It's safe to immediately call CreateInstanceInternal
|
||||
return CreateInstanceInternal(aClsid, aIid);
|
||||
}
|
||||
|
||||
// aClsid and aIid are references. Make local copies that we can put into the
|
||||
// lambda in case the sources of aClsid or aIid are not static data
|
||||
CLSID localClsid = aClsid;
|
||||
IID localIid = aIid;
|
||||
|
||||
auto invoker = [localClsid,
|
||||
localIid]() -> RefPtr<CreateInstanceAgileRefPromise> {
|
||||
return CreateInstanceInternal(localClsid, localIid);
|
||||
};
|
||||
|
||||
nsCOMPtr<nsIThread> mtaThread(GetMTAThread());
|
||||
|
||||
return InvokeAsync(mtaThread->SerialEventTarget(), __func__,
|
||||
std::move(invoker));
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsCOMPtr<nsIThread> EnsureMTA::GetMTAThread() {
|
||||
static StaticLocalAutoPtr<BackgroundMTAData> sMTAData(
|
||||
|
|
|
@ -10,8 +10,11 @@
|
|||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/mscom/AgileReference.h"
|
||||
#include "mozilla/mscom/Utils.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIThread.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
@ -107,7 +110,69 @@ class MOZ_STACK_CLASS EnsureMTA final {
|
|||
MOZ_ASSERT(waitResult == WAIT_OBJECT_0);
|
||||
}
|
||||
|
||||
using CreateInstanceAgileRefPromise =
|
||||
MozPromise<AgileReference, HRESULT, false>;
|
||||
|
||||
/**
|
||||
* *** A MSCOM PEER SHOULD REVIEW ALL NEW USES OF THIS API! ***
|
||||
*
|
||||
* Asynchronously instantiate a new COM object from a MTA thread, unless the
|
||||
* current thread is already living inside the multithreaded apartment, in
|
||||
* which case the object is immediately instantiated.
|
||||
*
|
||||
* This function only supports the most common configurations for creating
|
||||
* a new object, so it only supports in-process servers. Furthermore, this
|
||||
* function does not support aggregation (ie. the |pUnkOuter| parameter to
|
||||
* CoCreateInstance).
|
||||
*
|
||||
* Given that attempting to instantiate an Apartment-threaded COM object
|
||||
* inside the MTA results in a *loss* of performance, we assert when that
|
||||
* situation arises.
|
||||
*
|
||||
* The resulting promise, once resolved, provides an AgileReference that may
|
||||
* be passed between any COM-initialized thread in the current process.
|
||||
*
|
||||
* *** A MSCOM PEER SHOULD REVIEW ALL NEW USES OF THIS API! ***
|
||||
*
|
||||
* WARNING:
|
||||
* Some COM objects do not support creation in the multithreaded apartment,
|
||||
* in which case this function is not available as an option. In this case,
|
||||
* the promise will always be rejected. In debug builds we will assert.
|
||||
*
|
||||
* *** A MSCOM PEER SHOULD REVIEW ALL NEW USES OF THIS API! ***
|
||||
*
|
||||
* WARNING:
|
||||
* Any in-process COM objects whose interfaces accept HWNDs are probably
|
||||
* *not* safe to instantiate in the multithreaded apartment! Even if this
|
||||
* function succeeds when creating such an object, you *MUST NOT* do so, as
|
||||
* these failures might not become apparent until your code is running out in
|
||||
* the wild on the release channel!
|
||||
*
|
||||
* *** A MSCOM PEER SHOULD REVIEW ALL NEW USES OF THIS API! ***
|
||||
*
|
||||
* WARNING:
|
||||
* When you obtain an interface from the AgileReference, it may or may not be
|
||||
* a proxy to the real object. This depends entirely on the implementation of
|
||||
* the underlying class and the multithreading capabilities that the class
|
||||
* declares to the COM runtime. If the interface is proxied, it might be
|
||||
* expensive to invoke methods on that interface! *Always* test the
|
||||
* performance of your method calls when calling interfaces that are resolved
|
||||
* via this function!
|
||||
*
|
||||
* *** A MSCOM PEER SHOULD REVIEW ALL NEW USES OF THIS API! ***
|
||||
*
|
||||
* (Despite this myriad of warnings, it is still *much* safer to use this
|
||||
* function to asynchronously create COM objects than it is to roll your own!)
|
||||
*
|
||||
* *** A MSCOM PEER SHOULD REVIEW ALL NEW USES OF THIS API! ***
|
||||
*/
|
||||
static RefPtr<CreateInstanceAgileRefPromise> CreateInstance(REFCLSID aClsid,
|
||||
REFIID aIid);
|
||||
|
||||
private:
|
||||
static RefPtr<CreateInstanceAgileRefPromise> CreateInstanceInternal(
|
||||
REFCLSID aClsid, REFIID aIid);
|
||||
|
||||
static nsCOMPtr<nsIThread> GetMTAThread();
|
||||
|
||||
// The following function is private in order to force any consumers to be
|
||||
|
|
Загрузка…
Ссылка в новой задаче