LCOV - code coverage report
Current view: top level - modules/intra - intra_factory.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 169 179 94.4 %
Date: 2026-09-30 18:30:16 Functions: 33 37 89.2 %
Branches: 199 322 61.8 %

           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 "./intra_factory.h"
      25                 :            : 
      26                 :            : #include <algorithm>
      27                 :            : #include <type_traits>
      28                 :            : #include <utility>
      29                 :            : 
      30                 :            : #include "./base/memory_resource.h"
      31                 :            : #include "./base/utils.h"
      32                 :            : 
      33                 :            : namespace vlink {
      34                 :            : 
      35                 :            : constexpr size_t kMaxTaskSize = 10000U;
      36                 :            : 
      37                 :            : // IntraPipeline
      38                 :        547 : size_t IntraPipeline::get_max_task_count() const { return kMaxTaskSize; }
      39                 :            : 
      40                 :            : // IntraFactory
      41                 :        174 : IntraPipeline& IntraFactory::get_pipeline(int32_t pipeline) {
      42         [ +  - ]:        174 :   std::lock_guard lock(pipeline_mtx_);
      43                 :            : 
      44         [ +  - ]:        174 :   auto [iter, inserted] = pipeline_map_.try_emplace(pipeline);
      45                 :            : 
      46         [ +  + ]:        174 :   if (inserted) {
      47   [ +  -  +  -  :         16 :     iter->second.set_name("INTRA-PIPELINE-" + std::to_string(pipeline));
                   +  - ]
      48         [ +  - ]:         16 :     iter->second.async_run();
      49                 :            :   }
      50                 :            : 
      51                 :        348 :   return iter->second;
      52                 :        174 : }
      53                 :            : 
      54         [ +  - ]:         14 : IntraFactory::IntraFactory() {
      55                 :         14 :   Bytes::init_memory_pool();
      56                 :            : 
      57         [ -  + ]:         14 :   if VUNLIKELY (IntraConf::get_thread_count() != 1) {
      58   [ #  #  #  #  :          0 :     VLOG_W("IntraFactory: Intra does not support setting thread count.");
                   #  # ]
      59                 :            :   }
      60                 :         14 : }
      61                 :            : 
      62                 :         14 : IntraFactory::~IntraFactory() {
      63                 :            : #ifdef _WIN32
      64                 :            :   if (Utils::is_terminating()) {
      65                 :            :     return;
      66                 :            :   }
      67                 :            : #endif
      68                 :            : 
      69         [ +  + ]:         30 :   for (auto& [num, pipeline] : pipeline_map_) {
      70                 :         16 :     pipeline.quit(true);
      71                 :         16 :     pipeline.wait_for_quit();
      72                 :            :   }
      73                 :            : 
      74                 :         14 :   pipeline_map_.clear();
      75                 :         14 : }
      76                 :            : 
      77                 :            : // IntraNode
      78                 :        226 : IntraNode::IntraNode(const IntraID& id) {
      79                 :        226 :   const auto& [impl_type, address, pipeline, type, channel] = id;
      80                 :            : 
      81   [ +  +  +  -  :        226 :   static auto& factory = IntraFactory::get();
             +  -  -  - ]
      82                 :            : 
      83         [ +  + ]:        226 :   if (type == IntraType::kQueue) {
      84         [ +  - ]:        174 :     pipeline_ = &(factory.get_pipeline(pipeline));
      85                 :            :   }
      86                 :        226 : }
      87                 :            : 
      88                 :        452 : IntraNode::~IntraNode() = default;
      89                 :            : 
      90         [ #  # ]:          0 : std::any IntraNode::get_native_handle() const { return this; }
      91                 :            : 
      92                 :            : template <typename DataT>
      93                 :        518 : void IntraNode::deliver_data(MessageLoop* target_loop, uint32_t channel, const DataT& data) {
      94                 :       3383 :   auto deliver = [this, target_loop, channel, &data](NodeImpl* impl, const auto& callback) {
      95                 :        576 :     const auto* conf = impl->get_target_conf<IntraConf>();
      96                 :        576 :     auto* loop = impl->get_message_loop();
      97                 :            : 
      98   [ +  +  +  + ]:        576 :     if (!loop) {
      99                 :        567 :       loop = pipeline_;
     100                 :            :     }
     101                 :            : 
     102   [ +  -  -  +  :        576 :     if VUNLIKELY (conf->hash_code != channel || impl->has_suspend || loop != target_loop) {
          +  -  +  +  +  
          +  +  -  +  +  
          +  +  +  +  +  
                      + ]
     103                 :          5 :       return;
     104                 :            :     }
     105                 :            : 
     106                 :        571 :     callback(data);
     107                 :            :   };
     108                 :            : 
     109                 :            :   if constexpr (std::is_same_v<DataT, Bytes>) {
     110   [ +  -  +  - ]:        498 :     traverse_msg_callback(deliver);
     111                 :            :   } else {
     112   [ +  -  +  - ]:         20 :     traverse_intra_msg_callback(deliver);
     113                 :            :   }
     114                 :        518 : }
     115                 :            : 
     116                 :            : template <typename DataT>
     117                 :        570 : bool IntraNode::publish_data(IntraType type, uint32_t channel, const DataT& data) {
     118                 :        570 :   MessageLoop* first_loop = nullptr;
     119                 :        570 :   std::vector<MessageLoop*> other_loops;
     120                 :        571 :   bool delivered = false;
     121                 :            : 
     122                 :       4040 :   auto route = [this, channel, &data, &first_loop, &other_loops, &delivered](NodeImpl* impl, const auto& callback) {
     123   [ +  -  +  - ]:        695 :     const auto* conf = impl->get_target_conf<IntraConf>();
     124                 :            : 
     125   [ +  -  -  +  :        695 :     if VUNLIKELY (conf->hash_code != channel || impl->has_suspend) {
          -  +  +  -  +  
                +  +  + ]
     126                 :          3 :       return;
     127                 :            :     }
     128                 :            : 
     129   [ +  -  +  - ]:        692 :     auto* loop = impl->get_message_loop();
     130                 :            : 
     131   [ +  +  +  + ]:        692 :     if (!loop) {
     132                 :        687 :       loop = pipeline_;
     133                 :            :     }
     134                 :            : 
     135   [ -  +  +  + ]:        692 :     if (!loop) {
     136   [ -  -  +  - ]:         51 :       callback(data);
     137                 :         51 :       delivered = true;
     138   [ +  +  +  + ]:        641 :     } else if (!first_loop) {
     139                 :        514 :       first_loop = loop;
     140   [ +  -  +  -  :        127 :     } else if (loop != first_loop && std::find(other_loops.begin(), other_loops.end(), loop) == other_loops.end()) {
          +  -  +  -  +  
          +  +  -  +  -  
                   +  + ]
     141   [ +  -  +  - ]:          2 :       other_loops.push_back(loop);
     142                 :            :     }
     143                 :            :   };
     144                 :            : 
     145                 :            :   if constexpr (std::is_same_v<DataT, Bytes>) {
     146   [ +  -  +  - ]:        552 :     traverse_msg_callback(route);
     147                 :            :   } else {
     148   [ +  -  +  - ]:         19 :     traverse_intra_msg_callback(route);
     149                 :            :   }
     150                 :            : 
     151         [ +  + ]:        571 :   if (!first_loop) {
     152   [ +  +  +  + ]:         57 :     if (delivered || type != IntraType::kQueue) {
     153                 :         49 :       return delivered;
     154                 :            :     }
     155                 :            : 
     156                 :          8 :     first_loop = pipeline_;
     157                 :            :   }
     158                 :            : 
     159                 :        522 :   auto weak_self = weak_from_this();
     160                 :            : 
     161         [ +  + ]:        522 :   if (other_loops.empty()) {
     162   [ +  -  +  - ]:       2074 :     return first_loop->post_task([weak_self, first_loop, channel, data]() {
     163                 :        520 :       auto self = weak_self.lock();
     164                 :            : 
     165   [ -  +  +  + ]:        520 :       if VUNLIKELY (!self) {
     166                 :          6 :         return;
     167                 :            :       }
     168                 :            : 
     169   [ +  -  +  - ]:        514 :       self->deliver_data(first_loop, channel, data);
     170   [ +  -  +  + ]:       1040 :     });
     171                 :            :   }
     172                 :            : 
     173                 :            :   if constexpr (std::is_same_v<DataT, Bytes>) {
     174         [ +  - ]:          1 :     auto shared_data = MemoryResource::make_shared<Bytes>(data);
     175                 :          5 :     auto post = [&weak_self, channel, &shared_data](MessageLoop* loop) {
     176   [ +  -  +  - ]:          4 :       return loop->post_task([weak_self, loop, channel, shared_data]() {
     177                 :          2 :         auto self = weak_self.lock();
     178                 :            : 
     179         [ -  + ]:          2 :         if VUNLIKELY (!self) {
     180                 :          0 :           return;
     181                 :            :         }
     182                 :            : 
     183         [ +  - ]:          2 :         self->deliver_data(loop, channel, *shared_data);
     184         [ +  - ]:          6 :       });
     185                 :            :     };
     186                 :            : 
     187         [ +  - ]:          1 :     bool result = post(first_loop);
     188                 :            : 
     189         [ +  + ]:          2 :     for (auto* loop : other_loops) {
     190   [ +  -  +  -  :          1 :       result = post(loop) && result;
                   +  - ]
     191                 :            :     }
     192                 :            : 
     193                 :          1 :     return result;
     194                 :          1 :   } else {
     195                 :          5 :     auto post = [&weak_self, channel, &data](MessageLoop* loop) {
     196   [ +  -  +  - ]:          4 :       return loop->post_task([weak_self, loop, channel, data]() {
     197                 :          2 :         auto self = weak_self.lock();
     198                 :            : 
     199         [ -  + ]:          2 :         if VUNLIKELY (!self) {
     200                 :          0 :           return;
     201                 :            :         }
     202                 :            : 
     203         [ +  - ]:          2 :         self->deliver_data(loop, channel, data);
     204         [ +  - ]:          6 :       });
     205                 :            :     };
     206                 :            : 
     207         [ +  - ]:          1 :     bool result = post(first_loop);
     208                 :            : 
     209         [ +  + ]:          2 :     for (auto* loop : other_loops) {
     210   [ +  -  +  -  :          1 :       result = post(loop) && result;
                   +  - ]
     211                 :            :     }
     212                 :            : 
     213                 :          1 :     return result;
     214                 :            :   }
     215                 :        571 : }
     216                 :            : 
     217                 :        551 : bool IntraNode::publish(IntraType type, uint32_t channel, const Bytes& msg_data) {
     218                 :        551 :   return publish_data(type, channel, msg_data);
     219                 :            : }
     220                 :            : 
     221                 :         19 : bool IntraNode::publish(IntraType type, uint32_t channel, const IntraData& intra_data) {
     222                 :         19 :   return publish_data(type, channel, intra_data);
     223                 :            : }
     224                 :            : 
     225                 :         91 : void IntraNode::deliver_request(NodeImpl* server, NodeImpl* requester, MessageLoop* target_loop, const Bytes& request,
     226                 :            :                                 const NodeImpl::MsgCallback& callback) {
     227                 :         91 :   Bytes response;
     228                 :         91 :   bool replied = false;
     229                 :            : 
     230   [ +  -  +  - ]:         91 :   invoke_req_resp_callback(server, [&, this](NodeImpl* impl, const auto& handler) {
     231                 :         91 :     auto* loop = impl->get_message_loop();
     232                 :            : 
     233         [ +  + ]:         91 :     if (!loop) {
     234                 :         85 :       loop = pipeline_;
     235                 :            :     }
     236                 :            : 
     237   [ +  -  -  +  :         91 :     if VUNLIKELY (!is_contains_impl(requester) || impl->has_suspend || loop != target_loop) {
          +  -  -  +  -  
                      + ]
     238                 :          0 :       return;
     239                 :            :     }
     240                 :            : 
     241         [ +  + ]:         91 :     if (callback) {
     242                 :         78 :       handler(0, request, &response);
     243                 :         78 :       replied = true;
     244                 :            :     } else {
     245                 :         13 :       handler(0, request, nullptr);
     246                 :            :     }
     247                 :            :   });
     248                 :            : 
     249         [ +  + ]:         91 :   if (replied) {
     250         [ +  - ]:         78 :     callback(response);
     251                 :            :   }
     252                 :         91 : }
     253                 :            : 
     254                 :         97 : bool IntraNode::call(NodeImpl* requester, IntraType type, uint32_t channel, const Bytes& req_data,
     255                 :            :                      NodeImpl::MsgCallback&& callback, bool inline_if_same_thread) {
     256                 :         97 :   auto weak_self = weak_from_this();
     257                 :            : 
     258         [ +  + ]:         97 :   if (callback) {
     259                 :         80 :     auto response_callback = std::move(callback);
     260                 :            : 
     261                 :         78 :     callback = [weak_self, requester, inline_if_same_thread,
     262                 :        167 :                 response_callback = std::move(response_callback)](const Bytes& response) mutable {
     263                 :         78 :       auto self = weak_self.lock();
     264                 :            : 
     265         [ -  + ]:         78 :       if VUNLIKELY (!self) {
     266                 :          0 :         return;
     267                 :            :       }
     268                 :            : 
     269                 :         78 :       MessageLoop* loop = nullptr;
     270                 :            : 
     271   [ +  -  +  - ]:         78 :       self->invoke_callback(requester, [&]() {
     272                 :         78 :         loop = requester->get_message_loop();
     273                 :            : 
     274   [ +  +  +  + ]:         78 :         if (!loop || inline_if_same_thread) {
     275                 :         75 :           response_callback(response);
     276                 :            :         }
     277                 :         78 :       });
     278                 :            : 
     279   [ +  +  +  + ]:         78 :       if (loop && !inline_if_same_thread) {
     280   [ +  -  +  - ]:          3 :         loop->post_task([weak_self, requester, loop, response, response_callback = std::move(response_callback)]() {
     281                 :          3 :           auto self = weak_self.lock();
     282                 :            : 
     283         [ -  + ]:          3 :           if VUNLIKELY (!self) {
     284                 :          0 :             return;
     285                 :            :           }
     286                 :            : 
     287                 :          3 :           bool attached = false;
     288   [ +  -  +  - ]:          6 :           self->invoke_callback(requester, [&]() { attached = requester->get_message_loop() == loop; });
     289                 :            : 
     290         [ +  - ]:          3 :           if (attached) {
     291         [ +  - ]:          3 :             response_callback(response);
     292                 :            :           }
     293         [ +  - ]:          3 :         });
     294                 :            :       }
     295   [ +  -  +  - ]:        158 :     };
     296                 :         80 :   }
     297                 :            : 
     298                 :         97 :   bool found = false;
     299                 :         97 :   bool result = false;
     300                 :         97 :   NodeImpl* server = nullptr;
     301                 :         97 :   MessageLoop* target_loop = nullptr;
     302                 :            : 
     303   [ +  -  +  - ]:         97 :   traverse_req_resp_callback([&](NodeImpl* impl, const auto&) {
     304                 :         94 :     const auto* conf = impl->get_target_conf<IntraConf>();
     305                 :            : 
     306   [ +  -  +  +  :         94 :     if VUNLIKELY (conf->hash_code != channel || impl->has_suspend) {
                   +  + ]
     307                 :          3 :       return;
     308                 :            :     }
     309                 :            : 
     310         [ -  + ]:         91 :     if VUNLIKELY (found) {
     311   [ #  #  #  #  :          0 :       VLOG_F(*conf, "Two identical service requests.");
                   #  # ]
     312                 :          0 :       return;
     313                 :            :     }
     314                 :            : 
     315                 :         91 :     found = true;
     316                 :         91 :     server = impl;
     317                 :         91 :     target_loop = impl->get_message_loop();
     318                 :            : 
     319         [ +  + ]:         91 :     if (!target_loop) {
     320                 :         85 :       target_loop = pipeline_;
     321                 :            :     }
     322                 :            :   });
     323                 :            : 
     324         [ +  + ]:         97 :   if (server) {
     325   [ +  +  +  +  :         91 :     if (!target_loop || (inline_if_same_thread && target_loop->is_in_same_thread())) {
          +  -  +  +  +  
                      + ]
     326         [ +  - ]:         60 :       deliver_request(server, requester, target_loop, req_data, callback);
     327                 :         60 :       result = true;
     328                 :            :     } else {
     329   [ +  -  +  - ]:         62 :       result = target_loop->post_task(
     330                 :         31 :           [weak_self, server, requester, target_loop, req_data, callback = std::move(callback)]() {
     331                 :         31 :             auto self = weak_self.lock();
     332                 :            : 
     333         [ -  + ]:         31 :             if VUNLIKELY (!self) {
     334                 :          0 :               return;
     335                 :            :             }
     336                 :            : 
     337         [ +  - ]:         31 :             self->deliver_request(server, requester, target_loop, req_data, callback);
     338         [ +  - ]:         31 :           });
     339                 :            :     }
     340                 :            :   }
     341                 :            : 
     342   [ +  +  +  + ]:         97 :   if (!found && type == IntraType::kQueue) {
     343   [ +  -  +  - ]:          4 :     return pipeline_->post_task([weak_self, requester, channel, req_data, callback = std::move(callback)]() mutable {
     344   [ +  -  +  -  :          2 :       if (auto self = weak_self.lock(); self && self->is_contains_impl(requester)) {
             +  -  +  - ]
     345         [ +  - ]:          2 :         self->call(requester, IntraType::kDirect, channel, req_data, std::move(callback), true);
     346                 :          2 :       }
     347                 :          4 :     });
     348                 :            :   }
     349                 :            : 
     350                 :         95 :   return result;
     351                 :         97 : }
     352                 :            : 
     353                 :            : }  // namespace vlink

Generated by: LCOV version 1.14