Registry.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. // Copyright (c) 2013-2018 Josh Blum
  2. // SPDX-License-Identifier: BSL-1.0
  3. #include <Pothos/Plugin/Registry.hpp>
  4. #include <Pothos/Plugin/Exception.hpp>
  5. #include <Pothos/Util/SpinLockRW.hpp>
  6. #include <Pothos/Callable.hpp> //gets call implementation
  7. #include <Poco/Logger.h>
  8. #include <cassert>
  9. #include <mutex>
  10. #include <map>
  11. /***********************************************************************
  12. * registry data structure
  13. **********************************************************************/
  14. static Pothos::Util::SpinLockRW &getRegistryMutex(void)
  15. {
  16. static Pothos::Util::SpinLockRW lock;
  17. return lock;
  18. }
  19. struct RegistryEntry
  20. {
  21. RegistryEntry(void):
  22. hasPlugin(false){}
  23. Pothos::Plugin plugin;
  24. bool hasPlugin;
  25. std::vector<std::string> nodeNamesOrdered; //so we know the order that they were added
  26. std::map<std::string, RegistryEntry> nodes;
  27. //count number of plugins here and deeper
  28. size_t getNumPlugins(void) const
  29. {
  30. size_t count = 0;
  31. if (this->hasPlugin) count++;
  32. for (const auto &entry : this->nodes)
  33. {
  34. count += entry.second.getNumPlugins();
  35. }
  36. return count;
  37. }
  38. };
  39. static RegistryEntry &getRegistryRoot(void)
  40. {
  41. static RegistryEntry regRoot;
  42. return regRoot;
  43. }
  44. /***********************************************************************
  45. * plugin event handler
  46. **********************************************************************/
  47. static bool canObjectHandleEvent(const Pothos::Object &obj)
  48. {
  49. if (obj.type() != typeid(Pothos::Callable)) return false; //its not a call
  50. const Pothos::Callable &call = obj.extract<Pothos::Callable>();
  51. if (not call) return false; //its null
  52. //check the signature
  53. if (call.type(-1) != typeid(void)) return false;
  54. if (call.getNumArgs() != 2) return false;
  55. if (call.type(0) != typeid(Pothos::Plugin)) return false;
  56. if (call.type(1) != typeid(std::string)) return false;
  57. return true;
  58. }
  59. static void callPluginEventHandler(const Pothos::Object &handler, const Pothos::Plugin &plugin, const std::string &event)
  60. {
  61. if (not canObjectHandleEvent(handler)) return;
  62. POTHOS_EXCEPTION_TRY
  63. {
  64. handler.extract<Pothos::Callable>().call(plugin, event);
  65. }
  66. POTHOS_EXCEPTION_CATCH(const Pothos::Exception &ex)
  67. {
  68. poco_error_f3(Poco::Logger::get("Pothos.PluginRegistry.handlePluginEvent"),
  69. "exception %s, plugin %s, event %s", ex.displayText(), plugin.toString(), event);
  70. }
  71. }
  72. static void handlePluginEvent(const Pothos::Plugin &plugin, const std::string &event)
  73. {
  74. const Pothos::PluginPath &path = plugin.getPath();
  75. std::vector<Pothos::Plugin> parentPlugins;
  76. //traverse the tree - store a list of parent plugins
  77. //we lock the mutex here for protection and make a plugin copy
  78. {
  79. Pothos::Util::SpinLockRW::SharedLock lock(getRegistryMutex());
  80. const std::vector<std::string> pathNodes = path.listNodes();
  81. RegistryEntry *root = &getRegistryRoot();
  82. for (size_t i = 0; i+1 < pathNodes.size(); i++)
  83. {
  84. parentPlugins.insert(parentPlugins.begin(), root->plugin);
  85. //next node in the tree at this node name
  86. root = &root->nodes[pathNodes[i]];
  87. }
  88. parentPlugins.insert(parentPlugins.begin(), root->plugin);
  89. }
  90. //traverse back up the plugin tree -- calling all potential handlers
  91. for (size_t i = 0; i < parentPlugins.size(); i++)
  92. {
  93. callPluginEventHandler(parentPlugins[i].getObject(), plugin, event);
  94. }
  95. }
  96. //if the plugin is an event handler, and it just got added,
  97. //then what we do is do the event add on all sub-tree plugins
  98. static void handleMissedSubTreeEvents(const Pothos::Object &handler, const Pothos::PluginPath &path)
  99. {
  100. for (const auto &subdir : Pothos::PluginRegistry::list(path))
  101. {
  102. auto subPath = path.join(subdir);
  103. try
  104. {
  105. callPluginEventHandler(handler, Pothos::PluginRegistry::get(subPath), "add");
  106. }
  107. catch(const Pothos::PluginRegistryError &)
  108. {
  109. }
  110. handleMissedSubTreeEvents(handler, subPath);
  111. }
  112. }
  113. /***********************************************************************
  114. * active module implementation
  115. **********************************************************************/
  116. static Pothos::PluginModule &getActiveModuleLoading(void)
  117. {
  118. static Pothos::PluginModule module;
  119. return module;
  120. }
  121. void registrySetActiveModuleLoading(const Pothos::PluginModule &module)
  122. {
  123. //made thread safe by lock in caller routine from Module.cpp
  124. getActiveModuleLoading() = module;
  125. }
  126. void updatePluginAssociation(const std::string &action, const Pothos::Plugin &plugin);
  127. /***********************************************************************
  128. * Registry implementation
  129. **********************************************************************/
  130. void Pothos::PluginRegistry::add(const Plugin &plugin_)
  131. {
  132. //copy the plugin -- we need an rw copy to attach module to
  133. Plugin plugin = plugin_;
  134. const PluginPath &path = plugin.getPath();
  135. poco_debug(Poco::Logger::get("Pothos.PluginRegistry.add"), plugin.toString());
  136. {
  137. std::lock_guard<Pothos::Util::SpinLockRW> lock(getRegistryMutex());
  138. const std::vector<std::string> pathNodes = path.listNodes();
  139. RegistryEntry *root = &getRegistryRoot();
  140. for (size_t i = 0; i < pathNodes.size(); i++)
  141. {
  142. auto it = std::find(root->nodeNamesOrdered.begin(), root->nodeNamesOrdered.end(), pathNodes[i]);
  143. if (it == root->nodeNamesOrdered.end()) root->nodeNamesOrdered.push_back(pathNodes[i]);
  144. //next node in the tree at this node name
  145. root = &root->nodes[pathNodes[i]];
  146. }
  147. //throw if the root already has a plugin
  148. if (root->hasPlugin)
  149. {
  150. const auto newModulePath = getActiveModuleLoading().getFilePath();
  151. const auto currentModulePath = root->plugin.getModule().getFilePath();
  152. throw Pothos::PluginRegistryError("Pothos::PluginRegistry::add("+path.toString()+")", Poco::format(
  153. "plugin already registered\n\tLoading: %s, Conflicts: %s", newModulePath, currentModulePath));
  154. }
  155. //store the plugin and attach the module
  156. plugin = Plugin(plugin.getPath(), plugin.getObject(), getActiveModuleLoading());
  157. updatePluginAssociation("add", plugin);
  158. root->hasPlugin = true;
  159. root->plugin = plugin;
  160. }
  161. handlePluginEvent(plugin, "add");
  162. handleMissedSubTreeEvents(plugin.getObject(), plugin.getPath());
  163. }
  164. Pothos::Plugin Pothos::PluginRegistry::get(const PluginPath &path)
  165. {
  166. Pothos::Util::SpinLockRW::SharedLock lock(getRegistryMutex());
  167. const std::vector<std::string> pathNodes = path.listNodes();
  168. RegistryEntry *root = &getRegistryRoot();
  169. for (size_t i = 0; i < pathNodes.size(); i++)
  170. {
  171. //next node in the tree at this node name
  172. root = &root->nodes[pathNodes[i]];
  173. }
  174. //throw if the root does not have a plugin
  175. if (not root->hasPlugin)
  176. {
  177. throw Pothos::PluginRegistryError("Pothos::PluginRegistry::get("+path.toString()+")", "plugin path not found");
  178. }
  179. return root->plugin;
  180. }
  181. Pothos::Plugin Pothos::PluginRegistry::remove(const PluginPath &path)
  182. {
  183. poco_debug(Poco::Logger::get("Pothos.PluginRegistry.remove"), path.toString());
  184. Plugin plugin;
  185. {
  186. std::lock_guard<Pothos::Util::SpinLockRW> lock(getRegistryMutex());
  187. const std::vector<std::string> pathNodes = path.listNodes();
  188. RegistryEntry *root = &getRegistryRoot();
  189. for (size_t i = 0; i < pathNodes.size(); i++)
  190. {
  191. //next node in the tree at this node name
  192. root = &root->nodes[pathNodes[i]];
  193. }
  194. //throw if the root does not have a plugin
  195. if (not root->hasPlugin)
  196. {
  197. throw Pothos::PluginRegistryError("Pothos::PluginRegistry::remove("+path.toString()+")", "plugin path not found");
  198. }
  199. //clear plugin entry and return result
  200. plugin = root->plugin;
  201. updatePluginAssociation("remove", plugin);
  202. root->hasPlugin = false;
  203. root->plugin = Plugin(); //clears
  204. }
  205. handlePluginEvent(plugin, "remove");
  206. return plugin;
  207. }
  208. bool Pothos::PluginRegistry::empty(const PluginPath &path)
  209. {
  210. Pothos::Util::SpinLockRW::SharedLock lock(getRegistryMutex());
  211. const std::vector<std::string> pathNodes = path.listNodes();
  212. RegistryEntry *root = &getRegistryRoot();
  213. for (size_t i = 0; i < pathNodes.size(); i++)
  214. {
  215. //next node in the tree at this node name
  216. root = &root->nodes[pathNodes[i]];
  217. }
  218. return not root->hasPlugin;
  219. }
  220. bool Pothos::PluginRegistry::exists(const PluginPath &path)
  221. {
  222. Pothos::Util::SpinLockRW::SharedLock lock(getRegistryMutex());
  223. const std::vector<std::string> pathNodes = path.listNodes();
  224. RegistryEntry *root = &getRegistryRoot();
  225. for (size_t i = 0; i < pathNodes.size(); i++)
  226. {
  227. //next node in the tree at this node name
  228. root = &root->nodes[pathNodes[i]];
  229. }
  230. return root->getNumPlugins() != 0;
  231. }
  232. std::vector<std::string> Pothos::PluginRegistry::list(const PluginPath &path)
  233. {
  234. Pothos::Util::SpinLockRW::SharedLock lock(getRegistryMutex());
  235. const std::vector<std::string> pathNodes = path.listNodes();
  236. RegistryEntry *root = &getRegistryRoot();
  237. for (size_t i = 0; i < pathNodes.size(); i++)
  238. {
  239. //next node in the tree at this node name
  240. root = &root->nodes[pathNodes[i]];
  241. }
  242. std::vector<std::string> nodes;
  243. for (const auto &name : root->nodeNamesOrdered)
  244. {
  245. if (root->nodes[name].getNumPlugins() != 0) nodes.push_back(name);
  246. }
  247. return nodes;
  248. }
  249. Pothos::PluginRegistryInfoDump::PluginRegistryInfoDump(void)
  250. {
  251. return;
  252. }
  253. static void loadInfoDump(const Pothos::PluginPath &path, const RegistryEntry &entry, Pothos::PluginRegistryInfoDump &dump)
  254. {
  255. dump.pluginPath = path.toString();
  256. if (entry.hasPlugin)
  257. {
  258. assert(path == entry.plugin.getPath());
  259. const auto &obj = entry.plugin.getObject();
  260. if (obj) dump.objectType = obj.toString();
  261. dump.modulePath = entry.plugin.getModule().getFilePath();
  262. dump.moduleVersion = entry.plugin.getModule().getVersion();
  263. }
  264. for (const auto &name : entry.nodeNamesOrdered)
  265. {
  266. dump.subInfo.push_back(Pothos::PluginRegistryInfoDump());
  267. loadInfoDump(path.join(name), entry.nodes.at(name), dump.subInfo.back());
  268. }
  269. }
  270. Pothos::PluginRegistryInfoDump Pothos::PluginRegistry::dump(void)
  271. {
  272. Pothos::Util::SpinLockRW::SharedLock lock(getRegistryMutex());
  273. PluginRegistryInfoDump dump;
  274. loadInfoDump(PluginPath(), getRegistryRoot(), dump);
  275. return dump;
  276. }
  277. #include <Pothos/Managed.hpp>
  278. static auto managedPluginRegistry = Pothos::ManagedClass()
  279. .registerClass<Pothos::PluginRegistry>()
  280. .registerStaticMethod("add", &Pothos::PluginRegistry::add<const Pothos::Plugin &>)
  281. .registerStaticMethod(POTHOS_FCN_TUPLE(Pothos::PluginRegistry, remove))
  282. .registerStaticMethod(POTHOS_FCN_TUPLE(Pothos::PluginRegistry, get))
  283. .registerStaticMethod(POTHOS_FCN_TUPLE(Pothos::PluginRegistry, empty))
  284. .registerStaticMethod(POTHOS_FCN_TUPLE(Pothos::PluginRegistry, exists))
  285. .registerStaticMethod(POTHOS_FCN_TUPLE(Pothos::PluginRegistry, list))
  286. .registerStaticMethod(POTHOS_FCN_TUPLE(Pothos::PluginRegistry, dump))
  287. .commit("Pothos/PluginRegistry");
  288. #include <Pothos/Object/Serialize.hpp>
  289. namespace Pothos { namespace serialization {
  290. template <class Archive>
  291. void serialize(Archive &ar, Pothos::PluginRegistryInfoDump &t, const unsigned int ver)
  292. {
  293. ar & t.pluginPath;
  294. ar & t.objectType;
  295. ar & t.modulePath;
  296. if (ver > 1) ar & t.moduleVersion;
  297. ar & t.subInfo;
  298. }
  299. }}
  300. POTHOS_OBJECT_SERIALIZE(Pothos::PluginRegistryInfoDump)
  301. POTHOS_OBJECT_SERIALIZE(std::vector<Pothos::PluginRegistryInfoDump>)