VLink  2.1.0
A high-performance communication middleware
abstract_factory.h
浏览该文件的文档.
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 /**
25  * @file abstract_factory.h
26  * @brief Topic-scoped registration store that fans transport callbacks across @c NodeImpl peers.
27  *
28  * @details
29  * This is an internal implementation header used by the public VLink node templates
30  * (@c Publisher, @c Subscriber, @c Client, @c Server, @c Setter, @c Getter); it should not
31  * be included directly by application code. The header introduces two co-operating
32  * class templates that let several @c NodeImpl instances bound to the same logical
33  * topic share one registration record:
34  *
35  * - @c AbstractObject -- a per-topic record that owns the set of registered
36  * @c NodeImpl pointers together with six callback dictionaries (server connect,
37  * subscriber connect, request/response, serialised message, intra-process message
38  * and transport status).
39  * - @c AbstractFactory -- a thread-safe map keyed on @c FilterT (commonly the
40  * topic URL string) that lazily creates an @c AbstractObject for each key and
41  * reuses it via a cached @c std::weak_ptr until the last owner releases it.
42  *
43  * @par Registration flow
44  * @code
45  * +----------------------+
46  * | AbstractFactory<K> |
47  * | map<K, weak_ptr<O>> |
48  * +----------+-----------+
49  * | get_object<O>(key)
50  * v
51  * +----------------------+
52  * | AbstractObject<K> |
53  * | ImplList |
54  * | ConnectCallbackMap |
55  * | MsgCallbackMap ... |
56  * +----------+-----------+
57  * ^ | ^
58  * add_impl(impl) | | | register_msg_callback(impl, cb)
59  * remove_impl(impl)| | |
60  * | v
61  * +------+--+ +-+--------+ +----------+
62  * | NodeImpl| | NodeImpl | | NodeImpl |
63  * +---------+ +----------+ +----------+
64  * @endcode
65  *
66  * @par Registry keys
67  * | Map field | Callback signature |
68  * | ------------------------------- | ------------------------------------------------- |
69  * | @c server_connect_callback_map_ | @c void(bool) -- server side peer presence change |
70  * | @c sub_connect_callback_map_ | @c void(bool) -- subscriber side presence change |
71  * | @c req_resp_callback_map_ | @c void(uint64_t, const Bytes&, Bytes*) |
72  * | @c msg_callback_map_ | @c void(const Bytes&) |
73  * | @c intra_msg_callback_map_ | @c void(const IntraData&) |
74  * | @c status_callback_map_ | @c void(const Status::BasePtr&) |
75  *
76  * @par Example
77  * @code
78  * struct TopicObject final : vlink::AbstractObject<std::string> {
79  * using AbstractObject::AbstractObject;
80  * };
81  *
82  * vlink::AbstractFactory<std::string> factory;
83  *
84  * auto object = factory.get_object<TopicObject>("dds://my_topic");
85  * object->add_impl(impl);
86  * object->register_msg_callback(impl, [](const vlink::Bytes& bytes) {
87  * // forward each delivery to the owning node
88  * });
89  *
90  * object->traverse_msg_callback([](vlink::NodeImpl*, const vlink::NodeImpl::MsgCallback& cb) {
91  * cb(payload);
92  * });
93  * @endcode
94  *
95  * @note Every public @c AbstractObject method acquires the internal
96  * @c std::recursive_mutex; callbacks invoked through @c traverse_*() execute
97  * under that lock and may re-enter the same @c AbstractObject safely.
98  *
99  * @tparam FilterT Key type used to identify topics inside the factory map.
100  */
101 
102 #pragma once
103 
104 #include <map>
105 #include <memory>
106 #include <mutex>
107 #include <unordered_set>
108 #include <utility>
109 #include <vector>
110 
111 #include "../base/functional.h"
112 #include "../base/logger.h"
113 #include "./node_impl.h"
114 
115 namespace vlink {
116 
117 /**
118  * @class AbstractObject
119  * @brief Topic-scoped fan-out store of @c NodeImpl peers and their callbacks.
120  *
121  * @details
122  * Holds the active @c NodeImpl pointer set together with the six callback
123  * dictionaries documented at file scope and serialises mutation, traversal and
124  * accounting through a @c std::recursive_mutex. The traversal helpers honour
125  * the @c ignore_called() escape hatch so that individual callbacks can opt out
126  * of the "any callback was invoked" accounting tracked by @c has_called().
127  * Mutations that arrive while a traversal is running on the same thread are
128  * deferred: removals queue up and apply once the outermost traversal unwinds,
129  * so a handler that removes its own @c NodeImpl keeps executing on a live
130  * callable without per-entry copies or shared ownership, while node-based
131  * callback storage keeps live traversal iterators valid across registrations.
132  *
133  * @tparam FilterT Key type used by the owning @c AbstractFactory for this object.
134  */
135 template <typename FilterT>
136 class AbstractObject : public AbstractNode {
137  public:
138  using ImplList = std::unordered_set<NodeImpl*>; ///< Set of currently registered @c NodeImpl peers.
139 
140  using ConnectCallbackMap = std::map<NodeImpl*, NodeImpl::ConnectCallback>; ///< Connect handlers per impl.
141  using ReqRespCallbackMap = std::map<NodeImpl*, NodeImpl::ReqRespCallback>; ///< Req/resp callbacks, keyed by impl.
142  using MsgCallbackMap = std::map<NodeImpl*, NodeImpl::MsgCallback>; ///< Message callbacks, keyed by impl.
143  using IntraMsgCallbackMap = std::map<NodeImpl*, NodeImpl::IntraMsgCallback>; ///< Intra-message callbacks.
144  using StatusCallbackMap = std::map<NodeImpl*, NodeImpl::StatusCallback>; ///< Status callbacks per impl.
145 
147  Function<void(NodeImpl*, const NodeImpl::ConnectCallback&)>; ///< Visitor invoked for each connect entry.
149  Function<void(NodeImpl*, const NodeImpl::ReqRespCallback&)>; ///< Visitor invoked for each req/resp entry.
151  Function<void(NodeImpl*, const NodeImpl::MsgCallback&)>; ///< Visitor invoked for each message entry.
153  Function<void(NodeImpl*, const NodeImpl::IntraMsgCallback&)>; ///< Visitor for each intra-message entry.
155  Function<void(NodeImpl*, const NodeImpl::StatusCallback&)>; ///< Visitor invoked for each status entry.
156 
157  /**
158  * @brief Registers @p impl as an active peer on this topic.
159  *
160  * @details
161  * Inserts @p impl into @c impl_list_ and refreshes the cached @c first_impl_
162  * pointer to the latest registrant. The operation is serialised against all
163  * other public methods.
164  *
165  * @param impl Non-owning peer pointer to track.
166  * @return @c true when the pointer was newly inserted; @c false when it was
167  * already present.
168  */
169  bool add_impl(NodeImpl* impl);
170 
171  /**
172  * @brief Removes @p impl from the peer set and forgets every associated callback.
173  *
174  * @details
175  * Erases the pointer from @c impl_list_, reassigns @c first_impl_ if needed
176  * and drops the entry from all six callback dictionaries. Thread-safe.
177  *
178  * @param impl Peer pointer previously passed to @c add_impl().
179  * @return @c true if @p impl was found and removed; @c false otherwise.
180  */
181  bool remove_impl(NodeImpl* impl);
182 
183  /**
184  * @brief Returns the most recently registered peer.
185  *
186  * @details
187  * The "first" pointer follows the latest successful @c add_impl() call. After
188  * a matching @c remove_impl() the cache is repopulated with an arbitrary
189  * remaining peer, or @c nullptr if the set has been drained.
190  *
191  * @return Pointer to the current cached peer; @c nullptr when no peer is registered.
192  */
193  [[nodiscard]] NodeImpl* get_first_impl() const;
194 
195  /**
196  * @brief Tests whether @p impl is currently part of the peer set.
197  *
198  * @param impl Pointer to query.
199  * @return @c true when @p impl is registered, @c false otherwise.
200  */
201  [[nodiscard]] bool is_contains_impl(NodeImpl* impl) const;
202 
203  /**
204  * @brief Indicates whether at least one peer has been registered.
205  *
206  * @return @c true when @c impl_list_ is non-empty.
207  */
208  [[nodiscard]] bool has_impl() const;
209 
210  /**
211  * @brief Stores @p callback as the server-side connect handler for @p impl.
212  *
213  * @param impl Peer that owns @p callback.
214  * @param callback Callable @c void(bool) invoked when a remote client comes or goes.
215  * @return @c true if the entry was inserted; @c false when one was already present.
216  */
218 
219  /**
220  * @brief Stores @p callback as the subscriber-side connect handler for @p impl.
221  *
222  * @param impl Peer that owns @p callback.
223  * @param callback Callable @c void(bool) invoked when a subscriber appears or disappears.
224  * @return @c true if the entry was inserted; @c false when one was already present.
225  */
227 
228  /**
229  * @brief Stores @p callback as the request/response handler for @p impl.
230  *
231  * @param impl Peer that owns @p callback.
232  * @param callback Callable invoked for every incoming RPC request.
233  * @return @c true on insertion; @c false when already registered.
234  */
236 
237  /**
238  * @brief Stores @p callback as the serialised-message handler for @p impl.
239  *
240  * @param impl Peer that owns @p callback.
241  * @param callback Callable @c void(const Bytes&) invoked for every received message.
242  * @return @c true on insertion; @c false when already registered.
243  */
244  bool register_msg_callback(NodeImpl* impl, NodeImpl::MsgCallback&& callback);
245 
246  /**
247  * @brief Stores @p callback as the intra-process message handler for @p impl.
248  *
249  * @param impl Peer that owns @p callback.
250  * @param callback Callable @c void(const IntraData&) invoked for each in-process delivery.
251  * @return @c true on insertion; @c false when already registered.
252  */
254 
255  /**
256  * @brief Stores @p callback as the transport-status handler for @p impl.
257  *
258  * @param impl Peer that owns @p callback.
259  * @param callback Callable invoked when the transport reports a status change.
260  * @return @c true on insertion; @c false when already registered.
261  */
263 
264  /**
265  * @brief Reports whether the server-connect dictionary is empty.
266  *
267  * @return @c true when no server-connect callbacks are registered.
268  */
269  [[nodiscard]] bool server_connect_map_is_empty() const;
270 
271  /**
272  * @brief Reports whether the subscriber-connect dictionary is empty.
273  *
274  * @return @c true when no subscriber-connect callbacks are registered.
275  */
276  [[nodiscard]] bool sub_connect_map_is_empty() const;
277 
278  /**
279  * @brief Reports whether the request/response dictionary is empty.
280  *
281  * @return @c true when no req/resp callbacks are registered.
282  */
283  [[nodiscard]] bool req_resp_map_is_empty() const;
284 
285  /**
286  * @brief Reports whether the serialised-message dictionary is empty.
287  *
288  * @return @c true when no message callbacks are registered.
289  */
290  [[nodiscard]] bool msg_map_is_empty() const;
291 
292  /**
293  * @brief Reports whether the intra-process message dictionary is empty.
294  *
295  * @return @c true when no intra-message callbacks are registered.
296  */
297  [[nodiscard]] bool intra_msg_map_is_empty() const;
298 
299  /**
300  * @brief Reports whether the transport-status dictionary is empty.
301  *
302  * @return @c true when no status callbacks are registered.
303  */
304  [[nodiscard]] bool status_map_is_empty() const;
305 
306  /**
307  * @brief Walks the server-connect dictionary and invokes @p callback for every entry.
308  *
309  * @details
310  * Iteration is performed while holding the recursive mutex. Individual visits
311  * may call @c ignore_called() to keep the current entry from setting the
312  * @c has_called() flag; iteration carries on regardless.
313  *
314  * @param callback Visitor receiving each peer pointer and its stored handler.
315  */
317 
318  /**
319  * @brief Walks the subscriber-connect dictionary and invokes @p callback for every entry.
320  *
321  * @param callback Visitor receiving each peer pointer and its stored handler.
322  */
324 
325  /**
326  * @brief Walks the request/response dictionary and invokes @p callback for every entry.
327  *
328  * @param callback Visitor receiving each peer pointer and its stored handler.
329  */
330  void traverse_req_resp_callback(const FindReqRespCallback& callback);
331 
332  /**
333  * @brief Walks the serialised-message dictionary and invokes @p callback for every entry.
334  *
335  * @param callback Visitor receiving each peer pointer and its stored handler.
336  */
337  void traverse_msg_callback(const FindMsgCallback& callback);
338 
339  /**
340  * @brief Walks the intra-process dictionary and invokes @p callback for every entry.
341  *
342  * @param callback Visitor receiving each peer pointer and its stored handler.
343  */
345 
346  /**
347  * @brief Walks the transport-status dictionary and invokes @p callback for every entry.
348  *
349  * @param callback Visitor receiving each peer pointer and its stored handler.
350  */
351  void traverse_status_callback(const FindStatusCallback& callback);
352 
353  protected:
355 
356  ~AbstractObject() override;
357 
358  [[nodiscard]] bool has_called() const;
359 
360  void ignore_called();
361 
362  private:
363  struct TraverseGuard final {
364  AbstractObject& object;
365 
366  ~TraverseGuard() {
367  --object.traverse_depth_;
368 
369  if VLIKELY (object.traverse_depth_ == 0) {
370  object.apply_deferred_removals();
371  }
372  }
373  };
374 
375  template <typename CallbackMapT, typename CallbackT>
376  bool register_internal_callback(CallbackMapT& map, NodeImpl* impl, CallbackT&& callback);
377 
378  template <typename CallbackMapT>
379  [[nodiscard]] bool is_map_effectively_empty(const CallbackMapT& map) const;
380 
381  template <typename CallbackMapT, typename CallbackT>
382  void traverse_internal_callback(const CallbackMapT& map, const CallbackT& callback);
383 
384  [[nodiscard]] bool is_deferred_removed(NodeImpl* impl) const;
385 
386  void erase_impl_callbacks(NodeImpl* impl);
387 
388  void apply_deferred_removals();
389 
390  bool has_called_{false};
391  bool ignore_called_{false};
392  size_t traverse_depth_{0};
393  ImplList impl_list_;
394  std::vector<NodeImpl*> deferred_remove_list_;
395  mutable std::recursive_mutex mtx_;
396  ConnectCallbackMap server_connect_callback_map_;
397  ConnectCallbackMap sub_connect_callback_map_;
398  ReqRespCallbackMap req_resp_callback_map_;
399  MsgCallbackMap msg_callback_map_;
400  IntraMsgCallbackMap intra_msg_callback_map_;
401  StatusCallbackMap status_callback_map_;
402  NodeImpl* first_impl_{nullptr};
403 
405 };
406 
407 /**
408  * @class AbstractFactory
409  * @brief Lazily allocates and caches @c AbstractObject instances keyed by @c FilterT.
410  *
411  * @details
412  * Holds a @c std::map<FilterT, std::weak_ptr<Object>> so that multiple node
413  * peers requesting the same key reuse the same registration record. The
414  * returned @c std::shared_ptr carries a custom deleter that erases the map
415  * entry when the last owner releases the object, which keeps the lookup map
416  * free of stale @c weak_ptr slots.
417  *
418  * @note The factory itself is non-copyable and non-movable; share it through a
419  * singleton or a per-transport static instance.
420  *
421  * @tparam FilterT Topic-key type (typically @c std::string).
422  */
423 template <typename FilterT>
426  using Map = std::map<FilterT, std::weak_ptr<Object>>;
427  using Set = std::unordered_set<Object*>;
428 
429  public:
430  /**
431  * @brief Tests whether @p ptr corresponds to a live object created by this factory.
432  *
433  * @param ptr Raw pointer to validate.
434  * @return @c true when the object is still alive in this factory; @c false otherwise.
435  */
436  [[nodiscard]] bool has_object(Object* ptr) const;
437 
438  /**
439  * @brief Looks up or creates the @c ObjectT registered against @p filter.
440  *
441  * @details
442  * If the cached @c weak_ptr is still valid, the existing instance is shared
443  * with the caller. Otherwise a new @c ObjectT is allocated (outside the
444  * factory lock so its constructor can re-enter VLink safely), the resulting
445  * @c shared_ptr is given a deleter that removes the cache entry on
446  * destruction, and the value is stored back into the map.
447  *
448  * @tparam ObjectT Concrete subclass of @c AbstractObject<FilterT> to allocate.
449  *
450  * @param filter Key identifying the topic.
451  * @return Shared ownership handle for the cached object.
452  */
453  template <typename ObjectT>
454  [[nodiscard]] std::shared_ptr<ObjectT> get_object(const FilterT& filter);
455 
456  protected:
457  /**
458  * @brief Constructs an empty factory.
459  */
461 
462  /**
463  * @brief Destroys the factory and releases the cache.
464  */
465  virtual ~AbstractFactory();
466 
467  private:
468  Set set_;
469  Map map_;
470  mutable std::mutex mtx_;
471 
473 };
474 
475 ////////////////////////////////////////////////////////////////
476 /// Details
477 ////////////////////////////////////////////////////////////////
478 
479 template <typename FilterT>
481  std::lock_guard lock(mtx_);
482 
483  if VUNLIKELY (is_deferred_removed(impl)) {
484  return false;
485  }
486 
487  first_impl_ = impl;
488 
489  return impl_list_.emplace(impl).second;
490 }
491 
492 template <typename FilterT>
494  std::lock_guard lock(mtx_);
495 
496  if VUNLIKELY (traverse_depth_ != 0) {
497  if VUNLIKELY (impl_list_.find(impl) == impl_list_.end() || is_deferred_removed(impl)) {
498  return false;
499  }
500 
501  deferred_remove_list_.push_back(impl);
502 
503  if (first_impl_ == impl) {
504  first_impl_ = nullptr;
505  }
506 
507  return true;
508  }
509 
510  if VUNLIKELY (impl_list_.erase(impl) == 0) {
511  return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
512  }
513 
514  if (first_impl_ == impl) {
515  first_impl_ = impl_list_.empty() ? nullptr : *impl_list_.begin();
516  }
517 
518  erase_impl_callbacks(impl);
519 
520  return true;
521 }
522 
523 template <typename FilterT>
525  std::lock_guard lock(mtx_);
526  return first_impl_;
527 }
528 
529 template <typename FilterT>
531  std::lock_guard lock(mtx_);
532  return impl_list_.find(impl) != impl_list_.end() && !is_deferred_removed(impl);
533 }
534 
535 template <typename FilterT>
537  std::lock_guard lock(mtx_);
538 
539  if VLIKELY (deferred_remove_list_.empty()) {
540  return !impl_list_.empty();
541  }
542 
543  for (auto* impl : impl_list_) {
544  if (!is_deferred_removed(impl)) {
545  return true;
546  }
547  }
548 
549  return false;
550 }
551 
552 template <typename FilterT>
554  NodeImpl::ConnectCallback&& callback) {
555  return register_internal_callback(server_connect_callback_map_, impl, std::move(callback));
556 }
557 
558 template <typename FilterT>
560  NodeImpl::ConnectCallback&& callback) {
561  return register_internal_callback(sub_connect_callback_map_, impl, std::move(callback));
562 }
563 
564 template <typename FilterT>
566  return register_internal_callback(req_resp_callback_map_, impl, std::move(callback));
567 }
568 
569 template <typename FilterT>
571  return register_internal_callback(msg_callback_map_, impl, std::move(callback));
572 }
573 
574 template <typename FilterT>
576  NodeImpl::IntraMsgCallback&& callback) {
577  return register_internal_callback(intra_msg_callback_map_, impl, std::move(callback));
578 }
579 
580 template <typename FilterT>
582  return register_internal_callback(status_callback_map_, impl, std::move(callback));
583 }
584 
585 template <typename FilterT>
587  std::lock_guard lock(this->mtx_);
588  return is_map_effectively_empty(server_connect_callback_map_);
589 }
590 
591 template <typename FilterT>
593  std::lock_guard lock(this->mtx_);
594  return is_map_effectively_empty(sub_connect_callback_map_);
595 }
596 
597 template <typename FilterT>
599  std::lock_guard lock(this->mtx_);
600  return is_map_effectively_empty(req_resp_callback_map_);
601 }
602 
603 template <typename FilterT>
605  std::lock_guard lock(this->mtx_);
606  return is_map_effectively_empty(msg_callback_map_);
607 }
608 
609 template <typename FilterT>
611  std::lock_guard lock(this->mtx_);
612  return is_map_effectively_empty(intra_msg_callback_map_);
613 }
614 
615 template <typename FilterT>
617  std::lock_guard lock(this->mtx_);
618  return is_map_effectively_empty(status_callback_map_);
619 }
620 
621 template <typename FilterT>
623  this->traverse_internal_callback(server_connect_callback_map_, callback);
624 }
625 
626 template <typename FilterT>
628  this->traverse_internal_callback(sub_connect_callback_map_, callback);
629 }
630 
631 template <typename FilterT>
633  this->traverse_internal_callback(req_resp_callback_map_, callback);
634 }
635 
636 template <typename FilterT>
638  this->traverse_internal_callback(msg_callback_map_, callback);
639 }
640 
641 template <typename FilterT>
643  this->traverse_internal_callback(intra_msg_callback_map_, callback);
644 }
645 
646 template <typename FilterT>
648  this->traverse_internal_callback(status_callback_map_, callback);
649 }
650 
651 template <typename FilterT>
652 inline AbstractObject<FilterT>::AbstractObject() = default;
653 
654 template <typename FilterT>
656 
657 template <typename FilterT>
659  return has_called_;
660 }
661 
662 template <typename FilterT>
664  ignore_called_ = true;
665 }
666 
667 template <typename FilterT>
668 template <typename CallbackMapT, typename CallbackT>
669 inline bool AbstractObject<FilterT>::register_internal_callback(CallbackMapT& map, NodeImpl* impl,
670  CallbackT&& callback) {
671  std::lock_guard lock(mtx_);
672 
673  if VUNLIKELY (is_deferred_removed(impl)) {
674  return false;
675  }
676 
677  return map.try_emplace(impl, std::forward<CallbackT>(callback)).second;
678 }
679 
680 template <typename FilterT>
681 template <typename CallbackMapT>
682 inline bool AbstractObject<FilterT>::is_map_effectively_empty(const CallbackMapT& map) const {
683  if VLIKELY (deferred_remove_list_.empty()) {
684  return map.empty();
685  }
686 
687  for (const auto& item : map) {
688  if (!is_deferred_removed(item.first)) {
689  return false;
690  }
691  }
692 
693  return true;
694 }
695 
696 template <typename FilterT>
697 inline bool AbstractObject<FilterT>::is_deferred_removed(NodeImpl* impl) const {
698  for (auto* target : deferred_remove_list_) {
699  if (target == impl) {
700  return true;
701  }
702  }
703 
704  return false;
705 }
706 
707 template <typename FilterT>
708 inline void AbstractObject<FilterT>::erase_impl_callbacks(NodeImpl* impl) {
709  server_connect_callback_map_.erase(impl);
710  sub_connect_callback_map_.erase(impl);
711  req_resp_callback_map_.erase(impl);
712  msg_callback_map_.erase(impl);
713  intra_msg_callback_map_.erase(impl);
714  status_callback_map_.erase(impl);
715 }
716 
717 template <typename FilterT>
718 inline void AbstractObject<FilterT>::apply_deferred_removals() {
719  if VLIKELY (deferred_remove_list_.empty()) {
720  return;
721  }
722 
723  for (auto* impl : deferred_remove_list_) {
724  impl_list_.erase(impl);
725  erase_impl_callbacks(impl);
726  }
727 
728  deferred_remove_list_.clear();
729 
730  if (first_impl_ == nullptr && !impl_list_.empty()) {
731  first_impl_ = *impl_list_.begin();
732  }
733 }
734 
735 template <typename FilterT>
736 template <typename CallbackMapT, typename CallbackT>
737 inline void AbstractObject<FilterT>::traverse_internal_callback(const CallbackMapT& map, const CallbackT& callback) {
738  std::lock_guard lock(mtx_);
739 
740  this->ignore_called_ = false;
741  this->has_called_ = false;
742 
743  ++traverse_depth_;
744 
745  TraverseGuard guard{*this};
746 
747  for (const auto& [impl, target_callback] : map) {
748  if VUNLIKELY (is_deferred_removed(impl)) {
749  continue;
750  }
751 
752  callback(impl, target_callback);
753 
754  if VUNLIKELY (this->ignore_called_) {
755  this->ignore_called_ = false;
756  } else {
757  this->has_called_ = true;
758  }
759  }
760 }
761 
762 template <typename FilterT>
764  std::lock_guard lock(mtx_);
765  return set_.count(ptr) > 0;
766 }
767 
768 template <typename FilterT>
769 template <typename ObjectT>
770 inline std::shared_ptr<ObjectT> AbstractFactory<FilterT>::get_object(const FilterT& filter) {
771  static_assert(std::is_base_of_v<Object, ObjectT>, "ObjectT must be derived from AbstractObject");
772  std::shared_ptr<ObjectT> obj;
773  {
774  std::unique_lock lock(mtx_);
775 
776  const auto& deleter = [this, filter](ObjectT* obj) {
777  {
778  std::lock_guard lock(mtx_);
779 
780  set_.erase(obj);
781 
782  auto iter = map_.find(filter);
783 
784  if VUNLIKELY (iter != map_.end() && iter->second.expired()) {
785  map_.erase(iter);
786  }
787  }
788 
789  delete obj;
790  };
791 
792  auto iter = map_.find(filter);
793 
794  if (iter != map_.end()) {
795  obj = std::static_pointer_cast<ObjectT>(iter->second.lock());
796 
797  if VLIKELY (obj) {
798  return obj;
799  }
800 
801  map_.erase(iter); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
802  }
803 
804  {
805  lock.unlock();
806  auto* obj_ptr = new ObjectT(filter);
807  lock.lock();
808 
809  auto [it, inserted] = map_.try_emplace(filter, std::weak_ptr<Object>());
810 
811  if (!inserted) {
812  obj = std::static_pointer_cast<ObjectT>(it->second.lock()); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
813  }
814 
815  if (inserted || !obj) {
816  if (!inserted) {
817  map_.erase(it); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
818  it = map_.try_emplace(filter, std::weak_ptr<Object>()).first; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
819  }
820 
821  obj = std::shared_ptr<ObjectT>(obj_ptr, deleter);
822  it->second = obj;
823  set_.emplace(obj_ptr);
824  } else {
825  delete obj_ptr; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
826  }
827  }
828  return obj;
829  }
830 } // LCOV_EXCL_LINE GCOVR_EXCL_LINE
831 
832 template <typename FilterT>
834 
835 template <typename FilterT>
837 
838 } // namespace vlink
#define VUNLIKELY(...)
Short alias for VLINK_UNLIKELY.
Definition: macros.h:289
#define VLIKELY(...)
Short alias for VLINK_LIKELY.
Definition: macros.h:284
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition: macros.h:174
Foundational base classes shared by every transport-backed VLink node.