107 #include <unordered_set>
111 #include "../base/functional.h"
112 #include "../base/logger.h"
135 template <
typename FilterT>
208 [[nodiscard]]
bool has_impl()
const;
363 struct TraverseGuard final {
367 --
object.traverse_depth_;
369 if VLIKELY (
object.traverse_depth_ == 0) {
370 object.apply_deferred_removals();
375 template <
typename CallbackMapT,
typename CallbackT>
376 bool register_internal_callback(CallbackMapT& map, NodeImpl* impl, CallbackT&& callback);
378 template <
typename CallbackMapT>
379 [[nodiscard]]
bool is_map_effectively_empty(
const CallbackMapT& map)
const;
381 template <
typename CallbackMapT,
typename CallbackT>
382 void traverse_internal_callback(
const CallbackMapT& map,
const CallbackT& callback);
384 [[nodiscard]]
bool is_deferred_removed(NodeImpl* impl)
const;
386 void erase_impl_callbacks(NodeImpl* impl);
388 void apply_deferred_removals();
390 bool has_called_{
false};
391 bool ignore_called_{
false};
392 size_t traverse_depth_{0};
394 std::vector<NodeImpl*> deferred_remove_list_;
395 mutable std::recursive_mutex mtx_;
402 NodeImpl* first_impl_{
nullptr};
423 template <
typename FilterT>
426 using Map = std::map<FilterT, std::weak_ptr<Object>>;
427 using Set = std::unordered_set<Object*>;
453 template <
typename ObjectT>
454 [[nodiscard]] std::shared_ptr<ObjectT>
get_object(
const FilterT& filter);
470 mutable std::mutex mtx_;
479 template <
typename FilterT>
481 std::lock_guard lock(mtx_);
483 if VUNLIKELY (is_deferred_removed(impl)) {
489 return impl_list_.emplace(impl).second;
492 template <
typename FilterT>
494 std::lock_guard lock(mtx_);
497 if VUNLIKELY (impl_list_.find(impl) == impl_list_.end() || is_deferred_removed(impl)) {
501 deferred_remove_list_.push_back(impl);
503 if (first_impl_ == impl) {
504 first_impl_ =
nullptr;
510 if VUNLIKELY (impl_list_.erase(impl) == 0) {
514 if (first_impl_ == impl) {
515 first_impl_ = impl_list_.empty() ? nullptr : *impl_list_.begin();
518 erase_impl_callbacks(impl);
523 template <
typename FilterT>
525 std::lock_guard lock(mtx_);
529 template <
typename FilterT>
531 std::lock_guard lock(mtx_);
532 return impl_list_.find(impl) != impl_list_.end() && !is_deferred_removed(impl);
535 template <
typename FilterT>
537 std::lock_guard lock(mtx_);
539 if VLIKELY (deferred_remove_list_.empty()) {
540 return !impl_list_.empty();
543 for (
auto* impl : impl_list_) {
544 if (!is_deferred_removed(impl)) {
552 template <
typename FilterT>
555 return register_internal_callback(server_connect_callback_map_, impl, std::move(callback));
558 template <
typename FilterT>
561 return register_internal_callback(sub_connect_callback_map_, impl, std::move(callback));
564 template <
typename FilterT>
566 return register_internal_callback(req_resp_callback_map_, impl, std::move(callback));
569 template <
typename FilterT>
571 return register_internal_callback(msg_callback_map_, impl, std::move(callback));
574 template <
typename FilterT>
577 return register_internal_callback(intra_msg_callback_map_, impl, std::move(callback));
580 template <
typename FilterT>
582 return register_internal_callback(status_callback_map_, impl, std::move(callback));
585 template <
typename FilterT>
587 std::lock_guard lock(this->mtx_);
588 return is_map_effectively_empty(server_connect_callback_map_);
591 template <
typename FilterT>
593 std::lock_guard lock(this->mtx_);
594 return is_map_effectively_empty(sub_connect_callback_map_);
597 template <
typename FilterT>
599 std::lock_guard lock(this->mtx_);
600 return is_map_effectively_empty(req_resp_callback_map_);
603 template <
typename FilterT>
605 std::lock_guard lock(this->mtx_);
606 return is_map_effectively_empty(msg_callback_map_);
609 template <
typename FilterT>
611 std::lock_guard lock(this->mtx_);
612 return is_map_effectively_empty(intra_msg_callback_map_);
615 template <
typename FilterT>
617 std::lock_guard lock(this->mtx_);
618 return is_map_effectively_empty(status_callback_map_);
621 template <
typename FilterT>
623 this->traverse_internal_callback(server_connect_callback_map_, callback);
626 template <
typename FilterT>
628 this->traverse_internal_callback(sub_connect_callback_map_, callback);
631 template <
typename FilterT>
633 this->traverse_internal_callback(req_resp_callback_map_, callback);
636 template <
typename FilterT>
638 this->traverse_internal_callback(msg_callback_map_, callback);
641 template <
typename FilterT>
643 this->traverse_internal_callback(intra_msg_callback_map_, callback);
646 template <
typename FilterT>
648 this->traverse_internal_callback(status_callback_map_, callback);
651 template <
typename FilterT>
654 template <
typename FilterT>
657 template <
typename FilterT>
662 template <
typename FilterT>
664 ignore_called_ =
true;
667 template <
typename FilterT>
668 template <
typename CallbackMapT,
typename CallbackT>
670 CallbackT&& callback) {
671 std::lock_guard lock(mtx_);
673 if VUNLIKELY (is_deferred_removed(impl)) {
677 return map.try_emplace(impl, std::forward<CallbackT>(callback)).second;
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()) {
687 for (
const auto& item : map) {
688 if (!is_deferred_removed(item.first)) {
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) {
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);
717 template <
typename FilterT>
718 inline void AbstractObject<FilterT>::apply_deferred_removals() {
719 if VLIKELY (deferred_remove_list_.empty()) {
723 for (
auto* impl : deferred_remove_list_) {
724 impl_list_.erase(impl);
725 erase_impl_callbacks(impl);
728 deferred_remove_list_.clear();
730 if (first_impl_ ==
nullptr && !impl_list_.empty()) {
731 first_impl_ = *impl_list_.begin();
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_);
740 this->ignore_called_ =
false;
741 this->has_called_ =
false;
745 TraverseGuard guard{*
this};
747 for (
const auto& [impl, target_callback] : map) {
748 if VUNLIKELY (is_deferred_removed(impl)) {
752 callback(impl, target_callback);
755 this->ignore_called_ =
false;
757 this->has_called_ =
true;
762 template <
typename FilterT>
764 std::lock_guard lock(mtx_);
765 return set_.count(ptr) > 0;
768 template <
typename FilterT>
769 template <
typename ObjectT>
771 static_assert(std::is_base_of_v<Object, ObjectT>,
"ObjectT must be derived from AbstractObject");
772 std::shared_ptr<ObjectT> obj;
774 std::unique_lock lock(mtx_);
776 const auto& deleter = [
this, filter](ObjectT* obj) {
778 std::lock_guard lock(mtx_);
782 auto iter = map_.find(filter);
784 if VUNLIKELY (iter != map_.end() && iter->second.expired()) {
792 auto iter = map_.find(filter);
794 if (iter != map_.end()) {
795 obj = std::static_pointer_cast<ObjectT>(iter->second.lock());
806 auto* obj_ptr =
new ObjectT(filter);
809 auto [it, inserted] = map_.try_emplace(filter, std::weak_ptr<Object>());
812 obj = std::static_pointer_cast<ObjectT>(it->second.lock());
815 if (inserted || !obj) {
818 it = map_.try_emplace(filter, std::weak_ptr<Object>()).first;
821 obj = std::shared_ptr<ObjectT>(obj_ptr, deleter);
823 set_.emplace(obj_ptr);
832 template <
typename FilterT>
835 template <
typename FilterT>
Lazily allocates and caches AbstractObject instances keyed by FilterT.
Definition: abstract_factory.h:424
virtual ~AbstractFactory()
Destroys the factory and releases the cache.
bool has_object(Object *ptr) const
Tests whether ptr corresponds to a live object created by this factory.
Definition: abstract_factory.h:763
AbstractFactory()
Constructs an empty factory.
std::shared_ptr< ObjectT > get_object(const FilterT &filter)
Looks up or creates the ObjectT registered against filter.
Definition: abstract_factory.h:770
Tiny mix-in that exposes the backend-native handle behind a node.
Definition: node_impl.h:123
Topic-scoped fan-out store of NodeImpl peers and their callbacks.
Definition: abstract_factory.h:136
bool add_impl(NodeImpl *impl)
Registers impl as an active peer on this topic.
Definition: abstract_factory.h:480
bool req_resp_map_is_empty() const
Reports whether the request/response dictionary is empty.
Definition: abstract_factory.h:598
NodeImpl * get_first_impl() const
Returns the most recently registered peer.
Definition: abstract_factory.h:524
bool register_msg_callback(NodeImpl *impl, NodeImpl::MsgCallback &&callback)
Stores callback as the serialised-message handler for impl.
Definition: abstract_factory.h:570
bool server_connect_map_is_empty() const
Reports whether the server-connect dictionary is empty.
Definition: abstract_factory.h:586
void traverse_server_connect_callback(const FindConnectCallback &callback)
Walks the server-connect dictionary and invokes callback for every entry.
Definition: abstract_factory.h:622
bool register_req_resp_callback(NodeImpl *impl, NodeImpl::ReqRespCallback &&callback)
Stores callback as the request/response handler for impl.
Definition: abstract_factory.h:565
std::map< NodeImpl *, NodeImpl::MsgCallback > MsgCallbackMap
Message callbacks, keyed by impl.
Definition: abstract_factory.h:142
bool has_called() const
Definition: abstract_factory.h:658
bool sub_connect_map_is_empty() const
Reports whether the subscriber-connect dictionary is empty.
Definition: abstract_factory.h:592
bool remove_impl(NodeImpl *impl)
Removes impl from the peer set and forgets every associated callback.
Definition: abstract_factory.h:493
std::map< NodeImpl *, NodeImpl::ConnectCallback > ConnectCallbackMap
Connect handlers per impl.
Definition: abstract_factory.h:140
bool register_server_connect_callback(NodeImpl *impl, NodeImpl::ConnectCallback &&callback)
Stores callback as the server-side connect handler for impl.
Definition: abstract_factory.h:553
std::map< NodeImpl *, NodeImpl::IntraMsgCallback > IntraMsgCallbackMap
Intra-message callbacks.
Definition: abstract_factory.h:143
std::unordered_set< NodeImpl * > ImplList
Set of currently registered NodeImpl peers.
Definition: abstract_factory.h:138
bool status_map_is_empty() const
Reports whether the transport-status dictionary is empty.
Definition: abstract_factory.h:616
bool register_sub_connect_callback(NodeImpl *impl, NodeImpl::ConnectCallback &&callback)
Stores callback as the subscriber-side connect handler for impl.
Definition: abstract_factory.h:559
bool register_intra_msg_callback(NodeImpl *impl, NodeImpl::IntraMsgCallback &&callback)
Stores callback as the intra-process message handler for impl.
Definition: abstract_factory.h:575
bool msg_map_is_empty() const
Reports whether the serialised-message dictionary is empty.
Definition: abstract_factory.h:604
void ignore_called()
Definition: abstract_factory.h:663
void traverse_intra_msg_callback(const FindIntraMsgCallback &callback)
Walks the intra-process dictionary and invokes callback for every entry.
Definition: abstract_factory.h:642
void traverse_status_callback(const FindStatusCallback &callback)
Walks the transport-status dictionary and invokes callback for every entry.
Definition: abstract_factory.h:647
std::map< NodeImpl *, NodeImpl::StatusCallback > StatusCallbackMap
Status callbacks per impl.
Definition: abstract_factory.h:144
void traverse_sub_connect_callback(const FindConnectCallback &callback)
Walks the subscriber-connect dictionary and invokes callback for every entry.
Definition: abstract_factory.h:627
bool has_impl() const
Indicates whether at least one peer has been registered.
Definition: abstract_factory.h:536
std::map< NodeImpl *, NodeImpl::ReqRespCallback > ReqRespCallbackMap
Req/resp callbacks, keyed by impl.
Definition: abstract_factory.h:141
void traverse_req_resp_callback(const FindReqRespCallback &callback)
Walks the request/response dictionary and invokes callback for every entry.
Definition: abstract_factory.h:632
bool is_contains_impl(NodeImpl *impl) const
Tests whether impl is currently part of the peer set.
Definition: abstract_factory.h:530
bool intra_msg_map_is_empty() const
Reports whether the intra-process message dictionary is empty.
Definition: abstract_factory.h:610
~AbstractObject() override
void traverse_msg_callback(const FindMsgCallback &callback)
Walks the serialised-message dictionary and invokes callback for every entry.
Definition: abstract_factory.h:637
bool register_status_callback(NodeImpl *impl, NodeImpl::StatusCallback &&callback)
Stores callback as the transport-status handler for impl.
Definition: abstract_factory.h:581
Copyable type-erased callable analogue of std::function with a tunable SBO and pool spill.
Definition: functional.h:132
Backbone of every transport-specific VLink node implementation.
Definition: node_impl.h:163
#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.