123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- // Copyright (c) 2013-2018 Josh Blum
- // SPDX-License-Identifier: BSL-1.0
- #include <Pothos/Plugin/Registry.hpp>
- #include <Pothos/Plugin/Exception.hpp>
- #include <Pothos/Util/SpinLockRW.hpp>
- #include <Pothos/Callable.hpp> //gets call implementation
- #include <Poco/Logger.h>
- #include <cassert>
- #include <mutex>
- #include <map>
- /***********************************************************************
- * registry data structure
- **********************************************************************/
- static Pothos::Util::SpinLockRW &getRegistryMutex(void)
- {
- static Pothos::Util::SpinLockRW lock;
- return lock;
- }
- struct RegistryEntry
- {
- RegistryEntry(void):
- hasPlugin(false){}
- Pothos::Plugin plugin;
- bool hasPlugin;
- std::vector<std::string> nodeNamesOrdered; //so we know the order that they were added
- std::map<std::string, RegistryEntry> nodes;
- //count number of plugins here and deeper
- size_t getNumPlugins(void) const
- {
- size_t count = 0;
- if (this->hasPlugin) count++;
- for (const auto &entry : this->nodes)
- {
- count += entry.second.getNumPlugins();
- }
- return count;
- }
- };
- static RegistryEntry &getRegistryRoot(void)
- {
- static RegistryEntry regRoot;
- return regRoot;
- }
- /***********************************************************************
- * plugin event handler
- **********************************************************************/
- static bool canObjectHandleEvent(const Pothos::Object &obj)
- {
- if (obj.type() != typeid(Pothos::Callable)) return false; //its not a call
- const Pothos::Callable &call = obj.extract<Pothos::Callable>();
- if (not call) return false; //its null
- //check the signature
- if (call.type(-1) != typeid(void)) return false;
- if (call.getNumArgs() != 2) return false;
- if (call.type(0) != typeid(Pothos::Plugin)) return false;
- if (call.type(1) != typeid(std::string)) return false;
- return true;
- }
- static void callPluginEventHandler(const Pothos::Object &handler, const Pothos::Plugin &plugin, const std::string &event)
- {
- if (not canObjectHandleEvent(handler)) return;
- POTHOS_EXCEPTION_TRY
- {
- handler.extract<Pothos::Callable>().call(plugin, event);
- }
- POTHOS_EXCEPTION_CATCH(const Pothos::Exception &ex)
- {
- poco_error_f3(Poco::Logger::get("Pothos.PluginRegistry.handlePluginEvent"),
- "exception %s, plugin %s, event %s", ex.displayText(), plugin.toString(), event);
- }
- }
- static void handlePluginEvent(const Pothos::Plugin &plugin, const std::string &event)
- {
- const Pothos::PluginPath &path = plugin.getPath();
- std::vector<Pothos::Plugin> parentPlugins;
- //traverse the tree - store a list of parent plugins
- //we lock the mutex here for protection and make a plugin copy
- {
- Pothos::Util::SpinLockRW::SharedLock lock(getRegistryMutex());
- const std::vector<std::string> pathNodes = path.listNodes();
- RegistryEntry *root = &getRegistryRoot();
- for (size_t i = 0; i+1 < pathNodes.size(); i++)
- {
- parentPlugins.insert(parentPlugins.begin(), root->plugin);
- //next node in the tree at this node name
- root = &root->nodes[pathNodes[i]];
- }
- parentPlugins.insert(parentPlugins.begin(), root->plugin);
- }
- //traverse back up the plugin tree -- calling all potential handlers
- for (size_t i = 0; i < parentPlugins.size(); i++)
- {
- callPluginEventHandler(parentPlugins[i].getObject(), plugin, event);
- }
- }
- //if the plugin is an event handler, and it just got added,
- //then what we do is do the event add on all sub-tree plugins
- static void handleMissedSubTreeEvents(const Pothos::Object &handler, const Pothos::PluginPath &path)
- {
- for (const auto &subdir : Pothos::PluginRegistry::list(path))
- {
- auto subPath = path.join(subdir);
- try
- {
- callPluginEventHandler(handler, Pothos::PluginRegistry::get(subPath), "add");
- }
- catch(const Pothos::PluginRegistryError &)
- {
- }
- handleMissedSubTreeEvents(handler, subPath);
- }
- }
- /***********************************************************************
- * active module implementation
- **********************************************************************/
- static Pothos::PluginModule &getActiveModuleLoading(void)
- {
- static Pothos::PluginModule module;
- return module;
- }
- void registrySetActiveModuleLoading(const Pothos::PluginModule &module)
- {
- //made thread safe by lock in caller routine from Module.cpp
- getActiveModuleLoading() = module;
- }
- void updatePluginAssociation(const std::string &action, const Pothos::Plugin &plugin);
- /***********************************************************************
- * Registry implementation
- **********************************************************************/
- void Pothos::PluginRegistry::add(const Plugin &plugin_)
- {
- //copy the plugin -- we need an rw copy to attach module to
- Plugin plugin = plugin_;
- const PluginPath &path = plugin.getPath();
- poco_debug(Poco::Logger::get("Pothos.PluginRegistry.add"), plugin.toString());
- {
- std::lock_guard<Pothos::Util::SpinLockRW> lock(getRegistryMutex());
- const std::vector<std::string> pathNodes = path.listNodes();
- RegistryEntry *root = &getRegistryRoot();
- for (size_t i = 0; i < pathNodes.size(); i++)
- {
- auto it = std::find(root->nodeNamesOrdered.begin(), root->nodeNamesOrdered.end(), pathNodes[i]);
- if (it == root->nodeNamesOrdered.end()) root->nodeNamesOrdered.push_back(pathNodes[i]);
- //next node in the tree at this node name
- root = &root->nodes[pathNodes[i]];
- }
- //throw if the root already has a plugin
- if (root->hasPlugin)
- {
- const auto newModulePath = getActiveModuleLoading().getFilePath();
- const auto currentModulePath = root->plugin.getModule().getFilePath();
- throw Pothos::PluginRegistryError("Pothos::PluginRegistry::add("+path.toString()+")", Poco::format(
- "plugin already registered\n\tLoading: %s, Conflicts: %s", newModulePath, currentModulePath));
- }
- //store the plugin and attach the module
- plugin = Plugin(plugin.getPath(), plugin.getObject(), getActiveModuleLoading());
- updatePluginAssociation("add", plugin);
- root->hasPlugin = true;
- root->plugin = plugin;
- }
- handlePluginEvent(plugin, "add");
- handleMissedSubTreeEvents(plugin.getObject(), plugin.getPath());
- }
- Pothos::Plugin Pothos::PluginRegistry::get(const PluginPath &path)
- {
- Pothos::Util::SpinLockRW::SharedLock lock(getRegistryMutex());
- const std::vector<std::string> pathNodes = path.listNodes();
- RegistryEntry *root = &getRegistryRoot();
- for (size_t i = 0; i < pathNodes.size(); i++)
- {
- //next node in the tree at this node name
- root = &root->nodes[pathNodes[i]];
- }
- //throw if the root does not have a plugin
- if (not root->hasPlugin)
- {
- throw Pothos::PluginRegistryError("Pothos::PluginRegistry::get("+path.toString()+")", "plugin path not found");
- }
- return root->plugin;
- }
- Pothos::Plugin Pothos::PluginRegistry::remove(const PluginPath &path)
- {
- poco_debug(Poco::Logger::get("Pothos.PluginRegistry.remove"), path.toString());
- Plugin plugin;
- {
- std::lock_guard<Pothos::Util::SpinLockRW> lock(getRegistryMutex());
- const std::vector<std::string> pathNodes = path.listNodes();
- RegistryEntry *root = &getRegistryRoot();
- for (size_t i = 0; i < pathNodes.size(); i++)
- {
- //next node in the tree at this node name
- root = &root->nodes[pathNodes[i]];
- }
- //throw if the root does not have a plugin
- if (not root->hasPlugin)
- {
- throw Pothos::PluginRegistryError("Pothos::PluginRegistry::remove("+path.toString()+")", "plugin path not found");
- }
- //clear plugin entry and return result
- plugin = root->plugin;
- updatePluginAssociation("remove", plugin);
- root->hasPlugin = false;
- root->plugin = Plugin(); //clears
- }
- handlePluginEvent(plugin, "remove");
- return plugin;
- }
- bool Pothos::PluginRegistry::empty(const PluginPath &path)
- {
- Pothos::Util::SpinLockRW::SharedLock lock(getRegistryMutex());
- const std::vector<std::string> pathNodes = path.listNodes();
- RegistryEntry *root = &getRegistryRoot();
- for (size_t i = 0; i < pathNodes.size(); i++)
- {
- //next node in the tree at this node name
- root = &root->nodes[pathNodes[i]];
- }
- return not root->hasPlugin;
- }
- bool Pothos::PluginRegistry::exists(const PluginPath &path)
- {
- Pothos::Util::SpinLockRW::SharedLock lock(getRegistryMutex());
- const std::vector<std::string> pathNodes = path.listNodes();
- RegistryEntry *root = &getRegistryRoot();
- for (size_t i = 0; i < pathNodes.size(); i++)
- {
- //next node in the tree at this node name
- root = &root->nodes[pathNodes[i]];
- }
- return root->getNumPlugins() != 0;
- }
- std::vector<std::string> Pothos::PluginRegistry::list(const PluginPath &path)
- {
- Pothos::Util::SpinLockRW::SharedLock lock(getRegistryMutex());
- const std::vector<std::string> pathNodes = path.listNodes();
- RegistryEntry *root = &getRegistryRoot();
- for (size_t i = 0; i < pathNodes.size(); i++)
- {
- //next node in the tree at this node name
- root = &root->nodes[pathNodes[i]];
- }
- std::vector<std::string> nodes;
- for (const auto &name : root->nodeNamesOrdered)
- {
- if (root->nodes[name].getNumPlugins() != 0) nodes.push_back(name);
- }
- return nodes;
- }
- Pothos::PluginRegistryInfoDump::PluginRegistryInfoDump(void)
- {
- return;
- }
- static void loadInfoDump(const Pothos::PluginPath &path, const RegistryEntry &entry, Pothos::PluginRegistryInfoDump &dump)
- {
- dump.pluginPath = path.toString();
- if (entry.hasPlugin)
- {
- assert(path == entry.plugin.getPath());
- const auto &obj = entry.plugin.getObject();
- if (obj) dump.objectType = obj.toString();
- dump.modulePath = entry.plugin.getModule().getFilePath();
- dump.moduleVersion = entry.plugin.getModule().getVersion();
- }
- for (const auto &name : entry.nodeNamesOrdered)
- {
- dump.subInfo.push_back(Pothos::PluginRegistryInfoDump());
- loadInfoDump(path.join(name), entry.nodes.at(name), dump.subInfo.back());
- }
- }
- Pothos::PluginRegistryInfoDump Pothos::PluginRegistry::dump(void)
- {
- Pothos::Util::SpinLockRW::SharedLock lock(getRegistryMutex());
- PluginRegistryInfoDump dump;
- loadInfoDump(PluginPath(), getRegistryRoot(), dump);
- return dump;
- }
- #include <Pothos/Managed.hpp>
- static auto managedPluginRegistry = Pothos::ManagedClass()
- .registerClass<Pothos::PluginRegistry>()
- .registerStaticMethod("add", &Pothos::PluginRegistry::add<const Pothos::Plugin &>)
- .registerStaticMethod(POTHOS_FCN_TUPLE(Pothos::PluginRegistry, remove))
- .registerStaticMethod(POTHOS_FCN_TUPLE(Pothos::PluginRegistry, get))
- .registerStaticMethod(POTHOS_FCN_TUPLE(Pothos::PluginRegistry, empty))
- .registerStaticMethod(POTHOS_FCN_TUPLE(Pothos::PluginRegistry, exists))
- .registerStaticMethod(POTHOS_FCN_TUPLE(Pothos::PluginRegistry, list))
- .registerStaticMethod(POTHOS_FCN_TUPLE(Pothos::PluginRegistry, dump))
- .commit("Pothos/PluginRegistry");
- #include <Pothos/Object/Serialize.hpp>
- namespace Pothos { namespace serialization {
- template <class Archive>
- void serialize(Archive &ar, Pothos::PluginRegistryInfoDump &t, const unsigned int ver)
- {
- ar & t.pluginPath;
- ar & t.objectType;
- ar & t.modulePath;
- if (ver > 1) ar & t.moduleVersion;
- ar & t.subInfo;
- }
- }}
- POTHOS_OBJECT_SERIALIZE(Pothos::PluginRegistryInfoDump)
- POTHOS_OBJECT_SERIALIZE(std::vector<Pothos::PluginRegistryInfoDump>)
|