// // DependencyManager.h // // // Created by Clément Brisset on 12/10/14. // Copyright 2014 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #ifndef hifi_DependencyManager_h #define hifi_DependencyManager_h #include #include #include #include #include #include #include #include #define SINGLETON_DEPENDENCY \ friend class ::DependencyManager; class Dependency { public: typedef std::function DeleterFunction; protected: virtual ~Dependency() {} virtual void customDeleter() { _customDeleter(this); } void setCustomDeleter(DeleterFunction customDeleter) { _customDeleter = customDeleter; } DeleterFunction _customDeleter = [](Dependency* pointer) { delete pointer; }; friend class DependencyManager; }; // usage: // auto instance = DependencyManager::get(); // auto instance = DependencyManager::set(Args... args); // DependencyManager::destroy(); // DependencyManager::registerInheritance(); class DependencyManager { public: template static QSharedPointer get(); template static bool isSet(); template static QSharedPointer set(Args&&... args); template static QSharedPointer set(Args&&... args); template static void destroy(); template static void registerInheritance(); template static size_t typeHash() { #ifdef Q_OS_ANDROID size_t hashCode = std::hash{}( typeid(T).name() ); #else size_t hashCode = typeid(T).hash_code(); #endif return hashCode; } static void prepareToExit() { manager()._exiting = true; } private: static DependencyManager& manager(); template size_t getHashCode() const; QSharedPointer safeGet(size_t hashCode) const; QHash> _instanceHash; QHash _inheritanceHash; mutable QMutex _instanceHashMutex; mutable QMutex _inheritanceHashMutex; bool _exiting { false }; }; template QSharedPointer DependencyManager::get() { static size_t hashCode = manager().getHashCode(); static QWeakPointer instance; if (instance.isNull()) { instance = qSharedPointerCast(manager().safeGet(hashCode)); #ifndef QT_NO_DEBUG // debug builds... if (instance.isNull()) { qWarning() << "DependencyManager::get(): No instance available for" << typeid(T).name(); } #else // for non-debug builds, don't print "No instance available" during shutdown, because // the act of printing this often causes crashes (because the LogHandler has-been/is-being // deleted). if (!manager()._exiting && instance.isNull()) { qWarning() << "DependencyManager::get(): No instance available for" << typeid(T).name(); } #endif } return instance.toStrongRef(); } template bool DependencyManager::isSet() { static size_t hashCode = manager().getHashCode(); QSharedPointer instance = manager().safeGet(hashCode); return !instance.isNull(); } template QSharedPointer DependencyManager::set(Args&&... args) { static size_t hashCode = manager().getHashCode(); QMutexLocker lock(&manager()._instanceHashMutex); // clear the previous instance before constructing the new instance auto iter = manager()._instanceHash.find(hashCode); if (iter != manager()._instanceHash.end()) { iter.value().clear(); } QSharedPointer newInstance(new T(args...), &T::customDeleter); manager()._instanceHash.insert(hashCode, newInstance); return newInstance; } template QSharedPointer DependencyManager::set(Args&&... args) { static size_t hashCode = manager().getHashCode(); QMutexLocker lock(&manager()._instanceHashMutex); // clear the previous instance before constructing the new instance auto iter = manager()._instanceHash.find(hashCode); if (iter != manager()._instanceHash.end()) { iter.value().clear(); } QSharedPointer newInstance(new I(args...), &I::customDeleter); manager()._instanceHash.insert(hashCode, newInstance); return newInstance; } template void DependencyManager::destroy() { static size_t hashCode = manager().getHashCode(); QMutexLocker lock(&manager()._instanceHashMutex); QSharedPointer shared = manager()._instanceHash.take(hashCode); QWeakPointer weak = shared; shared.clear(); // Check that the dependency was actually destroyed. If it wasn't, it was improperly captured somewhere if (weak.lock()) { qWarning() << "DependencyManager::destroy():" << typeid(T).name() << "was not properly destroyed!"; } } template void DependencyManager::registerInheritance() { size_t baseHashCode = typeHash(); size_t derivedHashCode = typeHash(); QMutexLocker lock(&manager()._inheritanceHashMutex); manager()._inheritanceHash.insert(baseHashCode, derivedHashCode); } template size_t DependencyManager::getHashCode() const { size_t hashCode = typeHash(); QMutexLocker lock(&_inheritanceHashMutex); auto derivedHashCode = _inheritanceHash.find(hashCode); while (derivedHashCode != _inheritanceHash.end()) { hashCode = derivedHashCode.value(); derivedHashCode = _inheritanceHash.find(hashCode); } return hashCode; } #endif // hifi_DependencyManager_h