LCOV - code coverage report
Current view: top level - modules/shm - shm_factory.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 673 776 86.7 %
Date: 2026-09-30 18:30:16 Functions: 104 128 81.2 %
Branches: 461 904 51.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 "./shm_factory.h"
      25                 :            : 
      26                 :            : #include <charconv>
      27                 :            : #include <filesystem>
      28                 :            : #include <memory>
      29                 :            : #include <new>
      30                 :            : #include <string>
      31                 :            : #include <utility>
      32                 :            : #include <vector>
      33                 :            : 
      34                 :            : #ifdef __QNX__
      35                 :            : #include <filesystem>
      36                 :            : #define SHM_QNX_LOCK_DIR "/var/lock"
      37                 :            : #endif
      38                 :            : 
      39                 :            : #include "./base/utils.h"
      40                 :            : #include "./impl/server_impl.h"
      41                 :            : 
      42                 :            : #define SHM_USE_RUNTIME_IMPL 1
      43                 :            : 
      44                 :            : #include <iceoryx_posh/internal/roudi/roudi.hpp>
      45                 :            : #include <iceoryx_posh/internal/runtime/ipc_runtime_interface.hpp>
      46                 :            : #include <iceoryx_posh/internal/runtime/posh_runtime_impl.hpp>
      47                 :            : 
      48                 :            : #ifdef _WIN32
      49                 :            : #include <Windows.h>
      50                 :            : #undef min
      51                 :            : #undef max
      52                 :            : #undef GetMessage
      53                 :            : #endif
      54                 :            : 
      55                 :            : namespace vlink {
      56                 :            : 
      57                 :            : // ShmGlobal
      58                 :            : struct ShmGlobal final {
      59                 :            :   std::atomic_bool has_roudi_inited{false};
      60                 :            :   std::atomic_bool has_runtime_inited{false};
      61                 :            :   std::atomic_bool has_factory_inited{false};
      62                 :            :   class ShmRuntime* runtime_instance{nullptr};
      63                 :            :   std::unique_ptr<shm::runtime::PoshRuntime> shm_runtime;
      64                 :            :   shm::RuntimeName_t shm_runtime_name;
      65                 :            : #ifdef VLINK_SUPPORT_SHM_ROUDI
      66                 :            :   shm::cxx::optional<shm::roudi::IceOryxRouDiComponents> roudi_components;
      67                 :            :   shm::cxx::optional<shm::roudi::RouDi> roudi;
      68                 :            : #endif
      69                 :            : 
      70                 :        829 :   static ShmGlobal& get() {
      71                 :            : #ifdef _WIN32
      72                 :            :     static ShmGlobal* instance = new ShmGlobal();
      73                 :            :     return *instance;
      74                 :            : #else
      75   [ +  +  +  - ]:        829 :     static ShmGlobal instance;
      76                 :        829 :     return instance;
      77                 :            : #endif
      78                 :            :   }
      79                 :            : 
      80                 :            :  private:
      81                 :         13 :   ShmGlobal() = default;
      82                 :            : };
      83                 :            : 
      84                 :            : #if SHM_USE_RUNTIME_IMPL
      85                 :            : // ShmRuntime
      86                 :            : class ShmRuntime final : public shm::runtime::PoshRuntimeImpl {
      87                 :            :  public:
      88                 :         12 :   explicit ShmRuntime(const shm::RuntimeName_t& name, bool same_process_from_roudi = false)
      89                 :            : #if ICEORYX_VERSION_MAJOR == 2 && ICEORYX_VERSION_MINOR == 0
      90                 :         24 :       : shm::runtime::PoshRuntimeImpl(shm::cxx::make_optional<const shm::RuntimeName_t*>(&name),
      91                 :            :                                       same_process_from_roudi
      92                 :            :                                           ? shm::runtime::RuntimeLocation::SAME_PROCESS_LIKE_ROUDI
      93         [ +  + ]:         24 :                                           : shm::runtime::RuntimeLocation::SEPARATE_PROCESS_FROM_ROUDI)
      94                 :            : #else
      95                 :            :       : shm::runtime::PoshRuntimeImpl(shm::cxx::make_optional<const shm::RuntimeName_t*>(&name), shm::DEFAULT_DOMAIN_ID,
      96                 :            :                                       same_process_from_roudi
      97                 :            :                                           ? shm::runtime::RuntimeLocation::SAME_PROCESS_LIKE_ROUDI
      98                 :            :                                           : shm::runtime::RuntimeLocation::SEPARATE_PROCESS_FROM_ROUDI)
      99                 :            : #endif
     100                 :            :   {
     101                 :         12 :     ShmGlobal::get().runtime_instance = this;
     102                 :            : 
     103                 :         12 :     setRuntimeFactory(get_runtime_factory);
     104                 :         12 :   }
     105                 :            : 
     106                 :         24 :   ~ShmRuntime() override { ShmGlobal::get().runtime_instance = nullptr; }
     107                 :            : 
     108                 :        253 :   static PoshRuntime& get_runtime_factory(shm::cxx::optional<const shm::RuntimeName_t*>) {
     109                 :        253 :     return *ShmGlobal::get().runtime_instance;
     110                 :            :   }
     111                 :            : };
     112                 :            : #endif
     113                 :            : 
     114                 :            : // ShmFactory
     115   [ +  -  +  -  :         12 : ShmFactory::ShmFactory() {
                   +  - ]
     116   [ +  -  +  - ]:         12 :   static auto& global_instance = ShmGlobal::get();
     117                 :            : 
     118                 :         12 :   global_instance.has_factory_inited.store(true, std::memory_order_release);
     119                 :            : 
     120                 :         12 :   Bytes::init_memory_pool();
     121                 :            : 
     122         [ -  + ]:         12 :   if VUNLIKELY (ShmConf::get_thread_count() != 1) {
     123   [ #  #  #  #  :          0 :     VLOG_W("ShmFactory: Shm does not support setting thread count.");
                   #  # ]
     124                 :            :   }
     125                 :            : 
     126   [ +  -  +  - ]:         12 :   message_loop_.set_name("SHM-FACTORY");
     127                 :            : 
     128         [ +  - ]:         12 :   detect_timer_.attach(&message_loop_);
     129         [ +  - ]:         12 :   detect_timer_.set_interval(10);
     130         [ +  - ]:         12 :   detect_timer_.set_loop_count(Timer::kInfinite);
     131   [ +  -  +  - ]:         12 :   detect_timer_.set_callback([this]() {
     132         [ -  + ]:        408 :     if VUNLIKELY (!global_instance.has_runtime_inited.load(std::memory_order_acquire)) {
     133                 :          0 :       return;
     134                 :            :     }
     135                 :            : 
     136         [ +  - ]:        408 :     detect_timer_.set_interval(50);
     137                 :            : 
     138         [ +  + ]:        408 :     if (port_sub_) {
     139                 :         77 :       port_sub_->take().and_then(
     140                 :         12 :           [this](const shm::popo::Sample<const shm::roudi::PortIntrospectionFieldTopic>& sample) {
     141         [ +  - ]:          6 :             std::lock_guard lock(topic_mtx_);
     142                 :          6 :             topic_list_ = *sample;
     143                 :          6 :           });
     144                 :            :     }
     145                 :            : 
     146                 :        408 :     std::vector<DetectCallback> callbacks;
     147                 :            : 
     148                 :            :     {
     149         [ +  - ]:        408 :       std::shared_lock lock(detect_mtx_);
     150         [ +  - ]:        408 :       callbacks.reserve(detect_map_.size());
     151                 :            : 
     152         [ +  + ]:        840 :       for (const auto& [publisher, callback] : detect_map_) {
     153                 :            :         (void)publisher;
     154         [ +  - ]:        432 :         callbacks.emplace_back(callback);
     155                 :            :       }
     156                 :        408 :     }
     157                 :            : 
     158         [ +  + ]:        840 :     for (const auto& callback : callbacks) {
     159         [ +  - ]:        432 :       callback();
     160                 :            :     }
     161                 :        408 :   });
     162                 :            : 
     163         [ +  - ]:         12 :   message_loop_.async_run();
     164                 :            : 
     165         [ +  - ]:         12 :   init_log_level(true);
     166                 :            : 
     167   [ +  -  +  - ]:         12 :   init_runtime();
     168                 :            : 
     169         [ +  - ]:         12 :   init_log_level(false);
     170                 :            : 
     171                 :            :   {
     172   [ +  -  +  - ]:         24 :     std::string depth_env_str = Utils::get_env("VLINK_SHM_DEPTH");
     173                 :            : 
     174         [ -  + ]:         12 :     if (!depth_env_str.empty()) {
     175         [ #  # ]:          0 :       auto [p, error] = std::from_chars(depth_env_str.data(), depth_env_str.data() + depth_env_str.size(), sub_depth_);
     176                 :            : 
     177   [ #  #  #  #  :          0 :       if VUNLIKELY (error != std::errc() || sub_depth_ <= 0) {
                   #  # ]
     178                 :          0 :         sub_depth_ = kDefaultSubDepth;
     179                 :            :       }
     180                 :            :     }
     181                 :         12 :   }
     182                 :         12 : }
     183                 :            : 
     184                 :         12 : ShmFactory::~ShmFactory() {
     185                 :            : #ifdef _WIN32
     186                 :            :   if (Utils::is_terminating()) {
     187                 :            :     (void)::new (std::nothrow) auto(std::move(listener_map_));
     188                 :            :     return;
     189                 :            :   }
     190                 :            : #endif
     191                 :            : 
     192                 :         12 :   detect_timer_.stop();
     193                 :         12 :   detect_timer_.detach();
     194                 :            : 
     195                 :         12 :   message_loop_.quit();
     196                 :         12 :   message_loop_.wait_for_quit();
     197                 :            : 
     198                 :         12 :   deinit_runtime();
     199                 :         12 :   deinit_roudi();
     200                 :         12 : }
     201                 :            : 
     202                 :          3 : bool ShmFactory::has_roudi_inited() { return ShmGlobal::get().has_roudi_inited.load(std::memory_order_acquire); }
     203                 :            : 
     204                 :        496 : bool ShmFactory::has_runtime_inited() { return ShmGlobal::get().has_runtime_inited.load(std::memory_order_acquire); }
     205                 :            : 
     206                 :         13 : bool ShmFactory::has_roudi_running() {
     207                 :            : #if ICEORYX_VERSION_MAJOR == 2 && ICEORYX_VERSION_MINOR == 0
     208                 :            :   shm::runtime::IpcInterfaceUser roudi_ipc(
     209                 :         13 :       shm::RuntimeName_t(shm::cxx::TruncateToCapacity, shm::roudi::IPC_CHANNEL_ROUDI_NAME));
     210                 :            : 
     211                 :         13 :   return roudi_ipc.isInitialized();
     212                 :            : #else
     213                 :            :   shm::runtime::IpcInterfaceUser roudi_ipc(
     214                 :            :       shm::RuntimeName_t(shm::TruncateToCapacity, shm::roudi::IPC_CHANNEL_ROUDI_NAME), shm::DEFAULT_DOMAIN_ID,
     215                 :            :       shm::ResourceType::ICEORYX_DEFINED);
     216                 :            : 
     217                 :            :   return roudi_ipc.isInitialized();
     218                 :            : #endif
     219                 :         13 : }
     220                 :            : 
     221                 :         84 : bool ShmFactory::auto_init_roudi(bool same_process_from_roudi, int memory_strategy) {
     222                 :         84 :   Bytes::init_memory_pool();
     223                 :            : 
     224                 :            :   struct RoudiManager final {
     225                 :         11 :     RoudiManager(bool same_process_from_roudi, int memory_strategy) {
     226                 :         11 :       bool roudi_running = ShmFactory::has_roudi_running();
     227                 :            : 
     228         [ -  + ]:         11 :       if (!roudi_running) {
     229                 :            : #ifdef _WIN32
     230                 :            :         (void)memory_strategy;
     231                 :            : 
     232                 :            :         if (same_process_from_roudi) {
     233                 :            :           status_ = false;
     234                 :            :           return;
     235                 :            :         }
     236                 :            : #else
     237         [ #  # ]:          0 :         ShmFactory::init_roudi({}, memory_strategy);
     238                 :            : #endif
     239                 :            :       }
     240                 :            : 
     241                 :         11 :       ShmFactory::init_log_level(false);
     242   [ -  +  -  -  :         11 :       ShmFactory::init_runtime({}, !roudi_running && same_process_from_roudi);
                   +  - ]
     243                 :         11 :     }
     244                 :            : 
     245                 :         11 :     ~RoudiManager() {
     246                 :            : #ifdef _WIN32
     247                 :            :       if (Utils::is_terminating()) {
     248                 :            :         return;
     249                 :            :       }
     250                 :            : #endif
     251                 :            : 
     252         [ +  - ]:         11 :       if (status_) {
     253                 :         11 :         ShmFactory::deinit_runtime();
     254                 :         11 :         ShmFactory::deinit_roudi();
     255                 :            :       }
     256                 :         11 :     }
     257                 :            : 
     258                 :         84 :     bool status() const { return status_; }
     259                 :            : 
     260                 :            :    private:
     261                 :            :     bool status_{true};
     262                 :            :   };
     263                 :            : 
     264   [ +  +  +  -  :         84 :   static RoudiManager manager(same_process_from_roudi, memory_strategy);
             +  -  -  - ]
     265                 :            : 
     266                 :         84 :   return manager.status();
     267                 :            : }
     268                 :            : 
     269                 :        174 : shm::capro::ServiceDescription ShmFactory::get_description(const std::string& service, const std::string& instance,
     270                 :            :                                                            const std::string& event) {
     271                 :            : #if ICEORYX_VERSION_MAJOR == 2 && ICEORYX_VERSION_MINOR == 0
     272                 :        174 :   return {shm::capro::IdString_t(shm::cxx::TruncateToCapacity, service.c_str()),
     273                 :        174 :           shm::capro::IdString_t(shm::cxx::TruncateToCapacity, instance.c_str()),
     274                 :        174 :           shm::capro::IdString_t(shm::cxx::TruncateToCapacity, event.c_str())};
     275                 :            : #else
     276                 :            :   return {shm::capro::IdString_t(shm::TruncateToCapacity, service.c_str()),
     277                 :            :           shm::capro::IdString_t(shm::TruncateToCapacity, instance.c_str()),
     278                 :            :           shm::capro::IdString_t(shm::TruncateToCapacity, event.c_str())};
     279                 :            : #endif
     280                 :            : }
     281                 :            : 
     282                 :         36 : void ShmFactory::init_log_level(bool wait_roudi) {
     283                 :            : #if ICEORYX_VERSION_MAJOR == 2 && ICEORYX_VERSION_MINOR == 0
     284                 :         36 :   auto& log_manager = shm::log::LogManager::GetLogManager();
     285                 :            : 
     286         [ +  + ]:         36 :   if (wait_roudi) {
     287                 :         12 :     log_manager.SetDefaultLogLevel(shm::log::LogLevel::kWarn, shm::log::LogLevelOutput::kHideLogLevel);
     288                 :         12 :     return;
     289                 :            :   }
     290                 :            : 
     291   [ +  +  +  -  :         24 :   static std::string shm_debug_str = Utils::get_env("VLINK_SHM_DEBUG");
          +  -  +  -  -  
                      - ]
     292                 :            : 
     293         [ -  + ]:         24 :   if (shm_debug_str == "1") {
     294                 :          0 :     log_manager.SetDefaultLogLevel(shm::log::LogLevel::kInfo, shm::log::LogLevelOutput::kHideLogLevel);
     295                 :            :   } else {
     296                 :         24 :     log_manager.SetDefaultLogLevel(shm::log::LogLevel::kFatal, shm::log::LogLevelOutput::kHideLogLevel);
     297                 :            :   }
     298                 :            : #else
     299                 :            : 
     300                 :            :   if (wait_roudi) {
     301                 :            :     shm::log::Logger::setLogLevel(shm::log::LogLevel::Warn);
     302                 :            :     return;
     303                 :            :   }
     304                 :            : 
     305                 :            :   static std::string shm_debug_str = Utils::get_env("VLINK_SHM_DEBUG");
     306                 :            : 
     307                 :            :   if (shm_debug_str == "1") {
     308                 :            :     shm::log::Logger::setLogLevel(shm::log::LogLevel::Info);
     309                 :            :   } else {
     310                 :            :     shm::log::Logger::setLogLevel(shm::log::LogLevel::Fatal);
     311                 :            :   }
     312                 :            : #endif
     313                 :            : }
     314                 :            : 
     315                 :          1 : void ShmFactory::init_roudi(const std::string& config_path, int memory_strategy, bool monitoring_enable) {
     316                 :          1 :   Bytes::init_memory_pool();
     317                 :            : 
     318                 :            : #ifdef VLINK_SUPPORT_SHM_ROUDI
     319                 :            : 
     320                 :            : #if __QNX__
     321                 :            :   try {
     322                 :            :     if (!std::filesystem::exists(SHM_QNX_LOCK_DIR)) {
     323                 :            :       std::filesystem::create_directories(SHM_QNX_LOCK_DIR);
     324                 :            :     }
     325                 :            :   } catch (std::filesystem::filesystem_error&) {
     326                 :            :   }
     327                 :            : #endif
     328                 :            : 
     329                 :          1 :   bool expected = false;
     330                 :            : 
     331         [ -  + ]:          1 :   if VUNLIKELY (!ShmGlobal::get().has_roudi_inited.compare_exchange_strong(expected, true, std::memory_order_acq_rel,
     332                 :            :                                                                            std::memory_order_relaxed)) {
     333                 :          0 :     return;
     334                 :            :   }
     335                 :            : 
     336         [ +  - ]:          1 :   init_log_level(false);
     337                 :            : 
     338   [ +  -  +  -  :          2 :   VLOG_I("ShmFactory: Start shm roudi.");
                   +  - ]
     339                 :            : 
     340                 :          1 :   shm::RouDiConfig_t config;
     341                 :            : 
     342         [ -  + ]:          1 :   if (!config_path.empty()) {
     343                 :          0 :     shm::config::CmdLineArgs_t cmd;
     344                 :            : 
     345                 :            : #if ICEORYX_VERSION_MAJOR == 2 && ICEORYX_VERSION_MINOR == 0
     346                 :          0 :     cmd.configFilePath = shm::roudi::ConfigFilePathString_t(shm::cxx::TruncateToCapacity, config_path.c_str());
     347                 :            : #else
     348                 :            :     cmd.configFilePath = shm::roudi::ConfigFilePathString_t(shm::TruncateToCapacity, config_path.c_str());
     349                 :            : #endif
     350                 :            : 
     351                 :          0 :     shm::config::TomlRouDiConfigFileProvider provider(cmd);
     352                 :          0 :     config = provider.parse().value();
     353                 :          0 :   } else {
     354                 :          1 :     shm::mepoo::MePooConfig poo_config;
     355                 :            : 
     356         [ -  + ]:          1 :     if (memory_strategy == 1) {
     357                 :          0 :       poo_config.addMemPool({1024, 1000});
     358                 :          0 :       poo_config.addMemPool({16384, 500});
     359                 :          0 :       poo_config.addMemPool({131072, 100});
     360                 :          0 :       poo_config.addMemPool({1048576, 10});
     361         [ -  + ]:          1 :     } else if (memory_strategy == 2) {
     362                 :          0 :       poo_config.addMemPool({1024, 5000});
     363                 :          0 :       poo_config.addMemPool({16384, 1000});
     364                 :          0 :       poo_config.addMemPool({131072, 100});
     365                 :          0 :       poo_config.addMemPool({1048576, 20});
     366                 :          0 :       poo_config.addMemPool({4194304, 10});
     367                 :          0 :       poo_config.addMemPool({8388608, 5});
     368         [ -  + ]:          1 :     } else if (memory_strategy == 4) {
     369                 :          0 :       poo_config.addMemPool({1024, 10000});
     370                 :          0 :       poo_config.addMemPool({16384, 1000});
     371                 :          0 :       poo_config.addMemPool({131072, 500});
     372                 :          0 :       poo_config.addMemPool({1048576, 200});
     373                 :          0 :       poo_config.addMemPool({3145728, 100});
     374                 :          0 :       poo_config.addMemPool({6291456, 50});
     375                 :          0 :       poo_config.addMemPool({13631488, 30});
     376                 :          0 :       poo_config.addMemPool({25165824, 20});
     377                 :            :     } else {  // default
     378                 :          1 :       poo_config.addMemPool({1024, 10000});
     379                 :          1 :       poo_config.addMemPool({16384, 1000});
     380                 :          1 :       poo_config.addMemPool({131072, 500});
     381                 :          1 :       poo_config.addMemPool({1048576, 100});
     382                 :          1 :       poo_config.addMemPool({3145728, 50});
     383                 :          1 :       poo_config.addMemPool({6291456, 30});
     384                 :          1 :       poo_config.addMemPool({13631488, 20});
     385                 :            :     }
     386                 :            : 
     387                 :            : #if ICEORYX_VERSION_MAJOR == 2 && ICEORYX_VERSION_MINOR == 0
     388                 :          1 :     auto group_name = shm::posix::PosixGroup::getGroupOfCurrentProcess().getName();
     389                 :            : #else
     390                 :            :     auto group_name = shm::PosixGroup::getGroupOfCurrentProcess().getName();
     391                 :            : #endif
     392                 :            : 
     393                 :          1 :     config.m_sharedMemorySegments.emplace_back(group_name, group_name, std::move(poo_config));
     394                 :          1 :   }
     395                 :            : 
     396                 :            : #if ICEORYX_VERSION_MAJOR == 2 && ICEORYX_VERSION_MINOR == 0
     397                 :            :   shm::roudi::RouDi::RoudiStartupParameters parameters{
     398                 :            :       monitoring_enable ? shm::roudi::MonitoringMode::ON : shm::roudi::MonitoringMode::OFF, true,
     399                 :            :       shm::roudi::RouDi::RuntimeMessagesThreadStart::IMMEDIATE, shm::version::CompatibilityCheckLevel::PATCH,
     400         [ -  + ]:          1 :       shm::units::Duration::fromSeconds(10)};
     401                 :            : #else
     402                 :            :   shm::config::RouDiConfig parameters;
     403                 :            :   parameters.sharesAddressSpaceWithApplications = true;
     404                 :            :   parameters.monitoringMode = monitoring_enable ? shm::roudi::MonitoringMode::ON : shm::roudi::MonitoringMode::OFF;
     405                 :            : #endif
     406                 :            : 
     407                 :          1 :   ShmGlobal::get().roudi_components.emplace(config);
     408                 :          2 :   ShmGlobal::get().roudi.emplace(ShmGlobal::get().roudi_components.value().rouDiMemoryManager,
     409                 :          1 :                                  ShmGlobal::get().roudi_components.value().portManager, parameters);
     410                 :            : #else
     411                 :            :   (void)config_path;
     412                 :            :   (void)memory_strategy;
     413                 :            :   (void)monitoring_enable;
     414                 :            : 
     415                 :            :   VLOG_F("ShmFactory: Shm roudi is not supported.");
     416                 :            : #endif
     417                 :          1 : }
     418                 :            : 
     419                 :         24 : void ShmFactory::init_runtime(std::string name, bool same_process_from_roudi) {
     420                 :         24 :   Bytes::init_memory_pool();
     421                 :            : 
     422   [ +  +  +  - ]:         24 :   static auto& global_instance = ShmGlobal::get();
     423                 :            : 
     424                 :         24 :   bool expected = false;
     425                 :            : 
     426         [ +  + ]:         24 :   if VLIKELY (global_instance.has_runtime_inited.compare_exchange_strong(expected, true, std::memory_order_acq_rel,
     427                 :            :                                                                          std::memory_order_relaxed)) {
     428         [ +  + ]:         12 :     if (name.empty()) {
     429   [ +  -  +  - ]:         11 :       name = Utils::get_app_name() + "_" + Utils::get_pid_str();
     430                 :            :     }
     431                 :            : 
     432                 :            : #if ICEORYX_VERSION_MAJOR == 2 && ICEORYX_VERSION_MINOR == 0
     433                 :         12 :     global_instance.shm_runtime_name = shm::RuntimeName_t(shm::cxx::TruncateToCapacity, name.c_str());
     434                 :            : #else
     435                 :            :     global_instance.shm_runtime_name = shm::RuntimeName_t(shm::TruncateToCapacity, name.c_str());
     436                 :            : #endif
     437                 :            : 
     438                 :            : #if SHM_USE_RUNTIME_IMPL
     439                 :         12 :     global_instance.shm_runtime =
     440         [ +  - ]:         12 :         std::make_unique<ShmRuntime>(global_instance.shm_runtime_name, same_process_from_roudi);
     441                 :            : #else
     442                 :            :     (void)same_process_from_roudi;
     443                 :            :     shm::runtime::PoshRuntime::initRuntime(global_instance.shm_runtime_name);
     444                 :            : #endif
     445                 :            :   }
     446                 :         24 : }
     447                 :            : 
     448                 :         23 : void ShmFactory::deinit_runtime() {
     449   [ +  +  +  - ]:         23 :   static auto& global_instance = ShmGlobal::get();
     450                 :            : 
     451                 :         23 :   bool expected = true;
     452                 :            : 
     453         [ +  + ]:         23 :   if VLIKELY (global_instance.has_runtime_inited.compare_exchange_strong(expected, false, std::memory_order_acq_rel,
     454                 :            :                                                                          std::memory_order_relaxed)) {
     455         [ +  - ]:         12 :     if (global_instance.has_factory_inited.load(std::memory_order_acquire)) {
     456         [ +  - ]:         12 :       auto& factory = ShmFactory::get();
     457                 :            : 
     458                 :            :       {
     459         [ +  - ]:         12 :         std::lock_guard lock(factory.listener_mtx_);
     460                 :         12 :         factory.listener_map_.clear();
     461                 :         12 :       }
     462                 :            : 
     463                 :         12 :       factory.port_sub_.reset();
     464                 :            : 
     465                 :         12 :       Utils::yield_cpu();
     466                 :            :     }
     467                 :            : 
     468                 :            : #if SHM_USE_RUNTIME_IMPL
     469                 :            : 
     470         [ +  - ]:         12 :     if (global_instance.shm_runtime) {
     471                 :         12 :       global_instance.shm_runtime.reset();
     472                 :            :     }
     473                 :            : #endif
     474                 :            :   }
     475                 :         23 : }
     476                 :            : 
     477                 :         23 : void ShmFactory::deinit_roudi() {
     478                 :            : #ifdef VLINK_SUPPORT_SHM_ROUDI
     479   [ +  +  +  - ]:         23 :   static auto& global_instance = ShmGlobal::get();
     480                 :            : 
     481                 :         23 :   bool expected = true;
     482                 :            : 
     483         [ +  + ]:         23 :   if VLIKELY (global_instance.has_roudi_inited.compare_exchange_strong(expected, false, std::memory_order_acq_rel,
     484                 :            :                                                                        std::memory_order_relaxed)) {
     485                 :          1 :     global_instance.roudi.reset();
     486                 :          1 :     global_instance.roudi_components.reset();
     487                 :            :   }
     488                 :            : #endif
     489                 :         23 : }
     490                 :            : 
     491                 :        111 : std::shared_ptr<shm::popo::Listener> ShmFactory::get_listener(int32_t domain) {
     492         [ +  - ]:        111 :   std::lock_guard lock(listener_mtx_);
     493                 :            : 
     494         [ +  - ]:        111 :   auto& entry = listener_map_[domain];
     495                 :        111 :   auto listener = entry.lock();
     496                 :            : 
     497         [ +  + ]:        111 :   if (!listener) {
     498         [ +  - ]:         78 :     listener = std::make_shared<shm::popo::Listener>();
     499                 :         78 :     entry = listener;
     500                 :            :   }
     501                 :            : 
     502                 :        222 :   return listener;
     503                 :        111 : }
     504                 :            : 
     505                 :         62 : void ShmFactory::add_detect_callback(void* node, DetectCallback&& callback) {
     506         [ +  - ]:         62 :   std::lock_guard lock(detect_mtx_);
     507                 :            : 
     508         [ +  + ]:         62 :   if (detect_map_.empty()) {
     509         [ +  - ]:         58 :     detect_timer_.restart();
     510                 :            :   }
     511                 :            : 
     512   [ +  -  +  - ]:         62 :   detect_map_[node] = callback;
     513                 :            : 
     514   [ +  -  +  - ]:        124 :   message_loop_.post_task([callback = std::move(callback)]() { callback(); });
     515                 :         62 : }
     516                 :            : 
     517                 :         62 : void ShmFactory::remove_detect_callback(void* node) {
     518         [ +  - ]:         62 :   std::lock_guard lock(detect_mtx_);
     519                 :            : 
     520         [ +  - ]:         62 :   detect_map_.erase(node);
     521                 :            : 
     522         [ +  + ]:         62 :   if (detect_map_.empty()) {
     523         [ +  - ]:         58 :     detect_timer_.stop();
     524                 :            :   }
     525                 :         62 : }
     526                 :            : 
     527                 :          1 : void ShmFactory::start_detect_node_count() {
     528         [ +  - ]:          1 :   if (!port_sub_) {
     529                 :          1 :     shm::popo::SubscriberOptions port_sub_opt;
     530                 :          1 :     port_sub_opt.queueCapacity = 1U;
     531                 :          1 :     port_sub_opt.historyRequest = 1U;
     532                 :          1 :     port_sub_.emplace(shm::roudi::IntrospectionPortService, port_sub_opt);
     533                 :            :   }
     534                 :            : 
     535                 :          1 :   detect_timer_.restart();
     536                 :          1 : }
     537                 :            : 
     538                 :          0 : uint64_t ShmFactory::get_publisher_count(const shm::capro::ServiceDescription& description) {
     539         [ #  # ]:          0 :   std::shared_lock lock(topic_mtx_);
     540                 :            : 
     541                 :          0 :   uint64_t count = 0;
     542                 :            : 
     543         [ #  # ]:          0 :   for (const auto& info : topic_list_.m_publisherList) {
     544         [ #  # ]:          0 :     if (info.m_caproServiceID == description.getServiceIDString() &&
     545   [ #  #  #  #  :          0 :         info.m_caproInstanceID == description.getInstanceIDString() &&
                   #  # ]
     546                 :          0 :         info.m_caproEventMethodID == description.getEventIDString()) {
     547                 :          0 :       ++count;
     548                 :            :     }
     549                 :            :   }
     550                 :            : 
     551                 :          0 :   return count;
     552                 :          0 : }
     553                 :            : 
     554                 :          1 : uint64_t ShmFactory::get_subscriber_count(const shm::capro::ServiceDescription& description) {
     555         [ +  - ]:          1 :   std::shared_lock lock(topic_mtx_);
     556                 :            : 
     557                 :          1 :   uint64_t count = 0;
     558                 :            : 
     559         [ +  + ]:          2 :   for (const auto& info : topic_list_.m_subscriberList) {
     560         [ -  + ]:          2 :     if (info.m_caproServiceID == description.getServiceIDString() &&
     561   [ +  -  -  -  :          2 :         info.m_caproInstanceID == description.getInstanceIDString() &&
                   -  + ]
     562                 :          0 :         info.m_caproEventMethodID == description.getEventIDString()) {
     563                 :          0 :       ++count;
     564                 :            :     }
     565                 :            :   }
     566                 :            : 
     567                 :          1 :   return count;
     568                 :          1 : }
     569                 :            : 
     570                 :         59 : int ShmFactory::get_sub_depth() const { return sub_depth_; }
     571                 :            : 
     572                 :            : // ShmServer
     573                 :         24 : ShmServer::ShmServer(const ShmID& id) {
     574   [ +  +  +  -  :         24 :   static auto& factory = ShmFactory::get();
             +  -  -  - ]
     575                 :            : 
     576                 :         24 :   const auto& [impl_type, address, domain, depth, history, wait, method, owner] = id;
     577                 :            : 
     578                 :         24 :   domain_ = domain;
     579                 :            : 
     580                 :         24 :   shm::popo::ServerOptions options;
     581                 :         24 :   options.offerOnCreate = false;
     582                 :            : 
     583         [ +  + ]:         24 :   if (depth > 0) {
     584                 :          2 :     options.requestQueueCapacity = depth;
     585                 :            :   } else {
     586                 :         22 :     options.requestQueueCapacity = kDefaultReqDepth;
     587                 :            :   }
     588                 :            : 
     589   [ +  -  +  - ]:         24 :   server_.emplace(ShmFactory::get_description("vlink.method." + std::to_string(domain), address, method), options);
     590                 :            : 
     591         [ +  - ]:         24 :   listener_ = factory.get_listener(domain_);
     592                 :            : 
     593   [ +  -  -  + ]:         24 :   if VUNLIKELY (listener_
     594                 :            :                     ->attachEvent(server_.value(), shm::popo::ServerEvent::REQUEST_RECEIVED,
     595                 :            :                                   shm::popo::createNotificationCallback(ShmServer::on_request_received, *this))
     596                 :            :                     .has_error()) {
     597   [ #  #  #  #  :          0 :     VLOG_F("ShmFactory: Failed to attach REQUEST_RECEIVED event to listener.");
                   #  # ]
     598                 :            :   }
     599                 :         24 : }
     600                 :            : 
     601                 :         48 : ShmServer::~ShmServer() {
     602                 :         24 :   quit_flag_.store(true, std::memory_order_release);
     603                 :            : 
     604                 :         24 :   server_->stopOffer();
     605                 :            : 
     606                 :         24 :   listener_->detachEvent(server_.value(), shm::popo::ServerEvent::REQUEST_RECEIVED);
     607                 :            : 
     608                 :            :   {
     609                 :         24 :     std::lock_guard lock(callback_mtx_);
     610                 :         24 :   }
     611                 :            : 
     612                 :         24 :   server_->releaseQueuedRequests();
     613                 :         48 : }
     614                 :            : 
     615         [ +  - ]:          1 : std::any ShmServer::get_native_handle() const { return this; }
     616                 :            : 
     617                 :          1 : bool ShmServer::suspend() {
     618                 :          1 :   is_suspend_.store(true, std::memory_order_relaxed);
     619                 :            : 
     620                 :          1 :   return true;
     621                 :            : }
     622                 :            : 
     623                 :          1 : bool ShmServer::resume() {
     624                 :          1 :   is_suspend_.store(false, std::memory_order_relaxed);
     625                 :            : 
     626                 :          1 :   return true;
     627                 :            : }
     628                 :            : 
     629                 :          2 : bool ShmServer::is_suspend() const { return is_suspend_.load(std::memory_order_relaxed); }
     630                 :            : 
     631                 :         24 : void ShmServer::process_message() {
     632   [ +  +  +  -  :         48 :   while (server_->hasRequests() && !quit_flag_.load(std::memory_order_acquire)) {
                   +  + ]
     633                 :         48 :     server_->take()
     634                 :         24 :         .and_then([this](const void* buffer) {
     635                 :         24 :           const auto* read_req = static_cast<const uint8_t*>(buffer);
     636                 :         24 :           const auto* read_header = shm::popo::RequestHeader::fromPayload(read_req);
     637                 :            : 
     638                 :         24 :           uint64_t channel = 0;
     639                 :         24 :           uint64_t seq = 0;
     640                 :         24 :           Bytes req_bytes;
     641                 :         24 :           ShmFactory::read_data(read_req, read_header->getChunkHeader()->userPayloadSize(), channel, seq, req_bytes);
     642                 :            : 
     643                 :            :           (void)seq;
     644                 :            : 
     645                 :         24 :           bool request_released = false;
     646                 :            : 
     647   [ +  -  +  - ]:         24 :           traverse_req_resp_callback([this, channel, &req_bytes, &read_req, &read_header, &request_released](
     648                 :        210 :                                          NodeImpl* impl, const auto& callback) {
     649         [ +  - ]:         24 :             const auto* conf_ptr = impl->get_target_conf<ShmConf>();
     650                 :            : 
     651         [ -  + ]:         24 :             if (static_cast<uint64_t>(conf_ptr->hash_code) != channel) {
     652                 :          0 :               ignore_called();
     653                 :          0 :               return;
     654                 :            :             }
     655                 :            : 
     656         [ -  + ]:         24 :             if VUNLIKELY (has_called()) {
     657   [ #  #  #  #  :          0 :               VLOG_F(*conf_ptr, "Two identical service requests.");
                   #  # ]
     658                 :          0 :               return;
     659                 :            :             }
     660                 :            : 
     661         [ +  - ]:         24 :             std::lock_guard lock(mtx_);
     662                 :            : 
     663         [ +  + ]:         24 :             if (static_cast<ServerImpl*>(impl)->is_resp_type) {
     664                 :         21 :               last_req_header_ = read_header;
     665                 :            : 
     666                 :         21 :               Bytes resp_bytes;
     667                 :            : 
     668                 :         21 :               auto seq = static_cast<uint64_t>(read_header->getSequenceId());
     669                 :            : 
     670         [ +  - ]:         21 :               callback(seq, req_bytes, &resp_bytes);
     671                 :         21 :             } else {
     672         [ +  - ]:          3 :               callback(0, req_bytes, nullptr);
     673                 :            :             }
     674                 :            : 
     675                 :         24 :             server_->releaseRequest(read_req);
     676                 :         24 :             request_released = true;
     677                 :         24 :           });
     678                 :            : 
     679         [ -  + ]:         24 :           if VUNLIKELY (!request_released) {
     680                 :          0 :             server_->releaseRequest(read_req);
     681                 :            :           }
     682                 :         24 :         })
     683   [ #  #  #  #  :         24 :         .or_else([](auto& e) { VLOG_E("ShmFactory: Failed to take request, error: ", e, "."); });
                   #  # ]
     684                 :            :   }
     685                 :         24 : }
     686                 :            : 
     687                 :         22 : void ShmServer::start() { server_->offer(); }
     688                 :            : 
     689                 :          0 : void ShmServer::stop() { server_->stopOffer(); }
     690                 :            : 
     691                 :          0 : bool ShmServer::has_clients() const { return server_->hasClients(); }
     692                 :            : 
     693                 :          1 : Bytes ShmServer::loan(uint64_t channel, int64_t size) {
     694         [ -  + ]:          1 :   if VUNLIKELY (size <= 0) {
     695                 :          0 :     return Bytes();
     696                 :            :   }
     697                 :            : 
     698                 :            :   auto write_req_result =
     699                 :          1 :       server_->loan(last_req_header_, size + ShmFactory::get_loaned_offset(), ShmFactory::get_loaned_alignment());
     700                 :            : 
     701                 :          1 :   seq_.fetch_add(1, std::memory_order_relaxed);
     702                 :            : 
     703         [ -  + ]:          1 :   if VUNLIKELY (write_req_result.has_error()) {
     704   [ #  #  #  #  :          0 :     VLOG_E("ShmFactory: Failed to loan buffer, size: ", size + ShmFactory::get_loaned_offset(),
                   #  # ]
     705                 :            :            ", error: ", write_req_result.get_error(), ".");
     706                 :          0 :     return Bytes();
     707                 :            :   }
     708                 :            : 
     709                 :          1 :   auto* write_req = static_cast<uint8_t*>(write_req_result.value());
     710                 :            : 
     711                 :          2 :   ShmFactory::write_header(write_req, channel, seq_.load(std::memory_order_relaxed));
     712                 :            : 
     713                 :          1 :   return Bytes::loan_internal(write_req + ShmFactory::get_loaned_offset(), size);
     714                 :          1 : }
     715                 :            : 
     716                 :          1 : bool ShmServer::release(const Bytes& bytes) {
     717         [ +  - ]:          1 :   if VUNLIKELY (!bytes.is_loaned()) {
     718                 :          1 :     return false;
     719                 :            :   }
     720                 :            : 
     721                 :          0 :   server_->releaseResponse(const_cast<uint8_t*>(bytes.data()) - ShmFactory::get_loaned_offset());
     722                 :            : 
     723                 :          0 :   return true;
     724                 :            : }
     725                 :            : 
     726                 :         21 : bool ShmServer::reply(uint64_t channel, const Bytes& resp_data) {
     727         [ -  + ]:         21 :   if (resp_data.is_loaned()) {
     728                 :          0 :     auto send_result = server_->send(const_cast<uint8_t*>(resp_data.data() - ShmFactory::get_loaned_offset()));
     729                 :            : 
     730         [ #  # ]:          0 :     if VUNLIKELY (send_result.has_error()) {
     731   [ #  #  #  #  :          0 :       VLOG_E("ShmFactory: Failed to send, error: ", send_result.get_error(), ".");
                   #  # ]
     732                 :          0 :       return false;
     733                 :            :     }
     734         [ #  # ]:          0 :   } else {
     735                 :         21 :     auto write_resp_result = server_->loan(last_req_header_, resp_data.size() + ShmFactory::get_loaned_offset(),
     736                 :         21 :                                            ShmFactory::get_loaned_alignment());
     737                 :            : 
     738                 :         21 :     seq_.fetch_add(1, std::memory_order_relaxed);
     739                 :            : 
     740         [ -  + ]:         21 :     if VUNLIKELY (write_resp_result.has_error()) {
     741   [ #  #  #  #  :          0 :       VLOG_E("ShmFactory: Failed to loan buffer, size: ", resp_data.size() + ShmFactory::get_loaned_offset(),
                   #  # ]
     742                 :            :              ", error: ", write_resp_result.get_error(), ".");
     743                 :          0 :       return false;
     744                 :            :     }
     745                 :            : 
     746                 :         21 :     auto* write_resp = static_cast<uint8_t*>(write_resp_result.value());
     747                 :            : 
     748                 :         42 :     ShmFactory::write_data(write_resp, channel, seq_.load(std::memory_order_relaxed), resp_data);
     749                 :            : 
     750                 :         21 :     auto send_result = server_->send(write_resp);
     751                 :            : 
     752         [ -  + ]:         21 :     if VUNLIKELY (send_result.has_error()) {
     753   [ #  #  #  #  :          0 :       VLOG_E("ShmFactory: Failed to send, error: ", send_result.get_error(), ".");
                   #  # ]
     754                 :          0 :       return false;
     755                 :            :     }
     756   [ +  -  +  - ]:         21 :   }
     757                 :            : 
     758                 :         21 :   return true;
     759                 :            : }
     760                 :            : 
     761                 :         25 : void ShmServer::on_request_received(shm::popo::UntypedServer*, ShmServer* target) {
     762         [ +  - ]:         25 :   std::lock_guard callback_lock(target->callback_mtx_);
     763                 :            : 
     764         [ -  + ]:         25 :   if VUNLIKELY (target->quit_flag_.load(std::memory_order_acquire)) {
     765                 :          0 :     return;
     766                 :            :   }
     767                 :            : 
     768         [ +  + ]:         25 :   if VUNLIKELY (target->is_suspend_.load(std::memory_order_relaxed)) {
     769                 :          1 :     target->server_->releaseQueuedRequests();
     770                 :            : 
     771                 :          1 :     return;
     772                 :            :   }
     773                 :            : 
     774         [ +  - ]:         24 :   auto* impl = target->get_first_impl();
     775                 :            : 
     776         [ -  + ]:         24 :   if VUNLIKELY (!impl) {
     777                 :          0 :     return;
     778                 :            :   }
     779                 :            : 
     780                 :         24 :   MessageLoop* message_loop = nullptr;
     781                 :            : 
     782   [ +  -  +  - ]:         48 :   target->invoke_callback(impl, [&] { message_loop = impl->get_message_loop(); });
     783                 :            : 
     784         [ +  + ]:         24 :   if (message_loop) {
     785                 :          9 :     std::weak_ptr<ShmServer> weak_target = target->weak_from_this();
     786                 :            : 
     787   [ +  -  +  - ]:          9 :     message_loop->post_task([weak_target, message_loop]() {
     788                 :          9 :       auto target = weak_target.lock();
     789                 :            : 
     790         [ -  + ]:          9 :       if VUNLIKELY (!target) {
     791                 :          0 :         return;
     792                 :            :       }
     793                 :            : 
     794         [ +  - ]:          9 :       auto* impl = target->get_first_impl();
     795                 :            : 
     796                 :          9 :       bool attached = false;
     797   [ +  -  +  - ]:         18 :       target->invoke_callback(impl, [&] { attached = impl->get_message_loop() == message_loop; });
     798                 :            : 
     799         [ -  + ]:          9 :       if VUNLIKELY (!attached) {
     800                 :          0 :         return;
     801                 :            :       }
     802                 :            : 
     803                 :          9 :       target->process_message();
     804         [ +  - ]:          9 :     });
     805                 :          9 :   } else {
     806                 :         15 :     target->process_message();
     807                 :            :   }
     808         [ +  + ]:         25 : }
     809                 :            : 
     810                 :            : // ShmClient
     811                 :         22 : ShmClient::ShmClient(const ShmID& id) {
     812   [ +  +  +  -  :         22 :   static auto& factory = ShmFactory::get();
             +  -  -  - ]
     813                 :            : 
     814                 :         22 :   const auto& [impl_type, address, domain, depth, history, wait, method, owner] = id;
     815                 :            : 
     816                 :         22 :   domain_ = domain;
     817                 :            : 
     818                 :         22 :   shm::popo::ClientOptions options;
     819                 :         22 :   options.connectOnCreate = true;
     820                 :            : 
     821         [ +  + ]:         22 :   if (depth > 0) {
     822                 :          3 :     options.responseQueueCapacity = depth;
     823                 :            :   } else {
     824                 :         19 :     options.responseQueueCapacity = kDefaultRespDepth;
     825                 :            :   }
     826                 :            : 
     827   [ +  -  +  - ]:         22 :   client_.emplace(ShmFactory::get_description("vlink.method." + std::to_string(domain), address, method), options);
     828                 :            : 
     829         [ +  - ]:         22 :   listener_ = factory.get_listener(domain_);
     830                 :            : 
     831   [ +  -  -  + ]:         22 :   if VUNLIKELY (listener_
     832                 :            :                     ->attachEvent(client_.value(), shm::popo::ClientEvent::RESPONSE_RECEIVED,
     833                 :            :                                   shm::popo::createNotificationCallback(ShmClient::on_response_received, *this))
     834                 :            :                     .has_error()) {
     835   [ #  #  #  #  :          0 :     VLOG_F("ShmFactory: Failed to attach RESPONSE_RECEIVED event to listener.");
                   #  # ]
     836                 :            :   }
     837                 :         22 : }
     838                 :            : 
     839                 :         44 : ShmClient::~ShmClient() {
     840                 :         22 :   quit_flag_.store(true, std::memory_order_release);
     841                 :            : 
     842                 :         22 :   client_->disconnect();
     843                 :            : 
     844                 :         22 :   disable_detect_timer();
     845                 :            : 
     846                 :         22 :   listener_->detachEvent(client_.value(), shm::popo::ClientEvent::RESPONSE_RECEIVED);
     847                 :            : 
     848                 :            :   {
     849                 :         22 :     std::lock_guard lock(callback_mtx_);
     850                 :         22 :   }
     851                 :            : 
     852                 :         22 :   client_->releaseQueuedResponses();
     853                 :         44 : }
     854                 :            : 
     855         [ +  - ]:          1 : std::any ShmClient::get_native_handle() const { return this; }
     856                 :            : 
     857                 :         21 : void ShmClient::process_message() {
     858   [ +  +  +  -  :         42 :   while (client_->hasResponses() && !quit_flag_.load(std::memory_order_acquire)) {
                   +  + ]
     859         [ +  - ]:         21 :     std::unique_lock lock(mtx_);
     860                 :            : 
     861                 :         21 :     auto received = client_->take();
     862                 :            : 
     863         [ -  + ]:         21 :     if VUNLIKELY (received.has_error()) {
     864   [ #  #  #  #  :          0 :       VLOG_E("ShmFactory: Failed to take response, error: ", received.get_error(), ".");
                   #  # ]
     865                 :          0 :       break;
     866                 :            :     }
     867                 :            : 
     868                 :         21 :     const auto* read_resp = static_cast<const uint8_t*>(received.value());
     869                 :         21 :     const auto* read_header = shm::popo::ResponseHeader::fromPayload(read_resp);
     870         [ +  - ]:         21 :     auto iter = callbacks_.find(read_header->getSequenceId());
     871                 :            : 
     872         [ +  + ]:         21 :     if VUNLIKELY (iter == callbacks_.end()) {
     873                 :          1 :       client_->releaseResponse(read_resp);
     874                 :          1 :       continue;
     875                 :            :     }
     876                 :            : 
     877                 :         20 :     auto callback = std::move(iter->second);
     878         [ +  - ]:         20 :     callbacks_.erase(iter);
     879         [ +  - ]:         20 :     lock.unlock();
     880                 :            : 
     881                 :         20 :     uint64_t channel = 0;
     882                 :         20 :     uint64_t seq = 0;
     883                 :         20 :     Bytes resp_bytes;
     884                 :         20 :     ShmFactory::read_data(read_resp, read_header->getChunkHeader()->userPayloadSize(), channel, seq, resp_bytes);
     885                 :            : 
     886                 :         20 :     MessageLoop* message_loop = nullptr;
     887                 :            : 
     888         [ +  + ]:         20 :     if (callback.dispatch) {
     889   [ +  -  +  - ]:          5 :       invoke_callback(callback.owner, [&]() {
     890                 :          5 :         message_loop = callback.owner->get_message_loop();
     891                 :            : 
     892         [ +  + ]:          5 :         if (!message_loop) {
     893                 :          2 :           callback.callback(resp_bytes);
     894                 :            :         }
     895                 :          5 :       });
     896                 :            :     } else {
     897         [ +  - ]:         15 :       callback.callback(resp_bytes);
     898                 :            :     }
     899                 :            : 
     900         [ +  + ]:         20 :     if (message_loop) {
     901                 :          3 :       auto release_response = [self = shared_from_this()](const uint8_t* data) {
     902         [ +  - ]:          3 :         std::lock_guard lock(self->mtx_);
     903                 :            : 
     904                 :          3 :         self->client_->releaseResponse(data);
     905         [ +  - ]:          6 :       };
     906                 :          3 :       std::unique_ptr<const uint8_t, decltype(release_response)> response(read_resp, std::move(release_response));
     907                 :            : 
     908   [ +  -  +  - ]:          6 :       message_loop->post_task([this, message_loop, response = std::move(response), resp_bytes = std::move(resp_bytes),
     909                 :          3 :                                callback = std::move(callback)]() {
     910                 :          3 :         bool attached = false;
     911   [ +  -  +  - ]:          6 :         invoke_callback(callback.owner, [&]() { attached = callback.owner->get_message_loop() == message_loop; });
     912                 :            : 
     913         [ +  - ]:          3 :         if (attached) {
     914         [ +  - ]:          3 :           callback.callback(resp_bytes);
     915                 :            :         }
     916                 :          3 :       });
     917                 :          3 :     } else {
     918         [ +  - ]:         17 :       lock.lock();
     919                 :            : 
     920                 :         17 :       client_->releaseResponse(read_resp);
     921                 :            :     }
     922   [ +  -  +  +  :         22 :   }
                   -  + ]
     923                 :         21 : }
     924                 :            : 
     925                 :        316 : bool ShmClient::is_connected() const { return client_->getConnectionState() == shm::ConnectionState::CONNECTED; }
     926                 :            : 
     927                 :         67 : void ShmClient::enable_detect_timer() {
     928         [ +  + ]:         67 :   if (!has_detect_timer_.load(std::memory_order_relaxed)) {
     929                 :         20 :     has_detect_timer_.store(true, std::memory_order_relaxed);
     930                 :            : 
     931   [ +  -  +  - ]:         20 :     ShmFactory::get().add_detect_callback(this, [weak = weak_from_this()]() {
     932                 :         74 :       auto self = weak.lock();
     933                 :            : 
     934         [ +  - ]:         74 :       if VLIKELY (self) {
     935         [ +  - ]:         74 :         self->detect_server();
     936                 :            :       }
     937                 :         74 :     });
     938                 :            :   }
     939                 :         67 : }
     940                 :            : 
     941                 :         22 : void ShmClient::disable_detect_timer() {
     942         [ +  + ]:         22 :   if (has_detect_timer_.load(std::memory_order_relaxed)) {
     943                 :         20 :     has_detect_timer_.store(false, std::memory_order_relaxed);
     944                 :            : 
     945                 :         20 :     ShmFactory::get().remove_detect_callback(this);
     946                 :            :   }
     947                 :         22 : }
     948                 :            : 
     949                 :         98 : Bytes ShmClient::loan(uint64_t channel, int64_t size) {
     950         [ -  + ]:         98 :   if VUNLIKELY (size <= 0) {
     951                 :          0 :     return Bytes();
     952                 :            :   }
     953                 :            : 
     954         [ +  - ]:         98 :   std::lock_guard lock(mtx_);
     955                 :            : 
     956                 :         98 :   auto write_req_result = client_->loan(size + ShmFactory::get_loaned_offset(), ShmFactory::get_loaned_alignment());
     957                 :            : 
     958         [ -  + ]:         98 :   if VUNLIKELY (write_req_result.has_error()) {
     959   [ #  #  #  #  :          0 :     VLOG_E("ShmFactory: Failed to loan buffer, size: ", size + ShmFactory::get_loaned_offset(),
                   #  # ]
     960                 :            :            ", error: ", write_req_result.get_error(), ".");
     961                 :          0 :     return Bytes();
     962                 :            :   }
     963                 :            : 
     964                 :         98 :   auto* write_req = static_cast<uint8_t*>(write_req_result.value());
     965                 :            : 
     966                 :        196 :   ShmFactory::write_header(write_req, channel, seq_.load(std::memory_order_relaxed));
     967                 :            : 
     968                 :         98 :   return Bytes::loan_internal(write_req + ShmFactory::get_loaned_offset(), size);
     969                 :         98 : }
     970                 :            : 
     971                 :         98 : bool ShmClient::release(const Bytes& bytes) {
     972         [ +  + ]:         98 :   if VUNLIKELY (!bytes.is_loaned()) {
     973                 :          2 :     return false;
     974                 :            :   }
     975                 :            : 
     976         [ +  - ]:         96 :   std::lock_guard lock(mtx_);
     977                 :            : 
     978                 :         96 :   client_->releaseRequest(const_cast<uint8_t*>(bytes.data()) - ShmFactory::get_loaned_offset());
     979                 :            : 
     980                 :         96 :   return true;
     981                 :         96 : }
     982                 :            : 
     983                 :         57 : bool ShmClient::call(NodeImpl* owner, uint64_t channel, const Bytes& req_data, NodeImpl::MsgCallback&& callback,
     984                 :            :                      uint64_t* seq_out, bool dispatch) {
     985         [ +  + ]:         57 :   if VUNLIKELY (!is_connected()) {
     986         [ +  - ]:         32 :     release(req_data);
     987                 :            : 
     988                 :         32 :     return false;
     989                 :            :   }
     990                 :            : 
     991         [ +  - ]:         25 :   std::lock_guard lock(mtx_);
     992                 :         25 :   uint64_t response_seq = 0;
     993                 :         25 :   bool has_response_callback = false;
     994                 :            : 
     995         [ +  + ]:         25 :   if (req_data.is_loaned()) {
     996                 :            :     auto* write_header =
     997                 :          2 :         shm::popo::RequestHeader::fromPayload(const_cast<uint8_t*>(req_data.data()) - ShmFactory::get_loaned_offset());
     998                 :            : 
     999         [ +  + ]:          2 :     if (callback) {
    1000                 :          1 :       response_seq = seq_.load(std::memory_order_relaxed);
    1001                 :          1 :       has_response_callback = true;
    1002         [ +  - ]:          1 :       callbacks_[response_seq] = ResponseCallback{owner, dispatch, std::move(callback)};
    1003                 :          1 :       write_header->setSequenceId(response_seq);
    1004                 :            : 
    1005         [ +  - ]:          1 :       if (seq_out) {
    1006                 :          1 :         *seq_out = response_seq;
    1007                 :            :       }
    1008                 :            : 
    1009                 :          1 :       seq_.fetch_add(1, std::memory_order_relaxed);
    1010                 :            :     }
    1011                 :            : 
    1012                 :          2 :     auto send_result = client_->send(const_cast<uint8_t*>(req_data.data()) - ShmFactory::get_loaned_offset());
    1013                 :            : 
    1014         [ -  + ]:          2 :     if VUNLIKELY (send_result.has_error()) {
    1015   [ #  #  #  #  :          0 :       VLOG_E("ShmFactory: Failed to send, error: ", send_result.get_error(), ".");
                   #  # ]
    1016         [ #  # ]:          0 :       if (has_response_callback) {
    1017         [ #  # ]:          0 :         callbacks_.erase(response_seq);
    1018                 :            :       }
    1019                 :          0 :       return false;
    1020                 :            :     }
    1021         [ +  - ]:          2 :   } else {
    1022                 :            :     auto write_req_result =
    1023                 :         23 :         client_->loan(req_data.size() + ShmFactory::get_loaned_offset(), ShmFactory::get_loaned_alignment());
    1024         [ -  + ]:         23 :     if VUNLIKELY (write_req_result.has_error()) {
    1025   [ #  #  #  #  :          0 :       VLOG_E("ShmFactory: Failed to loan buffer, size: ", req_data.size() + ShmFactory::get_loaned_offset(),
                   #  # ]
    1026                 :            :              ", error: ", write_req_result.get_error(), ".");
    1027                 :            : 
    1028                 :          0 :       return false;
    1029                 :            :     }
    1030                 :            : 
    1031                 :         23 :     auto* write_req = static_cast<uint8_t*>(write_req_result.value());
    1032                 :            : 
    1033                 :         23 :     auto* write_header = shm::popo::RequestHeader::fromPayload(write_req);
    1034                 :            : 
    1035         [ +  + ]:         23 :     if (callback) {
    1036                 :         21 :       response_seq = seq_.load(std::memory_order_relaxed);
    1037                 :         21 :       has_response_callback = true;
    1038         [ +  - ]:         21 :       callbacks_[response_seq] = ResponseCallback{owner, dispatch, std::move(callback)};
    1039                 :         21 :       write_header->setSequenceId(response_seq);
    1040                 :            : 
    1041         [ +  + ]:         21 :       if (seq_out) {
    1042                 :         15 :         *seq_out = response_seq;
    1043                 :            :       }
    1044                 :            : 
    1045                 :         21 :       seq_.fetch_add(1, std::memory_order_relaxed);
    1046                 :            :     }
    1047                 :            : 
    1048                 :         46 :     ShmFactory::write_data(write_req, channel, seq_.load(std::memory_order_relaxed), req_data);
    1049                 :            : 
    1050                 :         23 :     auto send_result = client_->send(write_req);
    1051                 :            : 
    1052         [ -  + ]:         23 :     if VUNLIKELY (send_result.has_error()) {
    1053   [ #  #  #  #  :          0 :       VLOG_E("ShmFactory: Failed to send, error: ", send_result.get_error(), ".");
                   #  # ]
    1054                 :            : 
    1055         [ #  # ]:          0 :       if (has_response_callback) {
    1056         [ #  # ]:          0 :         callbacks_.erase(response_seq);
    1057                 :            :       }
    1058                 :            : 
    1059                 :          0 :       return false;
    1060                 :            :     }
    1061   [ +  -  +  - ]:         23 :   }
    1062                 :            : 
    1063                 :         25 :   return true;
    1064                 :         25 : }
    1065                 :            : 
    1066                 :          1 : void ShmClient::remove_response_callback(uint64_t seq) {
    1067         [ +  - ]:          1 :   std::lock_guard lock(mtx_);
    1068         [ +  - ]:          1 :   callbacks_.erase(seq);
    1069                 :          1 : }
    1070                 :            : 
    1071                 :         48 : void ShmClient::cancel_calls(NodeImpl* owner) {
    1072         [ +  - ]:         48 :   std::lock_guard lock(mtx_);
    1073                 :            : 
    1074         [ +  + ]:         49 :   for (auto iter = callbacks_.begin(); iter != callbacks_.end();) {
    1075         [ +  - ]:          1 :     if (iter->second.owner == owner) {
    1076         [ +  - ]:          1 :       iter = callbacks_.erase(iter);
    1077                 :            :     } else {
    1078                 :          0 :       ++iter;
    1079                 :            :     }
    1080                 :            :   }
    1081                 :         48 : }
    1082                 :            : 
    1083                 :         74 : void ShmClient::detect_server() {
    1084         [ -  + ]:         74 :   if VUNLIKELY (quit_flag_.load(std::memory_order_acquire)) {
    1085                 :          0 :     return;
    1086                 :            :   }
    1087                 :            : 
    1088         [ -  + ]:         74 :   if VUNLIKELY (!ShmFactory::has_runtime_inited()) {
    1089                 :          0 :     return;
    1090                 :            :   }
    1091                 :            : 
    1092                 :         74 :   discovery_server(is_connected());
    1093                 :            : }
    1094                 :            : 
    1095                 :         74 : void ShmClient::discovery_server(bool connect) {
    1096         [ +  + ]:         74 :   if VLIKELY (last_connected_.load(std::memory_order_relaxed) == connect) {
    1097                 :         55 :     return;
    1098                 :            :   }
    1099                 :            : 
    1100   [ +  -  +  - ]:         40 :   traverse_server_connect_callback([connect](NodeImpl*, const auto& callback) { callback(connect); });
    1101                 :         19 :   last_connected_.store(connect, std::memory_order_relaxed);
    1102                 :            : }
    1103                 :            : 
    1104                 :         21 : void ShmClient::on_response_received(shm::popo::UntypedClient*, ShmClient* target) {
    1105         [ +  - ]:         21 :   std::lock_guard callback_lock(target->callback_mtx_);
    1106                 :            : 
    1107         [ -  + ]:         21 :   if VUNLIKELY (target->quit_flag_.load(std::memory_order_acquire)) {
    1108                 :          0 :     return;
    1109                 :            :   }
    1110                 :            : 
    1111         [ +  - ]:         21 :   target->process_message();
    1112         [ +  - ]:         21 : }
    1113                 :            : 
    1114                 :            : // ShmPublisher
    1115                 :         63 : ShmPublisher::ShmPublisher(const ShmID& id) {
    1116   [ +  +  +  -  :         63 :   static auto& factory = ShmFactory::get();
             +  -  -  - ]
    1117                 :            : 
    1118                 :         63 :   const auto& [impl_type, address, domain, depth, history, wait, field, owner] = id;
    1119                 :            : 
    1120                 :         63 :   domain_ = domain;
    1121                 :         63 :   wait_ = wait;
    1122                 :            : 
    1123                 :         63 :   shm::popo::PublisherOptions options;
    1124                 :         63 :   options.offerOnCreate = true;
    1125                 :         63 :   options.historyCapacity = history;
    1126                 :            : 
    1127         [ +  - ]:         63 :   std::string event = "event";
    1128                 :            : 
    1129         [ +  + ]:         63 :   if (domain != 0) {
    1130   [ +  -  +  -  :          3 :     event += ("_" + std::to_string(domain));
                   +  - ]
    1131                 :            :   }
    1132                 :            : 
    1133         [ +  - ]:         63 :   pub_.emplace(impl_type == kSetter
    1134   [ +  +  +  -  :        126 :                    ? ShmFactory::get_description("vlink.field." + std::to_string(domain), address, field)
          +  -  +  -  +  
          +  +  +  +  +  
          +  +  -  -  -  
             -  -  -  -  
                      - ]
    1135                 :            :                    : ShmFactory::get_description("vlink", address, event),
    1136                 :            :                options);
    1137                 :            : 
    1138         [ +  + ]:         63 :   if (wait > 0) {
    1139         [ +  - ]:          1 :     factory.start_detect_node_count();
    1140                 :            : 
    1141         [ +  - ]:          1 :     sem_.emplace();
    1142                 :            : 
    1143   [ +  -  +  -  :          2 :     std::string sem_address = std::to_string(domain) + "@" + address;
                   +  - ]
    1144                 :          1 :     std::replace(sem_address.begin(), sem_address.end(), '/', '@');
    1145                 :            : 
    1146                 :            : #ifdef __FreeBSD__
    1147                 :            :     sem_->attach("/vlink@shm@" + sem_address);
    1148                 :            : #else
    1149   [ +  -  +  - ]:          1 :     sem_->attach("vlink@shm@" + sem_address);
    1150                 :            : #endif
    1151                 :          1 :   }
    1152                 :         63 : }
    1153                 :            : 
    1154                 :        126 : ShmPublisher::~ShmPublisher() {
    1155                 :         63 :   quit_flag_.store(true, std::memory_order_release);
    1156                 :            : 
    1157                 :         63 :   pub_->stopOffer();
    1158                 :            : 
    1159                 :         63 :   disable_detect_timer();
    1160                 :            : 
    1161         [ +  + ]:         63 :   if (sem_) {
    1162                 :          1 :     sem_->detach(true);
    1163                 :            :   }
    1164                 :        126 : }
    1165                 :            : 
    1166         [ +  - ]:          2 : std::any ShmPublisher::get_native_handle() const { return this; }
    1167                 :            : 
    1168                 :       4750 : bool ShmPublisher::has_subscribers() const { return pub_->hasSubscribers(); }
    1169                 :            : 
    1170                 :         72 : Bytes ShmPublisher::loan(uint64_t channel, int64_t size) {
    1171         [ +  + ]:         72 :   if VUNLIKELY (size <= 0) {
    1172                 :          2 :     return Bytes();
    1173                 :            :   }
    1174                 :            : 
    1175         [ +  - ]:         70 :   std::lock_guard lock(mtx_);
    1176                 :            : 
    1177                 :         70 :   auto write_msg_result = pub_->loan(size + ShmFactory::get_loaned_offset(), ShmFactory::get_loaned_alignment());
    1178                 :            : 
    1179                 :         70 :   seq_.fetch_add(1, std::memory_order_relaxed);
    1180                 :            : 
    1181         [ -  + ]:         70 :   if VUNLIKELY (write_msg_result.has_error()) {
    1182   [ #  #  #  #  :          0 :     VLOG_E("ShmFactory: Failed to loan buffer, size: ", size + ShmFactory::get_loaned_offset(),
                   #  # ]
    1183                 :            :            ", error: ", write_msg_result.get_error(), ".");
    1184                 :          0 :     return Bytes();
    1185                 :            :   }
    1186                 :            : 
    1187                 :         70 :   auto* write_msg = static_cast<uint8_t*>(write_msg_result.value());
    1188                 :            : 
    1189                 :        140 :   ShmFactory::write_header(write_msg, channel, seq_.load(std::memory_order_relaxed));
    1190                 :            : 
    1191                 :         70 :   return Bytes::loan_internal(write_msg + ShmFactory::get_loaned_offset(), size);
    1192                 :         70 : }
    1193                 :            : 
    1194                 :          4 : bool ShmPublisher::release(const Bytes& bytes) {
    1195         [ +  + ]:          4 :   if VUNLIKELY (!bytes.is_loaned()) {
    1196                 :          2 :     return false;
    1197                 :            :   }
    1198                 :            : 
    1199         [ +  - ]:          2 :   std::lock_guard lock(mtx_);
    1200                 :            : 
    1201                 :          2 :   pub_->release(const_cast<uint8_t*>(bytes.data()) - ShmFactory::get_loaned_offset());
    1202                 :            : 
    1203                 :          2 :   return true;
    1204                 :          2 : }
    1205                 :            : 
    1206                 :       4371 : bool ShmPublisher::publish(uint64_t channel, const Bytes& bytes) {
    1207         [ +  + ]:       4371 :   if VUNLIKELY (wait_ > 0) {
    1208                 :          1 :     uint64_t sem_count = sem_->get_count();
    1209                 :            : 
    1210         [ -  + ]:          1 :     if (sem_count > 0) {
    1211                 :          0 :       sem_->acquire(sem_count, wait_);
    1212                 :            :     }
    1213                 :            :   }
    1214                 :            : 
    1215                 :            :   {
    1216         [ +  - ]:       4371 :     std::lock_guard lock(mtx_);
    1217                 :            : 
    1218         [ +  + ]:       4377 :     if (bytes.is_loaned()) {
    1219                 :         68 :       pub_->publish(const_cast<uint8_t*>(bytes.data()) - ShmFactory::get_loaned_offset());
    1220                 :            :     } else {
    1221                 :            :       auto write_msg_result =
    1222                 :       4308 :           pub_->loan(bytes.size() + ShmFactory::get_loaned_offset(), ShmFactory::get_loaned_alignment());
    1223                 :            : 
    1224                 :       4312 :       seq_.fetch_add(1, std::memory_order_relaxed);
    1225                 :            : 
    1226         [ -  + ]:       4312 :       if VUNLIKELY (write_msg_result.has_error()) {
    1227   [ #  #  #  #  :          0 :         VLOG_E("ShmFactory: Failed to loan buffer, size: ", bytes.size() + ShmFactory::get_loaned_offset(),
                   #  # ]
    1228                 :            :                ", error: ", write_msg_result.get_error(), ".");
    1229                 :          0 :         return false;
    1230                 :            :       }
    1231                 :            : 
    1232                 :       4311 :       auto* write_msg = static_cast<uint8_t*>(write_msg_result.value());
    1233                 :            : 
    1234                 :       8621 :       ShmFactory::write_data(write_msg, channel, seq_.load(std::memory_order_relaxed), bytes);
    1235                 :            : 
    1236                 :       4308 :       pub_->publish(write_msg);
    1237         [ +  # ]:       4315 :     }
    1238         [ +  - ]:       4374 :   }
    1239                 :            : 
    1240         [ +  + ]:       4380 :   if VUNLIKELY (wait_ > 0) {
    1241         [ +  - ]:          1 :     uint64_t sub_count = ShmFactory::get().get_subscriber_count(pub_->getServiceDescription());
    1242                 :          1 :     sem_->acquire(sub_count, wait_);
    1243                 :            :   }
    1244                 :            : 
    1245                 :       4379 :   return true;
    1246                 :            : }
    1247                 :            : 
    1248                 :         48 : void ShmPublisher::enable_detect_timer() {
    1249         [ +  + ]:         48 :   if (!has_detect_timer_.load(std::memory_order_relaxed)) {
    1250                 :         42 :     has_detect_timer_.store(true, std::memory_order_relaxed);
    1251   [ +  -  +  - ]:         42 :     ShmFactory::get().add_detect_callback(this, [weak = weak_from_this()]() {
    1252                 :        420 :       auto self = weak.lock();
    1253                 :            : 
    1254         [ +  - ]:        420 :       if VLIKELY (self) {
    1255         [ +  - ]:        420 :         self->detect_subscribers();
    1256                 :            :       }
    1257                 :        420 :     });
    1258                 :            :   }
    1259                 :         48 : }
    1260                 :            : 
    1261                 :         63 : void ShmPublisher::disable_detect_timer() {
    1262         [ +  + ]:         63 :   if (has_detect_timer_.load(std::memory_order_relaxed)) {
    1263                 :         42 :     has_detect_timer_.store(false, std::memory_order_relaxed);
    1264                 :         42 :     ShmFactory::get().remove_detect_callback(this);
    1265                 :            :   }
    1266                 :         63 : }
    1267                 :            : 
    1268                 :        420 : void ShmPublisher::detect_subscribers() {
    1269         [ -  + ]:        420 :   if VUNLIKELY (quit_flag_.load(std::memory_order_acquire)) {
    1270                 :          0 :     return;
    1271                 :            :   }
    1272                 :            : 
    1273         [ -  + ]:        420 :   if VUNLIKELY (!ShmFactory::has_runtime_inited()) {
    1274                 :          0 :     return;
    1275                 :            :   }
    1276                 :            : 
    1277                 :        420 :   discovery_subscribers(has_subscribers());
    1278                 :            : }
    1279                 :            : 
    1280                 :        420 : void ShmPublisher::discovery_subscribers(bool has_subscribers) {
    1281         [ +  + ]:        420 :   if VLIKELY (last_has_subscribers_.load(std::memory_order_relaxed) == has_subscribers) {
    1282                 :        377 :     return;
    1283                 :            :   }
    1284                 :            : 
    1285   [ +  -  +  - ]:         87 :   traverse_sub_connect_callback([has_subscribers](NodeImpl*, const auto& callback) { callback(has_subscribers); });
    1286                 :         43 :   last_has_subscribers_.store(has_subscribers, std::memory_order_relaxed);
    1287                 :            : }
    1288                 :            : 
    1289                 :            : // ShmSubscriber
    1290                 :         65 : ShmSubscriber::ShmSubscriber(const ShmID& id) {
    1291   [ +  +  +  -  :         65 :   static auto& factory = ShmFactory::get();
             +  -  -  - ]
    1292                 :            : 
    1293                 :         65 :   const auto& [impl_type, address, domain, depth, history, wait, field, owner] = id;
    1294                 :            : 
    1295                 :         65 :   domain_ = domain;
    1296                 :         65 :   wait_ = wait;
    1297                 :            : 
    1298                 :         65 :   shm::popo::SubscriberOptions options;
    1299                 :         65 :   options.subscribeOnCreate = false;
    1300                 :         65 :   options.historyRequest = history;
    1301                 :            :   // options.requiresPublisherHistorySupport = history > 0;
    1302                 :            : 
    1303         [ +  + ]:         65 :   if (depth > 0) {
    1304                 :          6 :     options.queueCapacity = depth;
    1305                 :            :   } else {
    1306                 :         59 :     options.queueCapacity = factory.get_sub_depth();
    1307                 :            :   }
    1308                 :            : 
    1309         [ +  - ]:         65 :   std::string event = "event";
    1310                 :            : 
    1311         [ +  + ]:         65 :   if (domain != 0) {
    1312   [ +  -  +  -  :          3 :     event += ("_" + std::to_string(domain));
                   +  - ]
    1313                 :            :   }
    1314                 :            : 
    1315         [ +  - ]:         65 :   sub_.emplace(impl_type == kGetter
    1316   [ +  +  +  -  :        130 :                    ? ShmFactory::get_description("vlink.field." + std::to_string(domain), address, field)
          +  -  +  -  +  
          +  +  +  +  +  
          +  +  -  -  -  
             -  -  -  -  
                      - ]
    1317                 :            :                    : ShmFactory::get_description("vlink", address, event),
    1318                 :            :                options);
    1319                 :            : 
    1320         [ +  - ]:         65 :   listener_ = factory.get_listener(domain_);
    1321                 :            : 
    1322   [ +  -  -  + ]:         65 :   if VUNLIKELY (listener_
    1323                 :            :                     ->attachEvent(sub_.value(), shm::popo::SubscriberEvent::DATA_RECEIVED,
    1324                 :            :                                   shm::popo::createNotificationCallback(ShmSubscriber::on_msg_received, *this))
    1325                 :            :                     .has_error()) {
    1326   [ #  #  #  #  :          0 :     VLOG_F("ShmFactory: Failed to attach DATA_RECEIVED event to listener.");
                   #  # ]
    1327                 :            :   }
    1328                 :            : 
    1329         [ +  + ]:         65 :   if (wait_ > 0) {
    1330         [ +  - ]:          1 :     sem_.emplace();
    1331                 :            : 
    1332   [ +  -  +  -  :          2 :     std::string sem_address = std::to_string(domain) + "@" + address;
                   +  - ]
    1333                 :          1 :     std::replace(sem_address.begin(), sem_address.end(), '/', '@');
    1334                 :            : 
    1335                 :            : #ifdef __FreeBSD__
    1336                 :            :     sem_->attach("/vlink@shm@" + sem_address);
    1337                 :            : #else
    1338   [ +  -  +  - ]:          1 :     sem_->attach("vlink@shm@" + sem_address);
    1339                 :            : #endif
    1340                 :          1 :   }
    1341                 :         65 : }
    1342                 :            : 
    1343                 :        130 : ShmSubscriber::~ShmSubscriber() {
    1344                 :         65 :   quit_flag_.store(true, std::memory_order_release);
    1345                 :            : 
    1346                 :         65 :   sub_->unsubscribe();
    1347                 :            : 
    1348                 :         65 :   listener_->detachEvent(sub_.value(), shm::popo::SubscriberEvent::DATA_RECEIVED);
    1349                 :            : 
    1350                 :            :   {
    1351                 :         65 :     std::lock_guard lock(callback_mtx_);
    1352                 :         65 :   }
    1353                 :            : 
    1354                 :         65 :   sub_->releaseQueuedData();
    1355                 :            : 
    1356         [ +  + ]:         65 :   if (sem_) {
    1357                 :          1 :     sem_->detach(false);
    1358                 :            :   }
    1359                 :        130 : }
    1360                 :            : 
    1361         [ +  - ]:          2 : std::any ShmSubscriber::get_native_handle() const { return this; }
    1362                 :            : 
    1363                 :          1 : bool ShmSubscriber::suspend() {
    1364                 :          1 :   is_suspend_.store(true, std::memory_order_relaxed);
    1365                 :            : 
    1366                 :          1 :   return true;
    1367                 :            : }
    1368                 :            : 
    1369                 :          1 : bool ShmSubscriber::resume() {
    1370                 :          1 :   is_suspend_.store(false, std::memory_order_relaxed);
    1371                 :            : 
    1372                 :          1 :   return true;
    1373                 :            : }
    1374                 :            : 
    1375                 :          2 : bool ShmSubscriber::is_suspend() const { return is_suspend_.load(std::memory_order_relaxed); }
    1376                 :            : 
    1377                 :       3007 : void ShmSubscriber::process_message(MessageLoop* dispatched_loop) {
    1378   [ +  +  +  -  :       7200 :   while (sub_->hasData() && !quit_flag_.load(std::memory_order_acquire)) {
                   +  + ]
    1379         [ +  - ]:       4193 :     std::unique_lock lock(receive_mtx_);
    1380                 :            : 
    1381                 :       4193 :     auto received = sub_->take();
    1382                 :            : 
    1383         [ -  + ]:       4193 :     if VUNLIKELY (received.has_error()) {
    1384   [ #  #  #  #  :          0 :       VLOG_E("ShmFactory: Failed to take sample, error: ", received.get_error(), ".");
                   #  # ]
    1385                 :          0 :       break;
    1386                 :            :     }
    1387                 :            : 
    1388                 :       4193 :     const auto* read_msg = static_cast<const uint8_t*>(received.value());
    1389                 :       4193 :     const auto* read_header = shm::mepoo::ChunkHeader::fromUserPayload(read_msg);
    1390                 :       4193 :     uint64_t channel = 0;
    1391                 :       4193 :     uint64_t seq = 0;
    1392                 :       4193 :     Bytes msg_bytes;
    1393                 :       4193 :     ShmFactory::read_data(read_msg, read_header->userPayloadSize(), channel, seq, msg_bytes);
    1394                 :            : 
    1395         [ +  + ]:       4193 :     if VUNLIKELY (is_latency_and_lost_enabled_.load(std::memory_order_acquire)) {
    1396         [ -  + ]:         10 :       calc_sample_.update(seq > 0 ? seq : read_header->sequenceNumber(),
    1397                 :         20 :                           static_cast<uint64_t>(read_header->originId()));
    1398                 :            :     }
    1399                 :            : 
    1400         [ +  - ]:       4193 :     lock.unlock();
    1401                 :            : 
    1402                 :       4193 :     std::vector<MessageLoop*> routes;
    1403                 :            : 
    1404   [ +  -  +  - ]:       4193 :     traverse_msg_callback([channel, &msg_bytes, dispatched_loop, &routes](NodeImpl* impl, const auto& callback) {
    1405         [ +  - ]:       4325 :       const auto* conf = impl->get_target_conf<ShmConf>();
    1406                 :            : 
    1407   [ +  +  +  +  :       4325 :       if VUNLIKELY (static_cast<uint64_t>(conf->hash_code) != channel ||
                   +  + ]
    1408                 :            :                     impl->has_suspend.load(std::memory_order_acquire)) {
    1409                 :          6 :         return;
    1410                 :            :       }
    1411                 :            : 
    1412         [ +  - ]:       4319 :       auto* loop = impl->get_message_loop();
    1413                 :            : 
    1414   [ +  +  +  +  :       4319 :       if VLIKELY (!loop || loop == dispatched_loop) {
                   +  + ]
    1415         [ +  - ]:       4314 :         callback(msg_bytes);
    1416   [ +  -  +  - ]:          5 :       } else if (std::find(routes.begin(), routes.end(), loop) == routes.end()) {
    1417         [ +  - ]:          5 :         routes.push_back(loop);
    1418                 :            :       }
    1419                 :            :     });
    1420                 :            : 
    1421         [ +  + ]:       4193 :     if VLIKELY (routes.empty()) {
    1422         [ +  - ]:       4190 :       lock.lock();
    1423                 :            : 
    1424                 :       4190 :       sub_->release(read_msg);
    1425         [ +  - ]:       4190 :       lock.unlock();
    1426                 :            : 
    1427         [ +  + ]:       4190 :       if (sem_) {
    1428         [ +  - ]:          1 :         sem_->release();
    1429                 :            :       }
    1430                 :            : 
    1431                 :       4190 :       continue;
    1432                 :            :     }
    1433                 :            : 
    1434                 :          3 :     std::shared_ptr<const uint8_t> retained(read_msg, [self = shared_from_this()](const uint8_t* data) {
    1435                 :            :       {
    1436         [ +  - ]:          3 :         std::lock_guard lock(self->receive_mtx_);
    1437                 :            : 
    1438                 :          3 :         self->sub_->release(data);
    1439                 :          3 :       }
    1440                 :            : 
    1441         [ -  + ]:          3 :       if (self->sem_) {
    1442                 :          0 :         self->sem_->release();
    1443                 :            :       }
    1444   [ +  -  +  - ]:          6 :     });
    1445                 :            : 
    1446         [ +  + ]:          8 :     for (auto* loop : routes) {
    1447   [ +  -  +  - ]:          5 :       loop->post_task([this, loop, channel, retained, size = msg_bytes.size()]() {
    1448                 :          5 :         auto bytes = Bytes::shallow_copy(retained.get() + ShmFactory::get_loaned_offset(), size);
    1449                 :            : 
    1450   [ +  -  +  - ]:          5 :         traverse_msg_callback([loop, channel, &bytes](NodeImpl* impl, const auto& callback) {
    1451                 :         10 :           const auto* conf = impl->get_target_conf<ShmConf>();
    1452                 :            : 
    1453   [ +  +  +  -  :         15 :           if (impl->get_message_loop() == loop && !impl->has_suspend.load(std::memory_order_acquire) &&
                   +  + ]
    1454         [ +  - ]:          5 :               static_cast<uint64_t>(conf->hash_code) == channel) {
    1455                 :          5 :             callback(bytes);
    1456                 :            :           }
    1457                 :         10 :         });
    1458                 :          5 :       });
    1459                 :            :     }
    1460   [ +  +  +  +  :      16763 :   }
          +  -  +  +  -  
                      + ]
    1461                 :       3007 : }
    1462                 :            : 
    1463                 :         80 : void ShmSubscriber::subscribe() { sub_->subscribe(); }
    1464                 :            : 
    1465                 :          0 : void ShmSubscriber::unsubscribe() { sub_->unsubscribe(); }
    1466                 :            : 
    1467                 :         86 : void ShmSubscriber::set_latency_and_lost_enabled(bool enable) {
    1468                 :         86 :   is_latency_and_lost_enabled_.store(enable, std::memory_order_release);
    1469                 :         86 : }
    1470                 :            : 
    1471                 :          6 : bool ShmSubscriber::is_latency_and_lost_enabled() const {
    1472                 :          6 :   return is_latency_and_lost_enabled_.load(std::memory_order_acquire);
    1473                 :            : }
    1474                 :            : 
    1475                 :          2 : const CalculateSample& ShmSubscriber::get_calculate_sample() const { return calc_sample_; }
    1476                 :            : 
    1477                 :       3008 : void ShmSubscriber::on_msg_received(shm::popo::UntypedSubscriber*, ShmSubscriber* target) {
    1478         [ +  - ]:       3008 :   std::lock_guard callback_lock(target->callback_mtx_);
    1479                 :            : 
    1480         [ -  + ]:       3008 :   if VUNLIKELY (target->quit_flag_.load(std::memory_order_acquire)) {
    1481                 :          0 :     return;
    1482                 :            :   }
    1483                 :            : 
    1484         [ +  + ]:       3008 :   if VUNLIKELY (target->is_suspend_.load(std::memory_order_relaxed)) {
    1485                 :          1 :     target->sub_->releaseQueuedData();
    1486                 :          1 :     return;
    1487                 :            :   }
    1488                 :            : 
    1489                 :       3007 :   MessageLoop* message_loop = nullptr;
    1490                 :       3007 :   bool first = true;
    1491                 :       3007 :   bool common_loop = true;
    1492                 :            : 
    1493   [ +  -  +  - ]:       3007 :   target->traverse_msg_callback([&](NodeImpl* impl, const auto&) {
    1494         [ +  + ]:       3138 :     if (first) {
    1495                 :       3007 :       message_loop = impl->get_message_loop();
    1496                 :       3007 :       first = false;
    1497         [ +  + ]:        131 :     } else if (message_loop != impl->get_message_loop()) {
    1498                 :          3 :       common_loop = false;
    1499                 :            :     }
    1500                 :       3138 :   });
    1501                 :            : 
    1502   [ +  +  +  + ]:       3007 :   if (common_loop && message_loop) {
    1503                 :          4 :     std::weak_ptr<ShmSubscriber> weak_target = target->weak_from_this();
    1504                 :            : 
    1505   [ +  -  +  - ]:          4 :     message_loop->post_task([weak_target, message_loop]() {
    1506                 :          4 :       auto target = weak_target.lock();
    1507                 :            : 
    1508         [ -  + ]:          4 :       if VUNLIKELY (!target) {
    1509                 :          0 :         return;
    1510                 :            :       }
    1511                 :            : 
    1512         [ +  - ]:          4 :       target->process_message(message_loop);
    1513         [ +  - ]:          4 :     });
    1514                 :          4 :   } else {
    1515         [ +  - ]:       3003 :     target->process_message();
    1516                 :            :   }
    1517         [ +  + ]:       3008 : }
    1518                 :            : 
    1519                 :            : }  // namespace vlink

Generated by: LCOV version 1.14