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

Generated by: LCOV version 1.14