LCOV - code coverage report
Current view: top level - src/base - plugin.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 149 151 98.7 %
Date: 2026-07-26 14:05:51 Functions: 32 32 100.0 %
Branches: 150 268 56.0 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright (C) 2026 by Thun Lu. All rights reserved.
       3                 :            :  * Author: Thun Lu <thun.lu@zohomail.cn>
       4                 :            :  * Repo:   https://github.com/thun-res/vlink
       5                 :            :  *  _    __   __      _           __
       6                 :            :  * | |  / /  / /     (_) ____    / /__
       7                 :            :  * | | / /  / /     / / / __ \  / //_/
       8                 :            :  * | |/ /  / /___  / / / / / / / ,<
       9                 :            :  * |___/  /_____/ /_/ /_/ /_/ /_/|_|
      10                 :            :  *
      11                 :            :  * Licensed under the Apache License, Version 2.0 (the "License");
      12                 :            :  * you may not use this file except in compliance with the License.
      13                 :            :  * You may obtain a copy of the License at
      14                 :            :  *
      15                 :            :  *     http://www.apache.org/licenses/LICENSE-2.0
      16                 :            :  *
      17                 :            :  * Unless required by applicable law or agreed to in writing, software
      18                 :            :  * distributed under the License is distributed on an "AS IS" BASIS,
      19                 :            :  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      20                 :            :  * See the License for the specific language governing permissions and
      21                 :            :  * limitations under the License.
      22                 :            :  */
      23                 :            : 
      24                 :            : #include "./base/plugin.h"
      25                 :            : 
      26                 :            : #include <algorithm>
      27                 :            : #include <atomic>
      28                 :            : #include <cctype>
      29                 :            : #include <deque>
      30                 :            : #include <filesystem>
      31                 :            : #include <memory>
      32                 :            : #include <mutex>
      33                 :            : #include <shared_mutex>
      34                 :            : #include <stdexcept>
      35                 :            : #include <string>
      36                 :            : #include <string_view>
      37                 :            : #include <unordered_map>
      38                 :            : #include <utility>
      39                 :            : 
      40                 :            : #if defined(_WIN32) || defined(_WIN64)
      41                 :            : #ifndef WIN32_LEAN_AND_MEAN
      42                 :            : #define WIN32_LEAN_AND_MEAN
      43                 :            : #define VLINK_PLUGIN_UNDEFINE_LEAN_AND_MEAN
      44                 :            : #endif
      45                 :            : #ifndef NOMINMAX
      46                 :            : #define NOMINMAX
      47                 :            : #define VLINK_PLUGIN_UNDEFINE_NOMINMAX
      48                 :            : #endif
      49                 :            : #include <windows.h>
      50                 :            : #ifdef VLINK_PLUGIN_UNDEFINE_LEAN_AND_MEAN
      51                 :            : #undef WIN32_LEAN_AND_MEAN
      52                 :            : #undef VLINK_PLUGIN_UNDEFINE_LEAN_AND_MEAN
      53                 :            : #endif
      54                 :            : #ifdef VLINK_PLUGIN_UNDEFINE_NOMINMAX
      55                 :            : #undef NOMINMAX
      56                 :            : #undef VLINK_PLUGIN_UNDEFINE_NOMINMAX
      57                 :            : #endif
      58                 :            : #else
      59                 :            : #include <dlfcn.h>
      60                 :            : #endif
      61                 :            : 
      62                 :            : #include "./base/helpers.h"
      63                 :            : #include "./base/logger.h"
      64                 :            : #include "./base/utils.h"
      65                 :            : 
      66                 :            : namespace vlink {
      67                 :            : 
      68                 :         33 : [[maybe_unused]] static std::string get_current_dir() {
      69                 :            :   try {
      70   [ +  -  +  - ]:         66 :     return std::filesystem::current_path().string();
      71                 :            :   } catch (std::filesystem::filesystem_error&) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      72                 :            :     return ".";                                   // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      73                 :            :   }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      74                 :            : }
      75                 :            : 
      76                 :        184 : [[maybe_unused]] static bool check_regular_file(const std::string& path) {
      77                 :            :   try {
      78   [ +  -  +  - ]:        184 :     return std::filesystem::is_regular_file(path);
      79                 :            :   } catch (std::filesystem::filesystem_error&) {  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      80                 :            :     return false;                                 // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      81                 :            :   }  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
      82                 :            : }
      83                 :            : 
      84                 :            : // DynamicLibrary
      85                 :            : class DynamicLibrary final {
      86                 :            :  public:
      87                 :            : #if defined(_WIN32) || defined(_WIN64)
      88                 :            :   static constexpr const char* kFilenamePrefix = "";
      89                 :            :   static constexpr const char* kFilenameSuffix = ".dll";
      90                 :            :   using NativeHandle = HINSTANCE;
      91                 :            :   using NativeSymbol = FARPROC;
      92                 :            : #elif defined(__APPLE__)
      93                 :            :   static constexpr const char* kFilenamePrefix = "lib";
      94                 :            :   static constexpr const char* kFilenameSuffix = ".dylib";
      95                 :            :   using NativeHandle = void*;
      96                 :            :   using NativeSymbol = void*;
      97                 :            : #else
      98                 :            :   static constexpr const char* kFilenamePrefix = "lib";
      99                 :            :   static constexpr const char* kFilenameSuffix = ".so";
     100                 :            :   using NativeHandle = void*;
     101                 :            :   using NativeSymbol = void*;
     102                 :            : #endif
     103                 :            : 
     104                 :            :   static_assert(std::is_pointer_v<NativeHandle>, "Expecting native handle to be a pointer.");
     105                 :            :   static_assert(std::is_pointer_v<NativeSymbol>, "Expecting native symbol to be a pointer.");
     106                 :            : 
     107                 :            :   class Exception : public std::runtime_error {
     108                 :            :     using std::runtime_error::runtime_error;
     109                 :            :   };
     110                 :            : 
     111                 :            :   class LoadError : public Exception {
     112                 :            :     using Exception::Exception;
     113                 :            :   };
     114                 :            : 
     115                 :            :   class SymbolError : public Exception {
     116                 :            :     using Exception::Exception;
     117                 :            :   };
     118                 :            : 
     119                 :         18 :   explicit DynamicLibrary(const std::string& path) {
     120                 :         18 :     handle_ = open_library(path.c_str());
     121                 :            : 
     122         [ +  + ]:         18 :     if VUNLIKELY (!handle_) {
     123   [ +  -  +  -  :          1 :       throw LoadError("Could not load library \"" + path + "\"\n" + get_error_description());
             +  -  +  - ]
     124                 :            :     }
     125                 :         17 :   }
     126                 :            : 
     127                 :         17 :   ~DynamicLibrary() {
     128         [ +  - ]:         17 :     if VLIKELY (handle_) {
     129                 :         17 :       close_library(handle_);
     130                 :            :     }
     131                 :         17 :   }
     132                 :            : 
     133                 :            :   DynamicLibrary(DynamicLibrary&& other) noexcept : handle_(other.handle_) { other.handle_ = nullptr; }
     134                 :            : 
     135                 :            :   DynamicLibrary& operator=(DynamicLibrary&& other) noexcept {
     136                 :            :     if VLIKELY (this != &other) {
     137                 :            :       std::swap(handle_, other.handle_);
     138                 :            :     }
     139                 :            : 
     140                 :            :     return *this;
     141                 :            :   }
     142                 :            : 
     143                 :            :   template <typename SignatureT>
     144                 :         32 :   SignatureT* get_function(const std::string& name) const {
     145         [ -  + ]:         32 :     if VUNLIKELY (!handle_) {
     146         [ #  # ]:          0 :       throw std::logic_error("The dynamic library handle is null. This object may have been moved from.");
     147                 :            :     }
     148                 :            : 
     149                 :         32 :     auto* symbol = locate_symbol(handle_, name.c_str());
     150                 :            : 
     151         [ +  + ]:         32 :     if VUNLIKELY (!symbol) {
     152   [ +  -  +  -  :          1 :       throw SymbolError("Could not get symbol \"" + name + "\"\n" + get_error_description());
             +  -  +  - ]
     153                 :            :     }
     154                 :            : 
     155                 :            : #if defined(__GNUC__) && __GNUC__ >= 8
     156                 :            : #pragma GCC diagnostic push
     157                 :            : #pragma GCC diagnostic ignored "-Wcast-function-type"
     158                 :            : #endif
     159                 :            : 
     160                 :         31 :     return reinterpret_cast<SignatureT*>(symbol);
     161                 :            : 
     162                 :            : #if defined(__GNUC__) && __GNUC__ >= 8
     163                 :            : #pragma GCC diagnostic pop
     164                 :            : #endif
     165                 :            :   }
     166                 :            : 
     167                 :            :   NativeHandle native_handle() const noexcept { return handle_; }
     168                 :            : 
     169                 :            :  private:
     170                 :         18 :   static NativeHandle open_library(const char* path) noexcept {
     171                 :            : #if defined(_WIN32) || defined(_WIN64)
     172                 :            :     return ::LoadLibraryA(path);
     173                 :            : #else
     174                 :         18 :     return ::dlopen(path, RTLD_NOW | RTLD_LOCAL);
     175                 :            : #endif
     176                 :            :   }
     177                 :            : 
     178                 :         32 :   static NativeSymbol locate_symbol(NativeHandle lib, const char* name) noexcept {
     179                 :            : #if defined(_WIN32) || defined(_WIN64)
     180                 :            :     return ::GetProcAddress(lib, name);
     181                 :            : #else
     182                 :         32 :     return ::dlsym(lib, name);
     183                 :            : #endif
     184                 :            :   }
     185                 :            : 
     186                 :         17 :   static void close_library(NativeHandle lib) noexcept {
     187                 :            : #if defined(_WIN32) || defined(_WIN64)
     188                 :            :     ::FreeLibrary(lib);
     189                 :            : #else
     190                 :         17 :     ::dlclose(lib);
     191                 :            : #endif
     192                 :         17 :   }
     193                 :            : 
     194                 :          2 :   static std::string get_error_description() noexcept {
     195                 :            : #if defined(_WIN32) || defined(_WIN64)
     196                 :            :     static constexpr size_t kBufferSize = 512;
     197                 :            :     const auto error_code = ::GetLastError();
     198                 :            : 
     199                 :            :     if (!error_code) {
     200                 :            :       return "No error reported by GetLastError";
     201                 :            :     }
     202                 :            : 
     203                 :            :     char description[kBufferSize];
     204                 :            : 
     205                 :            :     const auto language = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
     206                 :            :     const DWORD length =
     207                 :            :         ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, error_code, language, description, kBufferSize, nullptr);
     208                 :            : 
     209                 :            :     return (length == 0) ? "Unknown error (FormatMessage failed)" : description;
     210                 :            : #else
     211                 :          2 :     const auto* description = ::dlerror();
     212                 :            : 
     213         [ +  - ]:          2 :     return (description == nullptr) ? "No error reported by dlerror" : description;
     214                 :            : #endif
     215                 :            :   }
     216                 :            : 
     217                 :            :   NativeHandle handle_{nullptr};
     218                 :            : 
     219                 :            :   VLINK_DISALLOW_COPY_AND_ASSIGN(DynamicLibrary)
     220                 :            : };
     221                 :            : 
     222                 :            : // PluginEntry
     223                 :            : struct PluginEntry final {
     224                 :            :   std::unique_ptr<DynamicLibrary> loader;
     225                 :            :   std::string plugin_complex_id;
     226                 :            :   Logger::Level log_level{Logger::kTrace};
     227                 :            : };
     228                 :            : 
     229                 :            : // Plugin::Impl
     230                 :            : struct Plugin::Impl final {
     231                 :            :   std::atomic<Logger::Level> log_level{Logger::kTrace};
     232                 :            :   std::shared_mutex mtx;
     233                 :            :   std::unordered_map<std::string, std::shared_ptr<PluginEntry>> plugin_map;
     234                 :            : };
     235                 :            : 
     236                 :            : // Plugin
     237                 :        198 : Plugin::Plugin() : impl_(std::make_unique<Impl>()) {}
     238                 :            : 
     239                 :          5 : void Plugin::set_log_level(Logger::Level level) { impl_->log_level.store(level, std::memory_order_release); }
     240                 :            : 
     241                 :          2 : Logger::Level Plugin::get_log_level() const { return impl_->log_level.load(std::memory_order_acquire); }
     242                 :            : 
     243                 :        198 : Plugin::~Plugin() = default;
     244                 :            : 
     245                 :         33 : std::deque<std::string> Plugin::default_search_path() {
     246         [ +  - ]:         33 :   const std::string current_dir = get_current_dir();
     247   [ +  -  +  - ]:         66 :   const std::string plugin_dir_env = Utils::get_env("VLINK_PLUGIN_DIR");
     248                 :         33 :   const std::string local_app_dir = Utils::get_app_dir();
     249                 :            : 
     250                 :            :   std::deque<std::string> search_path{
     251                 :            :       current_dir,
     252                 :            :       local_app_dir,
     253                 :            :       local_app_dir + "/../lib64",
     254                 :            :       local_app_dir + "/../lib",
     255                 :            :       local_app_dir + "/lib64",
     256                 :            :       local_app_dir + "/lib",
     257                 :            :       "/lib64",  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     258                 :            :       "/lib",
     259   [ +  -  +  -  :        363 :   };
          +  -  +  -  +  
          -  +  -  +  -  
             +  -  +  - ]
     260                 :            : 
     261                 :         33 :   const auto plugin_dirs = Helpers::split_any(plugin_dir_env);
     262                 :            : 
     263         [ +  + ]:         33 :   if (!plugin_dirs.empty()) {
     264         [ +  - ]:          1 :     search_path.insert(search_path.begin(), plugin_dirs.begin(), plugin_dirs.end());
     265                 :            :   }
     266                 :            : 
     267                 :         66 :   return search_path;
     268                 :         33 : }
     269                 :            : 
     270                 :         72 : void Plugin::clear() {
     271         [ +  - ]:         72 :   std::lock_guard lock(impl_->mtx);
     272                 :         72 :   impl_->plugin_map.clear();
     273                 :         72 : }
     274                 :            : 
     275                 :         39 : Plugin::Handle Plugin::load_and_create(const std::string& plugin_id, const std::string& lib_name,
     276                 :            :                                        uint16_t version_major, uint16_t version_minor, const std::string& dir_name,
     277                 :            :                                        const std::deque<std::string>& search_paths, const std::string& function_name,
     278                 :            :                                        std::shared_ptr<PluginEntry>* plugin_entry) {
     279                 :         39 :   const auto log_level = impl_->log_level.load(std::memory_order_acquire);
     280                 :            : 
     281         [ -  + ]:         39 :   if VUNLIKELY (plugin_id.empty()) {
     282                 :            :     if (log_level <= Logger::kError) {        // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     283                 :            :       VLOG_E("Plugin: Plugin id is empty.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     284                 :            :     }
     285                 :            : 
     286                 :            :     return nullptr;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     287                 :            :   }
     288                 :            : 
     289         [ +  + ]:         39 :   if VUNLIKELY (lib_name.empty()) {
     290         [ +  - ]:          1 :     if (log_level <= Logger::kError) {
     291   [ +  -  +  - ]:          2 :       VLOG_E("Plugin: Lib name is empty.");
     292                 :            :     }
     293                 :            : 
     294                 :          1 :     return nullptr;
     295                 :            :   }
     296                 :            : 
     297   [ +  -  +  - ]:         38 :   std::string plugin_complex_id = lib_name + "@" + plugin_id;
     298                 :            : 
     299                 :            :   {
     300         [ +  - ]:         38 :     std::unique_lock load_lock(impl_->mtx);
     301                 :            : 
     302   [ +  -  +  + ]:         38 :     if VUNLIKELY (impl_->plugin_map.count(plugin_complex_id) != 0U) {
     303         [ +  - ]:          1 :       if (log_level <= Logger::kError) {
     304   [ +  -  +  - ]:          2 :         VLOG_E("Plugin: Already loaded (", plugin_complex_id, ").");
     305                 :            :       }
     306                 :            : 
     307                 :          1 :       return nullptr;
     308                 :            :     }
     309         [ +  + ]:         38 :   }
     310                 :            : 
     311                 :         37 :   std::string plugin_path;
     312                 :            : 
     313                 :            :   {
     314                 :         37 :     bool has_filename_suffix = vlink::Helpers::has_endwith(lib_name, DynamicLibrary::kFilenameSuffix);
     315                 :            : #if defined(_WIN32) || defined(_WIN64)
     316                 :            :     const std::string_view filename_suffix = DynamicLibrary::kFilenameSuffix;
     317                 :            : 
     318                 :            :     if (!has_filename_suffix && lib_name.size() >= filename_suffix.size()) {
     319                 :            :       has_filename_suffix =
     320                 :            :           std::equal(filename_suffix.begin(), filename_suffix.end(), lib_name.end() - filename_suffix.size(),
     321                 :            :                      [](unsigned char lhs, unsigned char rhs) { return std::tolower(lhs) == std::tolower(rhs); });
     322                 :            :     }
     323                 :            : #endif
     324                 :         37 :     const bool has_path_separator = lib_name.find_first_of("/\\") != std::string::npos;
     325   [ +  +  -  + ]:         37 :     const bool path_like = has_path_separator || has_filename_suffix;
     326                 :            : 
     327         [ +  + ]:         37 :     if (path_like) {
     328   [ +  -  -  + ]:          2 :       if (check_regular_file(lib_name)) {
     329   [ #  #  #  #  :          0 :         plugin_path = has_path_separator ? lib_name : get_current_dir() + "/" + lib_name;
          #  #  #  #  #  
          #  #  #  #  #  
          #  #  #  #  #  
                      # ]
     330                 :            :       }
     331                 :            :     } else {
     332   [ +  -  +  - ]:         35 :       std::string plugin_name = DynamicLibrary::kFilenamePrefix + lib_name + DynamicLibrary::kFilenameSuffix;
     333                 :         35 :       std::string check_path;
     334                 :            : 
     335         [ +  + ]:        199 :       for (const auto& path : search_paths) {
     336         [ +  - ]:        182 :         check_path = path;
     337         [ +  - ]:        182 :         check_path += '/';
     338                 :            : 
     339         [ +  + ]:        182 :         if (!dir_name.empty()) {
     340         [ +  - ]:          1 :           check_path += dir_name;
     341         [ +  - ]:          1 :           check_path += '/';
     342                 :            :         }
     343                 :            : 
     344         [ +  - ]:        182 :         check_path += plugin_name;
     345                 :            : 
     346   [ +  -  +  + ]:        182 :         if (check_regular_file(check_path)) {
     347                 :         18 :           plugin_path = std::move(check_path);
     348                 :         18 :           break;
     349                 :            :         }
     350                 :            :       }
     351                 :         35 :     }
     352                 :            :   }
     353                 :            : 
     354                 :         37 :   Handle handle = nullptr;
     355                 :            : 
     356         [ +  + ]:         37 :   if VUNLIKELY (plugin_path.empty()) {
     357         [ +  + ]:         19 :     if (log_level <= Logger::kError) {
     358   [ +  -  +  - ]:         36 :       VLOG_E("Plugin: Cannot find plugin (", plugin_complex_id, ").");
     359                 :            :     }
     360                 :            : 
     361                 :         19 :     return handle;
     362                 :            :   }
     363                 :            : 
     364         [ +  + ]:         18 :   if (log_level <= Logger::kTrace) {
     365   [ +  -  +  - ]:         32 :     VLOG_T("Plugin: Loading plugin: ", plugin_path, ".");
     366                 :            :   }
     367                 :            : 
     368                 :            :   try {
     369         [ +  - ]:         18 :     auto entry = std::make_shared<PluginEntry>();
     370         [ +  + ]:         18 :     entry->loader = std::make_unique<DynamicLibrary>(std::move(plugin_path));
     371         [ +  - ]:         17 :     entry->plugin_complex_id = plugin_complex_id;
     372                 :         17 :     entry->log_level = log_level;
     373                 :            : 
     374                 :            :     auto create_function =
     375         [ +  + ]:         17 :         entry->loader->get_function<Handle(const char*, const char*, uint16_t, uint16_t, uint8_t)>(function_name);
     376                 :            : 
     377         [ -  + ]:         16 :     if VUNLIKELY (!create_function) {
     378                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     379                 :            :       if (log_level <= Logger::kError) {
     380                 :            :         VLOG_E("Plugin: Cannot find symbol function to create (", plugin_complex_id, ").");
     381                 :            :       }
     382                 :            : 
     383                 :            :       return handle;
     384                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     385                 :            :     }
     386                 :            : 
     387         [ +  - ]:         16 :     handle = create_function(lib_name.c_str(), plugin_id.c_str(), version_major, version_minor, log_level);
     388                 :            : 
     389         [ +  + ]:         16 :     if VUNLIKELY (!handle) {
     390         [ +  - ]:          1 :       if (log_level <= Logger::kError) {
     391   [ +  -  +  - ]:          2 :         VLOG_E("Plugin: Failed to create handle (", plugin_complex_id, ").");
     392                 :            :       }
     393                 :            : 
     394                 :          1 :       return handle;
     395                 :            :     }
     396                 :            : 
     397         [ +  + ]:         15 :     if (log_level <= Logger::kTrace) {
     398   [ +  -  +  - ]:         26 :       VLOG_T("Plugin: Loaded successfully (", plugin_complex_id, ").");
     399                 :            :     }
     400                 :            : 
     401                 :         15 :     bool inserted = false;
     402                 :            : 
     403                 :            :     {
     404         [ +  - ]:         15 :       std::unique_lock load_lock(impl_->mtx);
     405         [ +  - ]:         15 :       auto [iter, emplaced] = impl_->plugin_map.emplace(plugin_complex_id, entry);
     406                 :            :       (void)iter;
     407                 :         15 :       inserted = emplaced;
     408                 :         15 :     }
     409                 :            : 
     410         [ -  + ]:         15 :     if VUNLIKELY (!inserted) {
     411                 :            :       destroy(entry, handle);  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     412                 :            :       return nullptr;          // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     413                 :            :     }
     414                 :            : 
     415         [ +  - ]:         15 :     if (plugin_entry) {
     416                 :         15 :       *plugin_entry = entry;
     417                 :            :     }
     418                 :            : 
     419                 :         15 :     return handle;
     420         [ -  + ]:         20 :   } catch (const DynamicLibrary::Exception& e) {
     421         [ +  - ]:          2 :     if (log_level <= Logger::kError) {
     422   [ +  -  +  - ]:          4 :       VLOG_E("Plugin: Failed to load plugin (", plugin_complex_id, "): ", e.what(), ".");
     423                 :            :     }
     424                 :            : 
     425                 :          2 :     return handle;
     426                 :          2 :   }
     427                 :         38 : }
     428                 :            : 
     429                 :          4 : bool Plugin::unload(const std::string& plugin_complex_id) {
     430                 :          4 :   const auto log_level = impl_->log_level.load(std::memory_order_acquire);
     431                 :            : 
     432         [ -  + ]:          4 :   if VUNLIKELY (plugin_complex_id.empty()) {
     433                 :            :     if (log_level <= Logger::kError) {        // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     434                 :            :       VLOG_E("Plugin: Plugin id is empty.");  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     435                 :            :     }
     436                 :            : 
     437                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     438                 :            :   }
     439                 :            : 
     440                 :          4 :   bool erased = false;
     441                 :            : 
     442                 :            :   {
     443         [ +  - ]:          4 :     std::lock_guard lock(impl_->mtx);
     444         [ +  - ]:          4 :     erased = impl_->plugin_map.erase(plugin_complex_id) != 0U;
     445                 :          4 :   }
     446                 :            : 
     447         [ +  + ]:          4 :   if VUNLIKELY (!erased) {
     448         [ +  + ]:          2 :     if (log_level <= Logger::kError) {
     449   [ +  -  +  - ]:          2 :       VLOG_E("Plugin: Not loaded (", plugin_complex_id, ").");
     450                 :            :     }
     451                 :            : 
     452                 :          2 :     return false;
     453                 :            :   }
     454                 :            : 
     455                 :          2 :   return true;
     456                 :            : }
     457                 :            : 
     458                 :          6 : bool Plugin::has_loaded(const std::string& plugin_complex_id) {
     459         [ +  - ]:          6 :   std::shared_lock lock(impl_->mtx);
     460         [ +  - ]:         12 :   return impl_->plugin_map.count(plugin_complex_id) != 0;
     461                 :          6 : }
     462                 :            : 
     463                 :         15 : bool Plugin::destroy(std::shared_ptr<PluginEntry> plugin_entry, Handle handle, const std::string& function_name) {
     464   [ +  -  -  +  :         15 :   if VUNLIKELY (!plugin_entry || !plugin_entry->loader || !handle) {
          +  -  -  +  -  
                      + ]
     465                 :            :     return false;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     466                 :            :   }
     467                 :            : 
     468                 :            :   try {
     469         [ +  - ]:         15 :     auto destroy_function = plugin_entry->loader->get_function<bool(Handle)>(function_name);
     470                 :            : 
     471         [ -  + ]:         15 :     if VUNLIKELY (!destroy_function) {
     472                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     473                 :            :       if (plugin_entry->log_level <= Logger::kError) {
     474                 :            :         VLOG_E("Plugin: Cannot find symbol function to destroy (", plugin_entry->plugin_complex_id, ").");
     475                 :            :       }
     476                 :            : 
     477                 :            :       return false;
     478                 :            :       // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     479                 :            :     }
     480                 :            : 
     481   [ +  -  -  + ]:         15 :     if VUNLIKELY (!destroy_function(handle)) {
     482                 :            :       // LCOV_EXCL_START GCOVR_EXCL_START
     483                 :            :       if (plugin_entry->log_level <= Logger::kError) {
     484                 :            :         VLOG_E("Plugin: Failed to destroy handle (", plugin_entry->plugin_complex_id, ").");
     485                 :            :       }
     486                 :            : 
     487                 :            :       return false;
     488                 :            :     }
     489                 :            :   } catch (const std::exception& e) {
     490                 :            :     if (plugin_entry->log_level <= Logger::kError) {
     491                 :            :       VLOG_E("Plugin: Failed to destroy handle (", plugin_entry->plugin_complex_id, "): ", e.what(), ".");
     492                 :            :     }
     493                 :            : 
     494                 :            :     return false;
     495                 :            :   } catch (...) {
     496                 :            :     if (plugin_entry->log_level <= Logger::kError) {
     497                 :            :       VLOG_E("Plugin: Failed to destroy handle (", plugin_entry->plugin_complex_id, "): non-std exception.");
     498                 :            :     }
     499                 :            : 
     500                 :            :     return false;
     501                 :            :   }
     502                 :            :   // LCOV_EXCL_STOP GCOVR_EXCL_STOP
     503                 :            : 
     504                 :         15 :   return true;
     505                 :            : }
     506                 :            : 
     507                 :         24 : bool Plugin::process_plugin_internal(const std::string& lib_name, const std::string& local_plugin_id,
     508                 :            :                                      uint16_t local_version_major, uint16_t local_version_minor,
     509                 :            :                                      const std::string& target_plugin_id, uint16_t target_version_major,
     510                 :            :                                      uint16_t target_version_minor, uint8_t log_level) {
     511         [ +  + ]:         24 :   if (log_level <= Logger::kInfo) {
     512   [ +  -  +  - ]:         30 :     VLOG_I("Plugin: ", lib_name, "@", local_plugin_id, "#", local_version_major, ".", local_version_minor, ".");
     513                 :            :   }
     514                 :            : 
     515   [ +  +  +  +  :         24 :   if VUNLIKELY (target_plugin_id.empty() || target_plugin_id != local_plugin_id) {
                   +  + ]
     516         [ +  - ]:          3 :     if (log_level <= Logger::kError) {
     517   [ +  -  +  - ]:          6 :       VLOG_E("Plugin: Plugin id mismatch: expected '", local_plugin_id, "', got '", target_plugin_id, "'.");
     518                 :            :     }
     519                 :            : 
     520                 :          3 :     return false;
     521                 :            :   }
     522                 :            : 
     523   [ +  +  +  +  :         21 :   if VUNLIKELY (target_version_major != local_version_major || target_version_minor > local_version_minor) {
                   +  + ]
     524         [ +  - ]:          2 :     if (log_level <= Logger::kError) {
     525   [ +  -  +  - ]:          4 :       VLOG_E("Plugin: Version mismatch: local ", local_version_major, ".", local_version_minor, ", required ",
     526                 :            :              target_version_major, ".", target_version_minor, ".");
     527                 :            :     }
     528                 :            : 
     529                 :          2 :     return false;
     530                 :            :   }
     531                 :            : 
     532                 :         19 :   return true;
     533                 :            : }
     534                 :            : 
     535                 :            : }  // namespace vlink

Generated by: LCOV version 1.14