diff --git a/include/boost_plugin_loader/plugin_loader.h b/include/boost_plugin_loader/plugin_loader.h index 36d0f28..0e43e51 100644 --- a/include/boost_plugin_loader/plugin_loader.h +++ b/include/boost_plugin_loader/plugin_loader.h @@ -23,17 +23,17 @@ #include #include #include +#include +#include + +// Boost +#include /** @brief Macro for explicitly template instantiating a plugin loader for a given base class */ #define INSTANTIATE_PLUGIN_LOADER(PluginBase) \ template std::vector boost_plugin_loader::PluginLoader::getAvailablePlugins() const; \ template std::shared_ptr boost_plugin_loader::PluginLoader::createInstance(const std::string&) const; -namespace boost::dll -{ -class shared_library; -} - namespace boost_plugin_loader { @@ -75,6 +75,13 @@ struct has_getSection class PluginLoader { public: + PluginLoader() = default; + ~PluginLoader() = default; + inline PluginLoader(const PluginLoader& other); + inline PluginLoader& operator=(const PluginLoader& other); + inline PluginLoader(PluginLoader&& other) noexcept; + inline PluginLoader& operator=(PluginLoader&& other) noexcept; + /** @brief Indicate is system folders may be search if plugin is not found in any of the paths */ bool search_system_folders{ true }; @@ -146,7 +153,14 @@ class PluginLoader */ inline bool empty() const; + /** @brief Clear the internal cache of loaded plugin libraries */ + inline void clear(); + protected: + mutable std::mutex libraries_mutex_; + /** @brief Internal cache of loaded plugin libraries, stored by the path from which the library was loaded */ + mutable std::unordered_map libraries_; + template void reportErrorCommon(std::ostream& msg, const std::string& plugin_name, bool search_system_folders, const std::vector& search_paths, diff --git a/include/boost_plugin_loader/plugin_loader.hpp b/include/boost_plugin_loader/plugin_loader.hpp index 819f736..a359423 100644 --- a/include/boost_plugin_loader/plugin_loader.hpp +++ b/include/boost_plugin_loader/plugin_loader.hpp @@ -63,6 +63,56 @@ static std::shared_ptr createSharedInstance(const boost::dll::shared_ #endif } +PluginLoader::PluginLoader(const PluginLoader& other) + : search_system_folders(other.search_system_folders) + , search_paths(other.search_paths) + , search_libraries(other.search_libraries) + , search_paths_env(other.search_paths_env) + , search_libraries_env(other.search_libraries_env) +{ + std::scoped_lock lock{ libraries_mutex_, other.libraries_mutex_ }; + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + libraries_ = other.libraries_; +} + +PluginLoader& PluginLoader::operator=(const PluginLoader& other) +{ + search_system_folders = other.search_system_folders; + search_paths = other.search_paths; + search_libraries = other.search_libraries; + search_paths_env = other.search_paths_env; + search_libraries_env = other.search_libraries_env; + + std::scoped_lock lock{ libraries_mutex_, other.libraries_mutex_ }; + libraries_ = other.libraries_; + return *this; +} + +PluginLoader::PluginLoader(PluginLoader&& other) noexcept + : search_system_folders(other.search_system_folders) + , search_paths(std::move(other.search_paths)) + , search_libraries(std::move(other.search_libraries)) + , search_paths_env(std::move(other.search_paths_env)) + , search_libraries_env(std::move(other.search_libraries_env)) +{ + std::scoped_lock lock{ libraries_mutex_, other.libraries_mutex_ }; + // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) + libraries_ = std::move(other.libraries_); +} + +PluginLoader& PluginLoader::operator=(PluginLoader&& other) noexcept +{ + search_system_folders = other.search_system_folders; + search_paths = std::move(other.search_paths); + search_libraries = std::move(other.search_libraries); + search_paths_env = std::move(other.search_paths_env); + search_libraries_env = std::move(other.search_libraries_env); + + std::scoped_lock lock{ libraries_mutex_, other.libraries_mutex_ }; + libraries_ = std::move(other.libraries_); + return *this; +} + template typename std::enable_if::value, bool>::type PluginLoader::hasSymbol(const boost::dll::shared_library& lib, const std::string& symbol_name) const @@ -96,13 +146,15 @@ PluginLoader::hasSymbol(const boost::dll::shared_library& lib, const std::string * Libraries specified with absolute paths will be returned first in the list before libraries found in local paths (but * in no particular order in at the front of the list). */ -static std::vector loadLibraries(const std::vector& library_names, - const std::vector& search_paths_local, - const bool search_system_folders) +static std::vector +loadLibraries(const std::vector& library_names, const std::vector& search_paths_local, + const bool search_system_folders, std::unordered_map& cache) { std::vector libraries; + libraries.reserve(library_names.size()); // Loop over each provided library name + std::optional lib = std::nullopt; for (const std::string& library_name : library_names) { // First check if the library name is actually a complete, absolute path where the library is located @@ -111,7 +163,8 @@ static std::vector loadLibraries(const std::vector lib = loadLibrary(library_path); + auto it = cache.find(library_path.string()); + lib = (it != cache.end()) ? it->second : loadLibrary(library_path); // If the library exists, add it to the output list and continue to the next library name if (lib.has_value()) @@ -119,6 +172,11 @@ static std::vector loadLibraries(const std::vector loadLibraries(const std::vector lib = std::nullopt; for (const std::string& search_path : search_paths_local) { const boost::filesystem::path library_path = boost::filesystem::path(search_path) / library_name; - lib = loadLibrary(library_path); + + auto it = cache.find(library_path.string()); + lib = (it != cache.end()) ? it->second : loadLibrary(library_path); // If the library exists at this path, add the library to the output list and break out of the loop if (lib.has_value()) { libraries.push_back(lib.value()); + + // Add to cache if it did not exist + if (it == cache.end()) + cache[library_path.string()] = lib.value(); + break; } } @@ -144,11 +208,18 @@ static std::vector loadLibraries(const std::vectorsecond : loadLibrary(library_name); // Add the library to the output list, and break out of the loop if (lib.has_value()) + { libraries.push_back(lib.value()); + + // Add to cache if it did not exist + if (it == cache.end()) + cache[library_name] = lib.value(); + } } } @@ -210,8 +281,10 @@ std::shared_ptr PluginLoader::createInstance(const std::string& plug const std::vector search_paths_local = getAllSearchPaths(search_paths_env, search_paths); // Load the libraries - const std::vector libraries = - loadLibraries(library_names, search_paths_local, search_system_folders); + const std::vector libraries = [&]() { + std::scoped_lock lock(libraries_mutex_); + return loadLibraries(library_names, search_paths_local, search_system_folders, libraries_); + }(); // Create an instance of the plugin for (const auto& lib : libraries) @@ -236,8 +309,10 @@ bool PluginLoader::isPluginAvailable(const std::string& plugin_name) const const std::vector search_paths_local = getAllSearchPaths(search_paths_env, search_paths); // Load the libraries - const std::vector libraries = - loadLibraries(library_names, search_paths_local, search_system_folders); + const std::vector libraries = [&]() { + std::scoped_lock lock(libraries_mutex_); + return loadLibraries(library_names, search_paths_local, search_system_folders, libraries_); + }(); // Check for the symbol name return std::any_of(libraries.begin(), libraries.end(), [&](const auto& lib) { return lib.has(plugin_name); }); @@ -261,8 +336,10 @@ std::vector PluginLoader::getAvailablePlugins(const std::string& se const std::vector search_paths_local = getAllSearchPaths(search_paths_env, search_paths); // Load the libraries - const std::vector libraries = - loadLibraries(library_names, search_paths_local, search_system_folders); + const std::vector libraries = [&]() { + std::scoped_lock lock(libraries_mutex_); + return loadLibraries(library_names, search_paths_local, search_system_folders, libraries_); + }(); // Populate the list of plugins std::vector plugins; @@ -286,8 +363,10 @@ std::vector PluginLoader::getAvailableSections(bool include_hidden) const std::vector search_paths_local = getAllSearchPaths(search_paths_env, search_paths); // Load the libraries - const std::vector libraries = - loadLibraries(library_names, search_paths_local, search_system_folders); + const std::vector libraries = [&]() { + std::scoped_lock lock(libraries_mutex_); + return loadLibraries(library_names, search_paths_local, search_system_folders, libraries_); + }(); // Populate the list of sections std::vector sections; @@ -310,6 +389,12 @@ bool PluginLoader::empty() const return (count() == 0); } +void PluginLoader::clear() +{ + std::scoped_lock lock(libraries_mutex_); + libraries_.clear(); +} + } // namespace boost_plugin_loader #endif // BOOST_PLUGIN_LOADER_PLUGIN_LOADER_HPP