@@ -97,7 +97,10 @@ struct IsFixedPortIdMessageTrait<T, true>
9797// / Instance of this class is supposed to be created once per transport instance (or even per application).
9898// / Main purpose of the presentation object is to create publishers, subscribers, and RPC clients and servers.
9999// /
100- class Presentation final : private detail::IPresentationDelegate
100+ // / No Sonar cpp:S4963 'The "Rule-of-Zero" should be followed'
101+ // / b/c we do directly handle resources here.
102+ // /
103+ class Presentation final : private detail::IPresentationDelegate // NOSONAR cpp:S4963
101104{
102105public:
103106 // / @brief Defines failure type of various `make...` methods of the presentation layer.
@@ -112,7 +115,30 @@ class Presentation final : private detail::IPresentationDelegate
112115 : memory_{memory}
113116 , executor_{executor}
114117 , transport_{transport}
118+ , unreferenced_nodes_{&unreferenced_nodes_, &unreferenced_nodes_}
119+ {
120+ unref_nodes_deleter_callback_ = executor_.registerCallback ([this ](const auto &) {
121+ //
122+ destroyUnreferencedNodes ();
123+ });
124+ CETL_DEBUG_ASSERT (unref_nodes_deleter_callback_, " Should not fail b/c we pass proper lambda." );
125+ }
126+
127+ Presentation (const Presentation&) = delete ;
128+ Presentation (Presentation&&) noexcept = delete ;
129+ Presentation& operator =(const Presentation&) = delete ;
130+ Presentation& operator =(Presentation&&) noexcept = delete ;
131+
132+ ~Presentation ()
115133 {
134+ destroyUnreferencedNodes ();
135+
136+ CETL_DEBUG_ASSERT (shared_client_nodes_.empty (), //
137+ " RPC clients must be destroyed before presentation." );
138+ CETL_DEBUG_ASSERT (publisher_impl_nodes_.empty (), //
139+ " Message publishers must be destroyed before presentation." );
140+ CETL_DEBUG_ASSERT (subscriber_impl_nodes_.empty (), //
141+ " Message subscribers must be destroyed before presentation." );
116142 }
117143
118144 // / @brief Gets reference to the executor instance of this presentation object.
@@ -161,6 +187,11 @@ class Presentation final : private detail::IPresentationDelegate
161187 auto * const publisher_impl = std::get<0 >(publisher_existing);
162188 CETL_DEBUG_ASSERT (publisher_impl != nullptr , " " );
163189
190+ // This publisher impl node might be in the list of previously unreferenced nodes -
191+ // the ones that are going to be deleted asynchronously (by the `destroyUnreferencedNodes`).
192+ // If it's the case, we need to remove it from the list b/c it's going to be referenced.
193+ publisher_impl->unlinkIfReferenced ();
194+
164195 return Publisher<Message>{publisher_impl};
165196 }
166197
@@ -456,6 +487,8 @@ class Presentation final : private detail::IPresentationDelegate
456487 }
457488
458489private:
490+ using Schedule = IExecutor::Callback::Schedule;
491+
459492 IPresentationDelegate& asDelegate () noexcept
460493 {
461494 return static_cast <IPresentationDelegate&>(*this );
@@ -517,6 +550,12 @@ class Presentation final : private detail::IPresentationDelegate
517550
518551 auto * const subscriber_impl = std::get<0 >(subscriber_existing);
519552 CETL_DEBUG_ASSERT (subscriber_impl != nullptr , " " );
553+
554+ // This subscriber impl node might be in the list of previously unreferenced nodes -
555+ // the ones that are going to be deleted asynchronously (by the `destroyUnreferencedNodes`).
556+ // If it's the case, we need to remove it from the list b/c it's going to be referenced.
557+ subscriber_impl->unlinkIfReferenced ();
558+
520559 return subscriber_impl;
521560 }
522561
@@ -571,6 +610,12 @@ class Presentation final : private detail::IPresentationDelegate
571610
572611 auto * const shared_client = std::get<0 >(shared_client_existing);
573612 CETL_DEBUG_ASSERT (shared_client != nullptr , " " );
613+
614+ // This client impl node might be in the list of previously unreferenced nodes -
615+ // the ones that are going to be deleted asynchronously (by the `destroyUnreferencedNodes`).
616+ // If it's the case, we need to remove it from the list b/c it's going to be referenced.
617+ shared_client->unlinkIfReferenced ();
618+
574619 return shared_client;
575620 }
576621
@@ -628,30 +673,57 @@ class Presentation final : private detail::IPresentationDelegate
628673 }
629674
630675 template <typename SharedNode>
631- void releaseSharedNode (SharedNode* const shared_node, cavl::Tree<SharedNode>& tree) const noexcept
676+ static void forgetSharedNode (SharedNode& shared_node) noexcept
632677 {
633- CETL_DEBUG_ASSERT (shared_node != nullptr , " " );
678+ CETL_DEBUG_ASSERT (shared_node.isLinked (), " " );
679+ CETL_DEBUG_ASSERT (!shared_node.isReferenced (), " " );
634680
635- // TODO: make it async (deferred to "on idle" callback).
636- tree.remove (shared_node);
637- shared_node->destroy ();
681+ // Remove the node from its tree (if it still there),
682+ // as well as from the list of unreferenced nodes (b/c we gonna finally destroy it).
683+ shared_node.remove (); // from the tree
684+ shared_node.unlinkIfReferenced (); // from the list
685+ }
686+
687+ void destroyUnreferencedNodes () const noexcept
688+ {
689+ // In the loop, destruction of a shared object also removes it from the list of unreferenced nodes.
690+ // So, it implicitly updates the `unreferenced_nodes_` list.
691+ while (unreferenced_nodes_.next_node != &unreferenced_nodes_)
692+ {
693+ auto * const shared_obj = static_cast <detail::SharedObject*>(unreferenced_nodes_.next_node );
694+ shared_obj->destroy ();
695+ }
638696 }
639697
640698 // MARK: IPresentationDelegate
641699
642- void releaseSharedClient (detail::SharedClient* const shared_client) noexcept override
700+ void markSharedObjAsUnreferenced (detail::SharedObject& shared_obj) noexcept override
701+ {
702+ // We are not going to destroy the shared object immediately, but schedule it for deletion.
703+ // This is b/c destruction of shared objects may be time-consuming (f.e. closing under the hood sockets).
704+ // Double-linked list is used to avoid the need to traverse the tree of shared objects.
705+ //
706+ CETL_DEBUG_ASSERT (!shared_obj.isReferenced (), " " );
707+ shared_obj.linkAsUnreferenced (unreferenced_nodes_);
708+ //
709+ const auto result = unref_nodes_deleter_callback_.schedule (Schedule::Once{executor_.now ()});
710+ CETL_DEBUG_ASSERT (result, " Should not fail b/c we never reset `unref_nodes_deleter_callback_`." );
711+ (void ) result;
712+ }
713+
714+ void forgetSharedClient (detail::SharedClient& shared_client) noexcept override
643715 {
644- releaseSharedNode (shared_client, shared_client_nodes_ );
716+ forgetSharedNode (shared_client);
645717 }
646718
647- void releasePublisherImpl (detail::PublisherImpl* const publisher_impl) noexcept override
719+ void forgetPublisherImpl (detail::PublisherImpl& publisher_impl) noexcept override
648720 {
649- releaseSharedNode (publisher_impl, publisher_impl_nodes_ );
721+ forgetSharedNode (publisher_impl);
650722 }
651723
652- void releaseSubscriberImpl (detail::SubscriberImpl* const subscriber_impl) noexcept override
724+ void forgetSubscriberImpl (detail::SubscriberImpl& subscriber_impl) noexcept override
653725 {
654- releaseSharedNode (subscriber_impl, subscriber_impl_nodes_ );
726+ forgetSharedNode (subscriber_impl);
655727 }
656728
657729 // MARK: Data members:
@@ -662,6 +734,8 @@ class Presentation final : private detail::IPresentationDelegate
662734 cavl::Tree<detail::SharedClient> shared_client_nodes_;
663735 cavl::Tree<detail::PublisherImpl> publisher_impl_nodes_;
664736 cavl::Tree<detail::SubscriberImpl> subscriber_impl_nodes_;
737+ detail::UnRefNode unreferenced_nodes_;
738+ IExecutor::Callback::Any unref_nodes_deleter_callback_;
665739
666740}; // Presentation
667741
0 commit comments