aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/common/ComponentManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/common/ComponentManager.cpp')
-rw-r--r--Src/external_dependencies/openmpt-trunk/common/ComponentManager.cpp475
1 files changed, 475 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/common/ComponentManager.cpp b/Src/external_dependencies/openmpt-trunk/common/ComponentManager.cpp
new file mode 100644
index 00000000..4c558ddc
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/common/ComponentManager.cpp
@@ -0,0 +1,475 @@
+/*
+ * 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<IComponent> 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<mpt::mutex> guard(ComponentListMutex());
+#if MPT_MSVC_BEFORE(2019,0)
+ // Guard against VS2017 compiler bug causing repeated initialization of inline variables.
+ // See <https://developercommunity.visualstudio.com/t/static-inline-variable-gets-destroyed-multiple-tim/297876>.
+ if(entry->next)
+ {
+ return false;
+ }
+#endif
+ entry->next = ComponentListHead();
+ ComponentListHead() = entry;
+ return true;
+}
+
+
+static std::shared_ptr<ComponentManager> 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<ComponentManager>(new ComponentManager(settings));
+}
+
+
+void ComponentManager::Release()
+{
+ MPT_LOG_GLOBAL(LogInformation, "Components", U_("Release"));
+ g_ComponentManager = nullptr;
+}
+
+
+std::shared_ptr<ComponentManager> ComponentManager::Instance()
+{
+ return g_ComponentManager;
+}
+
+
+ComponentManager::ComponentManager(const IComponentManagerSettings &settings)
+ : m_Settings(settings)
+{
+ mpt::lock_guard<mpt::mutex> 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<IComponent>();
+ 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<IComponent> component) const
+{
+ if(!component)
+ {
+ return;
+ }
+ if(component->IsInitialized())
+ {
+ return;
+ }
+ component->Initialize();
+}
+
+
+std::shared_ptr<const IComponent> ComponentManager::GetComponent(const IComponentFactory &componentFactory)
+{
+ std::shared_ptr<IComponent> 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<const IComponent> ComponentManager::ReloadComponent(const IComponentFactory &componentFactory)
+{
+ std::shared_ptr<IComponent> 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<IComponent>();
+ }
+ // 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<std::string> ComponentManager::GetRegisteredComponents() const
+{
+ std::vector<std::string> 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<IComponent> 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