summaryrefslogtreecommitdiff
path: root/cppuhelper
diff options
context:
space:
mode:
authorStephan Bergmann <sbergman@redhat.com>2020-09-30 21:31:10 +0200
committerStephan Bergmann <sbergman@redhat.com>2020-10-01 09:44:53 +0200
commit1a1d432420646bcf129624a866fd6c754e25d195 (patch)
tree9c0b292838486fccbda4942a5a44da82db6e9b2a /cppuhelper
parent9c850ca827486db3702699e5c2218842d7361b1b (diff)
Add single-instance attribute for *.compoonent <implementation>s
...to ease the implementation in C++ code of constructor-based <implementation>s that shall be single-instance (and which would have been implemented with e.g. cppu::creaetOneInstanceFactory for non--constructor-based <implementation>s). See e.g. 6e35794cad555485955c3b43593497dcdbf29840 "terminate XDesktop properly in unit tests" and 6362ebab298549e8616c32cafd75cb3959ba7d65 "dbaccess: create instances with uno constructors" for the clumsy approach used until now, where the C++ constructor function uses a static instance that is cleared in dispose(), adding fake <singleton> entries to <implementation>s where necessary so that the ServiceManager will call those XComponent::dispose() functions when it itself gets disposed. For every <implementation>, the ServiceManager already holds an Implementation data structure, so it can easily hold a singleInstance there and clear it when the ServiceManager gets disposed. (One consequence is that single-instance implementations are now created with their Instance.mutex locked, but that should not cause problems in practice, given that the construction of a single- instance implementation should not recursively request its own construction anyway.) The new single-instance="true" attribute is mostly useful in combination with the constructor attribute (see above), but it can also be used for non-- constructor-based <implementation>s, at least in theory. (The single-instance="true" attribute is orthogonal to <singleton> elements. There are existing single-instance services---even if those should arguably have been defined as singletons in the first place---that can benefit from single-instance="true". And there are <implementation>s that support one or more <singleton>s alongside one or more <service>s, where the latter are not single-instance.) This new single-instance="true" attribute in *.component files should not interfere with the lo_get_constructor_map machinery in the DISABLE_DYNLOADING branch of cppuhelper::detail::loadSharedLibComponentFactory (cppuhelper/source/shlib.cxx) used by Android, iOS and some fuzzer code. AFAIU, the lo_get_constructor_map machinery should only ever come into play after the ServiceManager has decided whether or not to create a new instance based on the single-instance attributes in the *.component files. This commit only provides the new single-instance="true" attribute. Actual changes of <implementation>s and their C++ constructor functions are delegated to a series of follow-up commits. (So that they can easily be reverted individually should the need arise.) Change-Id: Iea6c0fc539d74477b7a536dc771b198df6b0510e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/103734 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
Diffstat (limited to 'cppuhelper')
-rw-r--r--cppuhelper/source/servicemanager.cxx97
-rw-r--r--cppuhelper/source/servicemanager.hxx19
2 files changed, 88 insertions, 28 deletions
diff --git a/cppuhelper/source/servicemanager.cxx b/cppuhelper/source/servicemanager.cxx
index f06405f7794b..fe9ef7dbd2b4 100644
--- a/cppuhelper/source/servicemanager.cxx
+++ b/cppuhelper/source/servicemanager.cxx
@@ -327,6 +327,7 @@ void Parser::handleComponent() {
void Parser::handleImplementation() {
OUString attrName;
OUString attrConstructor;
+ bool attrSingleInstance = false;
xmlreader::Span name;
int nsId;
while (reader_.nextAttribute(&nsId, &name)) {
@@ -366,6 +367,19 @@ void Parser::handleImplementation() {
+ ": <implementation> has \"constructor\" attribute but"
" <component> has no \"environment\" attribute");
}
+ } else if (nsId == xmlreader::XmlReader::NAMESPACE_NONE
+ && name.equals(RTL_CONSTASCII_STRINGPARAM("single-instance")))
+ {
+ if (attrSingleInstance) {
+ throw css::registry::InvalidRegistryException(
+ reader_.getUrl()
+ + ": <implementation> has multiple \"single-instance\" attributes");
+ }
+ if (!reader_.getAttributeValue(false).equals(RTL_CONSTASCII_STRINGPARAM("true"))) {
+ throw css::registry::InvalidRegistryException(
+ reader_.getUrl() + ": <implementation> has bad \"single-instance\" attribute");
+ }
+ attrSingleInstance = true;
} else {
throw css::registry::InvalidRegistryException(
reader_.getUrl() + ": unexpected element attribute \""
@@ -380,7 +394,7 @@ void Parser::handleImplementation() {
implementation_ =
std::make_shared<cppuhelper::ServiceManager::Data::Implementation>(
attrName, attrLoader_, attrUri_, attrEnvironment_, attrConstructor,
- attrPrefix_, alienContext_, reader_.getUrl());
+ attrPrefix_, attrSingleInstance, alienContext_, reader_.getUrl());
if (!data_->namedImplementations.emplace(attrName, implementation_).
second)
{
@@ -647,28 +661,62 @@ cppuhelper::ServiceManager::Data::Implementation::createInstance(
bool singletonRequest)
{
css::uno::Reference<css::uno::XInterface> inst;
+ if (isSingleInstance) {
+ osl::MutexGuard g(mutex);
+ if (!singleInstance.is()) {
+ singleInstance = doCreateInstance(context);
+ }
+ inst = singleInstance;
+ } else {
+ inst = doCreateInstance(context);
+ }
+ updateDisposeInstance(singletonRequest, inst);
+ return inst;
+}
+
+css::uno::Reference<css::uno::XInterface>
+cppuhelper::ServiceManager::Data::Implementation::createInstanceWithArguments(
+ css::uno::Reference<css::uno::XComponentContext> const & context,
+ bool singletonRequest, css::uno::Sequence<css::uno::Any> const & arguments)
+{
+ css::uno::Reference<css::uno::XInterface> inst;
+ if (isSingleInstance) {
+ osl::MutexGuard g(mutex);
+ if (!singleInstance.is()) {
+ singleInstance = doCreateInstanceWithArguments(context, arguments);
+ }
+ inst = singleInstance;
+ } else {
+ inst = doCreateInstanceWithArguments(context, arguments);
+ }
+ updateDisposeInstance(singletonRequest, inst);
+ return inst;
+}
+
+css::uno::Reference<css::uno::XInterface>
+cppuhelper::ServiceManager::Data::Implementation::doCreateInstance(
+ css::uno::Reference<css::uno::XComponentContext> const & context)
+{
if (constructorFn) {
- inst.set(
+ return css::uno::Reference<css::uno::XInterface>(
constructorFn(context.get(), css::uno::Sequence<css::uno::Any>()),
SAL_NO_ACQUIRE);
} else if (factory1.is()) {
- inst = factory1->createInstanceWithContext(context);
+ return factory1->createInstanceWithContext(context);
} else {
assert(factory2.is());
- inst = factory2->createInstance();
+ return factory2->createInstance();
}
- updateDisposeSingleton(singletonRequest, inst);
- return inst;
}
css::uno::Reference<css::uno::XInterface>
-cppuhelper::ServiceManager::Data::Implementation::createInstanceWithArguments(
+cppuhelper::ServiceManager::Data::Implementation::doCreateInstanceWithArguments(
css::uno::Reference<css::uno::XComponentContext> const & context,
- bool singletonRequest, css::uno::Sequence<css::uno::Any> const & arguments)
+ css::uno::Sequence<css::uno::Any> const & arguments)
{
- css::uno::Reference<css::uno::XInterface> inst;
if (constructorFn) {
- inst.set(constructorFn(context.get(), arguments), SAL_NO_ACQUIRE);
+ css::uno::Reference<css::uno::XInterface> inst(
+ constructorFn(context.get(), arguments), SAL_NO_ACQUIRE);
//HACK: The constructor will either observe arguments and return inst
// that does not implement XInitialization (or null), or ignore
// arguments and return inst that implements XInitialization; this
@@ -679,18 +727,17 @@ cppuhelper::ServiceManager::Data::Implementation::createInstanceWithArguments(
if (init.is()) {
init->initialize(arguments);
}
+ return inst;
} else if (factory1.is()) {
- inst = factory1->createInstanceWithArgumentsAndContext(
+ return factory1->createInstanceWithArgumentsAndContext(
arguments, context);
} else {
assert(factory2.is());
- inst = factory2->createInstanceWithArguments(arguments);
+ return factory2->createInstanceWithArguments(arguments);
}
- updateDisposeSingleton(singletonRequest, inst);
- return inst;
}
-void cppuhelper::ServiceManager::Data::Implementation::updateDisposeSingleton(
+void cppuhelper::ServiceManager::Data::Implementation::updateDisposeInstance(
bool singletonRequest,
css::uno::Reference<css::uno::XInterface> const & instance)
{
@@ -702,15 +749,15 @@ void cppuhelper::ServiceManager::Data::Implementation::updateDisposeSingleton(
// the implementation hands out different instances):
if (singletonRequest) {
osl::MutexGuard g(mutex);
- disposeSingleton.clear();
+ disposeInstance.clear();
dispose = false;
- } else if (!singletons.empty()) {
+ } else if (shallDispose()) {
css::uno::Reference<css::lang::XComponent> comp(
instance, css::uno::UNO_QUERY);
if (comp.is()) {
osl::MutexGuard g(mutex);
if (dispose) {
- disposeSingleton = comp;
+ disposeInstance = comp;
}
}
}
@@ -840,20 +887,20 @@ void cppuhelper::ServiceManager::disposing() {
for (const auto& rEntry : data_.namedImplementations)
{
assert(rEntry.second);
- if (!rEntry.second->singletons.empty()) {
+ if (rEntry.second->shallDispose()) {
osl::MutexGuard g2(rEntry.second->mutex);
- if (rEntry.second->disposeSingleton.is()) {
- sngls.push_back(rEntry.second->disposeSingleton);
+ if (rEntry.second->disposeInstance.is()) {
+ sngls.push_back(rEntry.second->disposeInstance);
}
}
}
for (const auto& rEntry : data_.dynamicImplementations)
{
assert(rEntry.second);
- if (!rEntry.second->singletons.empty()) {
+ if (rEntry.second->shallDispose()) {
osl::MutexGuard g2(rEntry.second->mutex);
- if (rEntry.second->disposeSingleton.is()) {
- sngls.push_back(rEntry.second->disposeSingleton);
+ if (rEntry.second->disposeInstance.is()) {
+ sngls.push_back(rEntry.second->disposeInstance);
}
}
if (rEntry.second->component.is()) {
@@ -1395,7 +1442,7 @@ bool cppuhelper::ServiceManager::readLegacyRdbFile(OUString const & uri) {
std::shared_ptr< Data::Implementation > impl =
std::make_shared<Data::Implementation>(
name, readLegacyRdbString(uri, implKey, "UNO/ACTIVATOR"),
- readLegacyRdbString(uri, implKey, "UNO/LOCATION"), "", "", "",
+ readLegacyRdbString(uri, implKey, "UNO/LOCATION"), "", "", "", false,
css::uno::Reference< css::uno::XComponentContext >(), uri);
if (!data_.namedImplementations.emplace(name, impl).second)
{
diff --git a/cppuhelper/source/servicemanager.hxx b/cppuhelper/source/servicemanager.hxx
index 9a7cac513402..24a7e56a4ba9 100644
--- a/cppuhelper/source/servicemanager.hxx
+++ b/cppuhelper/source/servicemanager.hxx
@@ -74,11 +74,13 @@ public:
OUString const & theUri, OUString const & theEnvironment,
OUString const & theConstructorName,
OUString const & thePrefix,
+ bool theIsSingleInstance,
css::uno::Reference< css::uno::XComponentContext > const &
theAlienContext,
OUString const & theRdbFile):
name(theName), loader(theLoader), uri(theUri), environment(theEnvironment),
constructorName(theConstructorName), prefix(thePrefix),
+ isSingleInstance(theIsSingleInstance),
alienContext(theAlienContext), rdbFile(theRdbFile),
constructorFn(nullptr), status(STATUS_NEW), dispose(true)
{}
@@ -91,7 +93,7 @@ public:
theFactory2,
css::uno::Reference< css::lang::XComponent > const &
theComponent):
- name(theName), constructorFn(nullptr),
+ name(theName), isSingleInstance(false), constructorFn(nullptr),
factory1(theFactory1), factory2(theFactory2),
component(theComponent), status(STATUS_LOADED), dispose(true)
{ assert(theFactory1.is() || theFactory2.is()); }
@@ -111,6 +113,8 @@ public:
bool singletonRequest,
css::uno::Sequence<css::uno::Any> const & arguments);
+ bool shallDispose() const { return isSingleInstance || !singletons.empty(); }
+
enum Status { STATUS_NEW, STATUS_WRAPPER, STATUS_LOADED };
// Logically, exactly one of constructorFn, factory1, factory2 should
@@ -130,6 +134,7 @@ public:
OUString environment;
OUString constructorName;
OUString prefix;
+ bool isSingleInstance;
css::uno::Reference< css::uno::XComponentContext > alienContext;
OUString rdbFile;
std::vector< OUString > services;
@@ -141,11 +146,19 @@ public:
Status status;
osl::Mutex mutex;
- css::uno::Reference< css::lang::XComponent > disposeSingleton;
+ css::uno::Reference<css::uno::XInterface> singleInstance;
+ css::uno::Reference< css::lang::XComponent > disposeInstance;
bool dispose;
private:
- void updateDisposeSingleton(
+ css::uno::Reference<css::uno::XInterface> doCreateInstance(
+ css::uno::Reference<css::uno::XComponentContext> const & context);
+
+ css::uno::Reference<css::uno::XInterface> doCreateInstanceWithArguments(
+ css::uno::Reference<css::uno::XComponentContext> const & context,
+ css::uno::Sequence<css::uno::Any> const & arguments);
+
+ void updateDisposeInstance(
bool singletonRequest,
css::uno::Reference<css::uno::XInterface> const & instance);
};