VLink  2.1.0
A high-performance communication middleware
node-inl.h
Go to the documentation of this file.
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 #pragma once
25 
26 #include <memory>
27 #include <mutex>
28 #include <string>
29 #include <utility>
30 
31 #include "../base/logger.h"
32 #include "../impl/types.h"
33 #include "../node.h"
34 #include "../version.h"
35 
36 namespace vlink {
37 
38 template <typename ImplT, SecurityType SecT>
39 inline bool Node<ImplT, SecT>::init() {
40  if constexpr (SecT == SecurityType::kWithSecurity) {
41  if VUNLIKELY (!impl_->security) {
42  VLOG_F("Node::init(): security node has no usable Security; check Security::Config. url: ", impl_->url);
43  }
44  }
45 
46  bool expected = false;
47 
48  if VUNLIKELY (!has_inited_.compare_exchange_strong(expected, true, std::memory_order_acq_rel,
49  std::memory_order_relaxed)) {
50  return false;
51  }
52 
54 
55  impl_->init();
56  impl_->init_ext();
57 
58  is_support_loan_ = impl_->is_support_loan();
59 
60  return true;
61 }
62 
63 template <typename ImplT, SecurityType SecT>
65  bool expected = true;
66 
67  if VUNLIKELY (!has_inited_.compare_exchange_strong(expected, false, std::memory_order_acq_rel,
68  std::memory_order_relaxed)) {
69  return false;
70  }
71 
72  interrupt(); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
73 
74  if (quit_mtx_.has_value()) {
75  std::lock_guard quit_lock(quit_mtx_.value());
76  impl_->deinit();
77  impl_->deinit_ext();
78  } else {
79  impl_->deinit();
80  impl_->deinit_ext();
81  }
82 
83  return true;
84 }
85 
86 template <typename ImplT, SecurityType SecT>
87 inline bool Node<ImplT, SecT>::has_inited() const {
88  return has_inited_.load(std::memory_order_acquire);
89 }
90 
91 template <typename ImplT, SecurityType SecT>
93  return impl_->is_support_loan();
94 }
95 
96 template <typename ImplT, SecurityType SecT>
97 inline Bytes Node<ImplT, SecT>::loan(int64_t size) {
98  Bytes bytes = impl_->loan(size);
99 
100  return bytes;
101 }
102 
103 template <typename ImplT, SecurityType SecT>
104 inline bool Node<ImplT, SecT>::return_loan(const Bytes& bytes) {
105  return impl_->return_loan(bytes);
106 }
107 
108 template <typename ImplT, SecurityType SecT>
110  return impl_->suspend();
111 }
112 
113 template <typename ImplT, SecurityType SecT>
115  return impl_->resume();
116 }
117 
118 template <typename ImplT, SecurityType SecT>
119 inline bool Node<ImplT, SecT>::is_suspend() const {
120  return impl_->is_suspend();
121 }
122 
123 template <typename ImplT, SecurityType SecT>
124 inline bool Node<ImplT, SecT>::attach(class MessageLoop* message_loop) {
125  return impl_->attach(message_loop);
126 }
127 
128 template <typename ImplT, SecurityType SecT>
130  return impl_->detach();
131 }
132 
133 template <typename ImplT, SecurityType SecT>
135  return impl_->get_message_loop();
136 }
137 
138 template <typename ImplT, SecurityType SecT>
140  return impl_->interrupt();
141 }
142 
143 template <typename ImplT, SecurityType SecT>
145  return impl_->get_abstract_node();
146 }
147 
148 template <typename ImplT, SecurityType SecT>
150  return impl_->get_status(type);
151 }
152 
153 template <typename ImplT, SecurityType SecT>
155  impl_->register_status_handler(std::move(callback));
156 }
157 
158 template <typename ImplT, SecurityType SecT>
159 inline void Node<ImplT, SecT>::set_property(const std::string& prop, const std::string& value) {
160  impl_->set_property(prop, value);
161 }
162 
163 template <typename ImplT, SecurityType SecT>
164 inline std::string Node<ImplT, SecT>::get_property(const std::string& prop) const {
165  return impl_->get_property(prop);
166 }
167 
168 template <typename ImplT, SecurityType SecT>
170  return impl_->transport_type;
171 }
172 
173 template <typename ImplT, SecurityType SecT>
174 inline const std::string& Node<ImplT, SecT>::get_url() const {
175  return impl_->url;
176 }
177 
178 template <typename ImplT, SecurityType SecT>
179 inline void Node<ImplT, SecT>::set_record_path(const std::string& path) {
180  if VUNLIKELY (impl_->transport_type == TransportType::kIntra) {
181  VLOG_F("Node: Intra type does not support record.");
182  return;
183  }
184 
185  impl_->set_record_path(path);
186 }
187 
188 template <typename ImplT, SecurityType SecT>
189 inline void Node<ImplT, SecT>::set_ser_type(const std::string& ser_type, SchemaType schema_type) {
190  auto next_schema_type = impl_->schema_type;
191 
192  if (ser_type.empty()) {
193  next_schema_type = SchemaType::kUnknown;
194  } else if (SchemaData::is_valid_type(schema_type) && schema_type != SchemaType::kUnknown) {
195  next_schema_type = schema_type;
196  } else {
197  const auto inferred_schema_type = SchemaData::infer_ser_type(ser_type);
198 
199  if VLIKELY (inferred_schema_type != SchemaType::kUnknown) {
200  next_schema_type = inferred_schema_type;
201  } else if (impl_->schema_type == SchemaType::kRaw || impl_->schema_type == SchemaType::kZeroCopy) {
202  next_schema_type = SchemaType::kUnknown;
203  }
204  }
205 
206  const bool ser_changed = impl_->ser_type != ser_type;
207  const bool schema_changed = impl_->schema_type != next_schema_type;
208  const bool next_is_cdr_type = next_schema_type == SchemaType::kCdr;
209 
210  if VLIKELY (!ser_changed && !schema_changed) {
211  return;
212  }
213 
214  const bool has_inited = has_inited_.load(std::memory_order_acquire);
215  if VUNLIKELY (has_inited && impl_->transport_type == TransportType::kDds &&
216  (impl_->is_cdr_type != next_is_cdr_type || (impl_->is_cdr_type && ser_changed))) {
217  VLOG_F(
218  "Node: DDS raw/CDR mode and CDR type name cannot be changed while initialised; call "
219  "deinit() before changing serialization metadata.");
220  }
221 
222  if VUNLIKELY (ser_changed && !impl_->ser_type.empty() && !ser_type.empty()) {
223  CLOG_W("Node: Enforce serialization type [%s] => [%s].", impl_->ser_type.c_str(), ser_type.c_str());
224  }
225 
226  if VUNLIKELY (schema_changed && SchemaData::is_real_type(impl_->schema_type) &&
227  SchemaData::is_real_type(next_schema_type)) {
228  CLOG_W("Node: Enforce schema type [%d] => [%d].", static_cast<int>(impl_->schema_type),
229  static_cast<int>(next_schema_type));
230  }
231 
232  if VUNLIKELY (has_inited) {
233  impl_->deinit_ext();
234  }
235 
236  impl_->ser_type = ser_type;
237  impl_->schema_type = next_schema_type;
238 
239  if (impl_->transport_type == TransportType::kDds) {
240  impl_->is_cdr_type = next_is_cdr_type;
241  if constexpr (VLINK_HAS_MEMBER(ImplT, is_resp_cdr_type)) {
242  if (impl_->is_resp_type) {
243  impl_->is_resp_cdr_type = next_is_cdr_type;
244  }
245  }
246  }
247 
248  if VUNLIKELY (has_inited) {
249  impl_->init_ext();
250  }
251 }
252 
253 template <typename ImplT, SecurityType SecT>
254 inline const std::string& Node<ImplT, SecT>::get_ser_type() const {
255  return impl_->ser_type;
256 }
257 
258 template <typename ImplT, SecurityType SecT>
260  return impl_->schema_type;
261 }
262 
263 template <typename ImplT, SecurityType SecT>
265  if VUNLIKELY (has_inited_.load(std::memory_order_acquire)) {
266  impl_->deinit_ext();
267  impl_->set_discovery_enabled(enable);
268  impl_->init_ext();
269  } else {
270  impl_->set_discovery_enabled(enable);
271  }
272 }
273 
274 template <typename ImplT, SecurityType SecT>
276  return impl_->get_discovery_enabled();
277 }
278 
279 template <typename ImplT, SecurityType SecT>
280 inline void Node<ImplT, SecT>::bind_proto_arena(void* proto_arena) {
281  proto_arena_ = proto_arena;
282 }
283 
284 template <typename ImplT, SecurityType SecT>
285 inline double Node<ImplT, SecT>::get_cpu_usage() const {
286  if VUNLIKELY (impl_->profiler) {
287  return impl_->profiler->get();
288  } else {
289  return -1;
290  }
291 }
292 
293 template <typename ImplT, SecurityType SecT>
295  return quit_mtx_.has_value();
296 }
297 
298 template <typename ImplT, SecurityType SecT>
299 inline void Node<ImplT, SecT>::set_safety_quit(bool safety_quit) {
300  if VUNLIKELY (safety_quit) {
301  if (!quit_mtx_.has_value()) {
302  quit_mtx_.emplace();
303  }
304  } else {
305  if (quit_mtx_.has_value()) {
306  quit_mtx_.reset();
307  }
308  }
309 }
310 
311 template <typename ImplT, SecurityType SecT>
312 inline void Node<ImplT, SecT>::set_ssl_options(const SslOptions& options) {
313  impl_->set_ssl_options(options);
314 }
315 
316 template <typename ImplT, SecurityType SecT>
318  static_assert(std::is_base_of_v<NodeImpl, ImplT>, "ImplT must be derived from NodeImpl.");
319 }
320 
321 template <typename ImplT, SecurityType SecT>
323  deinit(); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
324 }
325 
326 template <typename ImplT, SecurityType SecT>
328  auto sec_cfg = cfg;
329 
330  return enable_security(std::move(sec_cfg));
331 }
332 
333 template <typename ImplT, SecurityType SecT>
335  static_assert(SecT == SecurityType::kWithSecurity, "Must be security type.");
336 
337  if VUNLIKELY (!impl_) {
338  return false;
339  }
340 
341  if VUNLIKELY (has_inited_.load(std::memory_order_acquire)) {
342  VLOG_W("Node::enable_security(): must run before init(); rejected to avoid live-traffic race.");
343  return false;
344  }
345 
346  return impl_->enable_security(std::move(cfg));
347 }
348 
349 template <typename ImplT, SecurityType SecT>
350 template <typename CallbackT, typename... ArgsT>
351 inline void Node<ImplT, SecT>::invoke_callback(const CallbackT& callback, ArgsT&&... args) {
352  if VUNLIKELY (quit_mtx_.has_value()) {
353  std::lock_guard quit_lock(quit_mtx_.value());
354  std::invoke(callback, std::forward<ArgsT>(args)...);
355  } else {
356  std::invoke(callback, std::forward<ArgsT>(args)...);
357  }
358 }
359 
360 template <typename ImplT, SecurityType SecT>
361 template <typename TypeT>
363  if constexpr (Traits::IsSharedPtr<TypeT>()) {
364  return std::make_shared<typename TypeT::element_type>();
365 #ifdef VLINK_HAS_PROTOBUF
366  } else if constexpr (Serializer::is_proto_ptr_type<TypeT>()) {
367  if VLIKELY (this->proto_arena_) {
368  return google::protobuf::Arena::Create<std::remove_pointer_t<TypeT>>(
369  static_cast<google::protobuf::Arena*>(this->proto_arena_));
370  }
371 
372  VLOG_F("Node: Proto arena is not bound, url: ", this->impl_->url, ".");
373 
374  return nullptr;
375 #endif
376  } else if constexpr (std::is_default_constructible_v<TypeT>) {
377  return TypeT{};
378  } else {
379  static_assert(Traits::ExpectFalse<TypeT>(), "TypeT is not default constructible.");
380  return {};
381  }
382 }
383 
384 } // namespace vlink
#define VLOG_F(...)
Definition: logger.h:843
#define VLOG_W(...)
Definition: logger.h:839
#define CLOG_W(...)
Definition: logger.h:851
#define VUNLIKELY(...)
Short alias for VLINK_UNLIKELY.
Definition: macros.h:289
#define VLIKELY(...)
Short alias for VLINK_LIKELY.
Definition: macros.h:284
#define VLINK_HAS_MEMBER(T, member)
Macro Definitions.
Definition: traits.h:316
#define VLINK_VERSION_PATCH
Definition: version.h:94
#define VLINK_VERSION_MAJOR
Definition: version.h:92
#define VLINK_VERSION_MINOR
Definition: version.h:93