diff --git a/src/mvvmcore/serviceregistry.cpp b/src/mvvmcore/serviceregistry.cpp index 25b87d1..3ee2177 100644 --- a/src/mvvmcore/serviceregistry.cpp +++ b/src/mvvmcore/serviceregistry.cpp @@ -15,9 +15,19 @@ Q_GLOBAL_STATIC_WITH_ARGS(ServiceRegistry, _instance, ServiceRegistry::ServiceRegistry(ServiceRegistryPrivate *d_ptr) : d(d_ptr) -{} +{ + // register quit scope cleanup + QObject::connect(qApp, &QCoreApplication::aboutToQuit, [this](){ + d->destroyServices(DestroyOnAppQuit); + }); + // register destroy scope cleanup + qAddPostRoutine(&ServiceRegistryPrivate::appDestroyedHook); +} -ServiceRegistry::~ServiceRegistry() = default; +ServiceRegistry::~ServiceRegistry() +{ + d->destroyServices(DestroyOnRegistryDestroy); +} ServiceRegistry *ServiceRegistry::instance() { @@ -30,20 +40,30 @@ bool ServiceRegistry::isRegistered(const QByteArray &iid) const return d->services.contains(iid); } -void ServiceRegistry::registerService(const QByteArray &iid, const QMetaObject *metaObject, bool weak) +void ServiceRegistry::registerService(const QByteArray &iid, const QMetaObject *metaObject, bool weak, DestructionScope scope) { QMutexLocker _(&d->serviceMutex); if(d->serviceBlocked(iid)) throw ServiceExistsException(iid); - d->services.insert(iid, QSharedPointer::create(metaObject, weak)); + d->services.insert(iid, QSharedPointer::create(metaObject, weak, scope)); } -void ServiceRegistry::registerService(const QByteArray &iid, const std::function &fn, QByteArrayList injectables, bool weak) +void ServiceRegistry::registerService(const QByteArray &iid, const QMetaObject *metaObject, bool weak) +{ + registerService(iid, metaObject, weak, DestroyOnAppDestroy); +} + +void ServiceRegistry::registerService(const QByteArray &iid, const std::function &fn, QByteArrayList injectables, bool weak, DestructionScope scope) { QMutexLocker _(&d->serviceMutex); if(d->serviceBlocked(iid)) throw ServiceExistsException(iid); - d->services.insert(iid, QSharedPointer::create(fn, injectables, weak)); + d->services.insert(iid, QSharedPointer::create(fn, std::move(injectables), weak, scope)); +} + +void ServiceRegistry::registerService(const QByteArray &iid, const std::function &fn, QByteArrayList injectables, bool weak) +{ + registerService(iid, fn, std::move(injectables), weak, DestroyOnAppDestroy); } QObject *ServiceRegistry::serviceObj(const QByteArray &iid) @@ -140,16 +160,37 @@ void ServiceRegistryPrivate::injectLocked(QObject *object) } } +void ServiceRegistryPrivate::destroyServices(ServiceRegistry::DestructionScope scope) +{ + QMutexLocker _(&serviceMutex); + logDebug() << "Beginning destruction for services in scope:" << scope; + for(auto it = services.begin(); it != services.end();) { + if(it.value()->needsDestroy(scope)) { + logDebug() << "Destroying service:" << it.key(); + it = services.erase(it); + } else + it++; + } + logDebug() << "Finished destruction for services in scope:" << scope; +} +void ServiceRegistryPrivate::appDestroyedHook() +{ + if(_instance.exists() && !_instance.isDestroyed()) + _instance->d->destroyServices(ServiceRegistry::DestroyOnAppDestroy); +} -ServiceRegistryPrivate::ServiceInfo::ServiceInfo(bool weak) : - _weak{weak} + + +ServiceRegistryPrivate::ServiceInfo::ServiceInfo(bool weak, ServiceRegistry::DestructionScope scope) : + _weak{weak}, + _scope{scope} {} ServiceRegistryPrivate::ServiceInfo::~ServiceInfo() { - if(_instance) { - if(QCoreApplication::closingDown()) + if(_instance && _scope != ServiceRegistry::DestroyNever) { + if(_closingDown) delete _instance; else QMetaObject::invokeMethod(_instance, "deleteLater"); @@ -161,6 +202,15 @@ bool ServiceRegistryPrivate::ServiceInfo::replaceable() const return _weak && !_instance; } +bool ServiceRegistryPrivate::ServiceInfo::needsDestroy(ServiceRegistry::DestructionScope scope) const +{ + if(_scope <= scope) { + _closingDown = true; + return true; + } else + return false; +} + QObject *ServiceRegistryPrivate::ServiceInfo::instance(ServiceRegistryPrivate *d, const QByteArray &iid) { if(!_instance) { @@ -178,10 +228,10 @@ QObject *ServiceRegistryPrivate::ServiceInfo::instance(ServiceRegistryPrivate *d -ServiceRegistryPrivate::FnServiceInfo::FnServiceInfo(std::function creator, QByteArrayList injectables, bool weak) : - ServiceInfo(weak), - creator(std::move(creator)), - injectables(std::move(injectables)) +ServiceRegistryPrivate::FnServiceInfo::FnServiceInfo(std::function creator, QByteArrayList injectables, bool weak, ServiceRegistry::DestructionScope scope) : + ServiceInfo{weak, scope}, + creator{std::move(creator)}, + injectables{std::move(injectables)} {} QObject *ServiceRegistryPrivate::FnServiceInfo::construct(ServiceRegistryPrivate *d) const @@ -199,9 +249,9 @@ QObject *ServiceRegistryPrivate::FnServiceInfo::construct(ServiceRegistryPrivate -ServiceRegistryPrivate::MetaServiceInfo::MetaServiceInfo(const QMetaObject *metaObject, bool weak) : - ServiceInfo(weak), - metaObject(metaObject) +ServiceRegistryPrivate::MetaServiceInfo::MetaServiceInfo(const QMetaObject *metaObject, bool weak, ServiceRegistry::DestructionScope scope) : + ServiceInfo{weak, scope}, + metaObject{metaObject} {} QObject *ServiceRegistryPrivate::MetaServiceInfo::construct(ServiceRegistryPrivate *d) const diff --git a/src/mvvmcore/serviceregistry.h b/src/mvvmcore/serviceregistry.h index 3326ad6..b17bb35 100644 --- a/src/mvvmcore/serviceregistry.h +++ b/src/mvvmcore/serviceregistry.h @@ -17,6 +17,14 @@ class ServiceRegistryPrivate; class Q_MVVMCORE_EXPORT ServiceRegistry { public: + enum DestructionScope { + DestroyOnAppQuit = 1, + DestroyOnAppDestroy = 2, + DestroyOnRegistryDestroy = 3, + + DestroyNever = 127 + }; + //! @private ServiceRegistry(ServiceRegistryPrivate *d_ptr); ~ServiceRegistry(); @@ -32,32 +40,41 @@ public: //! Register a service for its interface via the type template - void registerInterface(bool weak = false); + void registerInterface(bool weak = false, DestructionScope scope = DestroyOnAppDestroy); //! Register a service for its interface via a constructor function template - void registerInterface(TFunc fn, bool weak = false); + void registerInterface(TFunc fn, bool weak = false, DestructionScope scope = DestroyOnAppDestroy); //! Register a service for its interface via an already existing instance template - void registerInterface(TService *service, bool weak = false); + void registerInterface(TService *service, bool weak = false, DestructionScope scope = DestroyOnAppDestroy); //! Register a service via its type template - void registerObject(bool weak = false); + void registerObject(bool weak = false, DestructionScope scope = DestroyOnAppDestroy); //! Register a service via a constructor function template - void registerObject(TFunc fn, bool weak = false); + void registerObject(TFunc fn, bool weak = false, DestructionScope scope = DestroyOnAppDestroy); //! Register a service via an already existing instance template - void registerObject(TService *service, bool weak = false); + void registerObject(TService *service, bool weak = false, DestructionScope scope = DestroyOnAppDestroy); //! Register a service by an iid via their metadata void registerService(const QByteArray &iid, const QMetaObject *metaObject, - bool weak = false); + bool weak, + DestructionScope scope); + void registerService(const QByteArray &iid, + const QMetaObject *metaObject, + bool weak = false); //MAJOR merge methods //! Register a service by an iid via a generalized constructor function void registerService(const QByteArray &iid, const std::function &fn, QByteArrayList injectables, - bool weak = false); + bool weak, + DestructionScope scope); + void registerService(const QByteArray &iid, + const std::function &fn, + QByteArrayList injectables, + bool weak = false);//MAJOR merge methods //! Returns the service for the given interface template @@ -152,29 +169,29 @@ bool ServiceRegistry::isRegistered() const Q_ASSERT_X(qobject_interface_iid(), Q_FUNC_INFO, "TInterface must be registered with Q_DECLARE_INTERFACE"); template -void ServiceRegistry::registerInterface(bool weak) +void ServiceRegistry::registerInterface(bool weak, DestructionScope scope) { QTMVVM_SERVICE_ASSERT(TInterface, TService) - registerService(qobject_interface_iid(), &TService::staticMetaObject, weak); + registerService(qobject_interface_iid(), &TService::staticMetaObject, weak, scope); } template -void ServiceRegistry::registerInterface(TFunc fn, bool weak) +void ServiceRegistry::registerInterface(TFunc fn, bool weak, DestructionScope scope) { QTMVVM_SERVICE_ASSERT(TInterface, TService) QByteArrayList injectables; auto packed_fn = __helpertypes::pack_function(std::move(fn), injectables); - registerService(qobject_interface_iid(), packed_fn, injectables, weak); + registerService(qobject_interface_iid(), packed_fn, injectables, weak, scope); } template -void ServiceRegistry::registerInterface(TService *service, bool weak) +void ServiceRegistry::registerInterface(TService *service, bool weak, DestructionScope scope) { QTMVVM_SERVICE_ASSERT(TInterface, TService) registerService(qobject_interface_iid(), [service](const QObjectList ¶ms) -> QObject* { Q_UNUSED(params); return service; - }, QByteArrayList(), weak); + }, QByteArrayList(), weak, scope); } #undef QTMVVM_SERVICE_ASSERT @@ -182,29 +199,29 @@ void ServiceRegistry::registerInterface(TService *service, bool weak) static_assert(__helpertypes::is_qobj::value, "TService must be a qobject class"); template -void ServiceRegistry::registerObject(bool weak) +void ServiceRegistry::registerObject(bool weak, DestructionScope scope) { QTMVVM_SERVICE_ASSERT(TService) - registerService(__helpertypes::qobject_iid(), &TService::staticMetaObject, weak); + registerService(__helpertypes::qobject_iid(), &TService::staticMetaObject, weak, scope); } template -void ServiceRegistry::registerObject(TFunc fn, bool weak) +void ServiceRegistry::registerObject(TFunc fn, bool weak, DestructionScope scope) { QTMVVM_SERVICE_ASSERT(TService) QByteArrayList injectables; auto packed_fn = __helpertypes::pack_function(std::move(fn), injectables); - registerService(__helpertypes::qobject_iid(), packed_fn, injectables, weak); + registerService(__helpertypes::qobject_iid(), packed_fn, injectables, weak, scope); } template -void ServiceRegistry::registerObject(TService *service, bool weak) +void ServiceRegistry::registerObject(TService *service, bool weak, DestructionScope scope) { QTMVVM_SERVICE_ASSERT(TService) registerService(__helpertypes::qobject_iid(), [service](const QObjectList ¶ms) -> QObject* { Q_UNUSED(params); return service; - }, QByteArrayList(), weak); + }, QByteArrayList(), weak, scope); } #undef QTMVVM_SERVICE_ASSERT diff --git a/src/mvvmcore/serviceregistry_p.h b/src/mvvmcore/serviceregistry_p.h index 79af7f8..a9153b2 100644 --- a/src/mvvmcore/serviceregistry_p.h +++ b/src/mvvmcore/serviceregistry_p.h @@ -15,10 +15,11 @@ public: class ServiceInfo { Q_DISABLE_COPY(ServiceInfo) public: - ServiceInfo(bool weak); + ServiceInfo(bool weak, ServiceRegistry::DestructionScope scope); virtual ~ServiceInfo(); bool replaceable() const; + bool needsDestroy(ServiceRegistry::DestructionScope scope) const; QObject *instance(ServiceRegistryPrivate *d, const QByteArray &iid); protected: @@ -26,12 +27,14 @@ public: private: const bool _weak; + const ServiceRegistry::DestructionScope _scope; QObject *_instance = nullptr; + mutable bool _closingDown = false; }; class FnServiceInfo : public ServiceInfo { public: - FnServiceInfo(std::function creator, QByteArrayList injectables, bool weak); + FnServiceInfo(std::function creator, QByteArrayList injectables, bool weak, ServiceRegistry::DestructionScope scope); protected: QObject *construct(ServiceRegistryPrivate *d) const final; @@ -43,7 +46,7 @@ public: class MetaServiceInfo : public ServiceInfo { public: - MetaServiceInfo(const QMetaObject *metaObject, bool weak); + MetaServiceInfo(const QMetaObject *metaObject, bool weak, ServiceRegistry::DestructionScope scope); protected: QObject *construct(ServiceRegistryPrivate *d) const final; @@ -58,6 +61,10 @@ public: bool serviceBlocked(const QByteArray &iid) const; QObject *constructInjectedLocked(const QMetaObject *metaObject, QObject *parent); void injectLocked(QObject *object); + + void destroyServices(ServiceRegistry::DestructionScope scope); + + static void appDestroyedHook(); }; }