/* * ComponentManager.cpp * -------------------- * Purpose: Manages loading of optional components. * Notes : (currently none) * Authors: Joern Heusipp * OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "ComponentManager.h" #include "mpt/mutex/mutex.hpp" #include "Logging.h" OPENMPT_NAMESPACE_BEGIN ComponentBase::ComponentBase(ComponentType type) : m_Type(type) , m_Initialized(false) , m_Available(false) { return; } ComponentBase::~ComponentBase() { return; } void ComponentBase::SetInitialized() { m_Initialized = true; } void ComponentBase::SetAvailable() { m_Available = true; } ComponentType ComponentBase::GetType() const { return m_Type; } bool ComponentBase::IsInitialized() const { return m_Initialized; } bool ComponentBase::IsAvailable() const { return m_Initialized && m_Available; } mpt::ustring ComponentBase::GetVersion() const { return mpt::ustring(); } void ComponentBase::Initialize() { if(IsInitialized()) { return; } if(DoInitialize()) { SetAvailable(); } SetInitialized(); } #if defined(MODPLUG_TRACKER) ComponentLibrary::ComponentLibrary(ComponentType type) : ComponentBase(type) , m_BindFailed(false) { return; } ComponentLibrary::~ComponentLibrary() { return; } bool ComponentLibrary::AddLibrary(const std::string &libName, const mpt::LibraryPath &libPath) { if(m_Libraries[libName].IsValid()) { // prefer previous return true; } mpt::Library lib(libPath); if(!lib.IsValid()) { return false; } m_Libraries[libName] = lib; return true; } void ComponentLibrary::ClearLibraries() { m_Libraries.clear(); } void ComponentLibrary::SetBindFailed() { m_BindFailed = true; } void ComponentLibrary::ClearBindFailed() { m_BindFailed = false; } bool ComponentLibrary::HasBindFailed() const { return m_BindFailed; } mpt::Library ComponentLibrary::GetLibrary(const std::string &libName) const { const auto it = m_Libraries.find(libName); if(it == m_Libraries.end()) { return mpt::Library(); } return it->second; } #endif // MODPLUG_TRACKER #if MPT_COMPONENT_MANAGER ComponentFactoryBase::ComponentFactoryBase(const std::string &id, const std::string &settingsKey) : m_ID(id) , m_SettingsKey(settingsKey) { return; } ComponentFactoryBase::~ComponentFactoryBase() { return; } std::string ComponentFactoryBase::GetID() const { return m_ID; } std::string ComponentFactoryBase::GetSettingsKey() const { return m_SettingsKey; } void ComponentFactoryBase::PreConstruct() const { MPT_LOG_GLOBAL(LogInformation, "Components", MPT_UFORMAT("Constructing Component {}") ( mpt::ToUnicode(mpt::Charset::ASCII, m_ID) ) ); } void ComponentFactoryBase::Initialize(ComponentManager &componentManager, std::shared_ptr component) const { if(componentManager.IsComponentBlocked(GetSettingsKey())) { return; } componentManager.InitializeComponent(component); } // Global list of component register functions. // We do not use a global scope static list head because the corresponding // mutex would be no POD type and would thus not be safe to be usable in // zero-initialized state. // Function scope static initialization is guaranteed to be thread safe // in C++11. // We use this implementation to be future-proof. // MSVC currently does not exploit the possibility of using multiple threads // for global lifetime object's initialization. // An implementation with a simple global list head and no mutex at all would // thus work fine for MSVC (currently). static mpt::mutex & ComponentListMutex() { static mpt::mutex g_ComponentListMutex; return g_ComponentListMutex; } static ComponentListEntry * & ComponentListHead() { static ComponentListEntry g_ComponentListHeadEmpty = {nullptr, nullptr}; static ComponentListEntry *g_ComponentListHead = &g_ComponentListHeadEmpty; return g_ComponentListHead; } bool ComponentListPush(ComponentListEntry *entry) { mpt::lock_guard guard(ComponentListMutex()); #if MPT_MSVC_BEFORE(2019,0) // Guard against VS2017 compiler bug causing repeated initialization of inline variables. // See . if(entry->next) { return false; } #endif entry->next = ComponentListHead(); ComponentListHead() = entry; return true; } static std::shared_ptr g_ComponentManager; void ComponentManager::Init(const IComponentManagerSettings &settings) { MPT_LOG_GLOBAL(LogInformation, "Components", U_("Init")); // cannot use make_shared because the constructor is private g_ComponentManager = std::shared_ptr(new ComponentManager(settings)); } void ComponentManager::Release() { MPT_LOG_GLOBAL(LogInformation, "Components", U_("Release")); g_ComponentManager = nullptr; } std::shared_ptr ComponentManager::Instance() { return g_ComponentManager; } ComponentManager::ComponentManager(const IComponentManagerSettings &settings) : m_Settings(settings) { mpt::lock_guard guard(ComponentListMutex()); for(ComponentListEntry *entry = ComponentListHead(); entry; entry = entry->next) { if(entry->reg) { entry->reg(*this); } } } void ComponentManager::Register(const IComponentFactory &componentFactory) { if(m_Components.find(componentFactory.GetID()) != m_Components.end()) { return; } RegisteredComponent registeredComponent; registeredComponent.settingsKey = componentFactory.GetSettingsKey(); registeredComponent.factoryMethod = componentFactory.GetStaticConstructor(); registeredComponent.instance = nullptr; registeredComponent.weakInstance = std::weak_ptr(); m_Components.insert(std::make_pair(componentFactory.GetID(), registeredComponent)); } void ComponentManager::Startup() { MPT_LOG_GLOBAL(LogDebug, "Components", U_("Startup")); if(m_Settings.LoadOnStartup()) { for(auto &it : m_Components) { it.second.instance = it.second.factoryMethod(*this); it.second.weakInstance = it.second.instance; } } if(!m_Settings.KeepLoaded()) { for(auto &it : m_Components) { it.second.instance = nullptr; } } } bool ComponentManager::IsComponentBlocked(const std::string &settingsKey) const { if(settingsKey.empty()) { return false; } return m_Settings.IsBlocked(settingsKey); } void ComponentManager::InitializeComponent(std::shared_ptr component) const { if(!component) { return; } if(component->IsInitialized()) { return; } component->Initialize(); } std::shared_ptr ComponentManager::GetComponent(const IComponentFactory &componentFactory) { std::shared_ptr component = nullptr; auto it = m_Components.find(componentFactory.GetID()); if(it != m_Components.end()) { // registered component if((*it).second.instance) { // loaded component = (*it).second.instance; } else { // not loaded component = (*it).second.weakInstance.lock(); if(!component) { component = (*it).second.factoryMethod(*this); } if(m_Settings.KeepLoaded()) { // keep the component loaded (*it).second.instance = component; } (*it).second.weakInstance = component; } } else { // unregistered component component = componentFactory.Construct(*this); } MPT_ASSERT(component); return component; } std::shared_ptr ComponentManager::ReloadComponent(const IComponentFactory &componentFactory) { std::shared_ptr component = nullptr; auto it = m_Components.find(componentFactory.GetID()); if(it != m_Components.end()) { // registered component if((*it).second.instance) { // loaded (*it).second.instance = nullptr; if(!(*it).second.weakInstance.expired()) { throw std::runtime_error("Component not completely unloaded. Cannot reload."); } (*it).second.weakInstance = std::weak_ptr(); } // not loaded component = (*it).second.factoryMethod(*this); if(m_Settings.KeepLoaded()) { // keep the component loaded (*it).second.instance = component; } (*it).second.weakInstance = component; } else { // unregistered component component = componentFactory.Construct(*this); } MPT_ASSERT(component); return component; } std::vector ComponentManager::GetRegisteredComponents() const { std::vector result; result.reserve(m_Components.size()); for(const auto &it : m_Components) { result.push_back(it.first); } return result; } ComponentInfo ComponentManager::GetComponentInfo(std::string name) const { ComponentInfo result; result.name = name; result.state = ComponentStateUnregistered; result.settingsKey = ""; result.type = ComponentTypeUnknown; const auto it = m_Components.find(name); if(it == m_Components.end()) { result.state = ComponentStateUnregistered; return result; } result.settingsKey = it->second.settingsKey; if(IsComponentBlocked(it->second.settingsKey)) { result.state = ComponentStateBlocked; return result; } std::shared_ptr component = it->second.instance; if(!component) { component = it->second.weakInstance.lock(); } if(!component) { result.state = ComponentStateUnintialized; return result; } result.type = component->GetType(); if(!component->IsInitialized()) { result.state = ComponentStateUnintialized; return result; } if(!component->IsAvailable()) { result.state = ComponentStateUnavailable; return result; } result.state = ComponentStateAvailable; return result; } mpt::PathString ComponentManager::GetComponentPath() const { return m_Settings.Path(); } #endif // MPT_COMPONENT_MANAGER OPENMPT_NAMESPACE_END