| /* Observers | 
 |  | 
 |    Copyright (C) 2016-2025 Free Software Foundation, Inc. | 
 |  | 
 |    This file is part of GDB. | 
 |  | 
 |    This program is free software; you can redistribute it and/or modify | 
 |    it under the terms of the GNU General Public License as published by | 
 |    the Free Software Foundation; either version 3 of the License, or | 
 |    (at your option) any later version. | 
 |  | 
 |    This program is distributed in the hope that it will be useful, | 
 |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |    GNU General Public License for more details. | 
 |  | 
 |    You should have received a copy of the GNU General Public License | 
 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 | #ifndef GDBSUPPORT_OBSERVABLE_H | 
 | #define GDBSUPPORT_OBSERVABLE_H | 
 |  | 
 | #include <algorithm> | 
 | #include <functional> | 
 | #include <vector> | 
 |  | 
 | /* Print an "observer" debug statement.  */ | 
 |  | 
 | #define observer_debug_printf(fmt, ...) \ | 
 |   debug_prefixed_printf_cond (observer_debug, "observer", fmt, ##__VA_ARGS__) | 
 |  | 
 | /* Print "observer" start/end debug statements.  */ | 
 |  | 
 | #define OBSERVER_SCOPED_DEBUG_START_END(fmt, ...) \ | 
 |   scoped_debug_start_end (observer_debug, "observer", fmt, ##__VA_ARGS__) | 
 |  | 
 | namespace gdb | 
 | { | 
 |  | 
 | namespace observers | 
 | { | 
 |  | 
 | extern bool observer_debug; | 
 |  | 
 | /* An observer is an entity which is interested in being notified | 
 |    when GDB reaches certain states, or certain events occur in GDB. | 
 |    The entity being observed is called the observable.  To receive | 
 |    notifications, the observer attaches a callback to the observable. | 
 |    One observable can have several observers. | 
 |  | 
 |    The observer implementation is also currently not reentrant.  In | 
 |    particular, it is therefore not possible to call the attach or | 
 |    detach routines during a notification.  */ | 
 |  | 
 | /* The type of a key that can be passed to attach, which can be passed | 
 |    to detach to remove associated observers.  Tokens have address | 
 |    identity, and are thus usually const globals.  */ | 
 | struct token | 
 | { | 
 |   token () = default; | 
 |  | 
 |   DISABLE_COPY_AND_ASSIGN (token); | 
 | }; | 
 |  | 
 | namespace detail | 
 | { | 
 |   /* Types that don't depend on any template parameter.  This saves a | 
 |      bit of code and debug info size, compared to putting them inside | 
 |      class observable.  */ | 
 |  | 
 |   /* Use for sorting algorithm, to indicate which observer we have | 
 |      visited.  */ | 
 |   enum class visit_state | 
 |   { | 
 |     NOT_VISITED, | 
 |     VISITING, | 
 |     VISITED, | 
 |   }; | 
 | } | 
 |  | 
 | template<typename... T> | 
 | class observable | 
 | { | 
 | public: | 
 |   typedef std::function<void (T...)> func_type; | 
 |  | 
 | private: | 
 |   struct observer | 
 |   { | 
 |     observer (const struct token *token, func_type func, const char *name, | 
 | 	      const std::vector<const struct token *> &dependencies) | 
 |       : token (token), func (func), name (name), dependencies (dependencies) | 
 |     {} | 
 |  | 
 |     const struct token *token; | 
 |     func_type func; | 
 |     const char *name; | 
 |     std::vector<const struct token *> dependencies; | 
 |   }; | 
 |  | 
 | public: | 
 |   explicit observable (const char *name) | 
 |     : m_name (name) | 
 |   { | 
 |   } | 
 |  | 
 |   DISABLE_COPY_AND_ASSIGN (observable); | 
 |  | 
 |   /* Attach F as an observer to this observable.  F cannot be detached or | 
 |      specified as a dependency. | 
 |  | 
 |      DEPENDENCIES is a list of tokens of observers to be notified before this | 
 |      one. | 
 |  | 
 |      NAME is the name of the observer, used for debug output purposes.  Its | 
 |      lifetime must be at least as long as the observer is attached.  */ | 
 |   void attach (const func_type &f, const char *name, | 
 | 	       const std::vector<const struct token *> &dependencies = {}) | 
 |   { | 
 |     attach (f, nullptr, name, dependencies); | 
 |   } | 
 |  | 
 |   /* Attach F as an observer to this observable. | 
 |  | 
 |      T is a reference to a token that can be used to later remove F or specify F | 
 |      as a dependency of another observer. | 
 |  | 
 |      DEPENDENCIES is a list of tokens of observers to be notified before this | 
 |      one. | 
 |  | 
 |      NAME is the name of the observer, used for debug output purposes.  Its | 
 |      lifetime must be at least as long as the observer is attached.  */ | 
 |   void attach (const func_type &f, const token &t, const char *name, | 
 | 	       const std::vector<const struct token *> &dependencies = {}) | 
 |   { | 
 |     attach (f, &t, name, dependencies); | 
 |   } | 
 |  | 
 |   /* Remove observers associated with T from this observable.  T is | 
 |      the token that was previously passed to any number of "attach" | 
 |      calls.  */ | 
 |   void detach (const token &t) | 
 |   { | 
 |     auto iter = std::remove_if (m_observers.begin (), | 
 | 				m_observers.end (), | 
 | 				[&] (const observer &o) | 
 | 				{ | 
 | 				  return o.token == &t; | 
 | 				}); | 
 |  | 
 |     observer_debug_printf ("Detaching observable %s from observer %s", | 
 | 			   iter->name, m_name); | 
 |  | 
 |     m_observers.erase (iter, m_observers.end ()); | 
 |   } | 
 |  | 
 |   /* Notify all observers that are attached to this observable.  */ | 
 |   void notify (T... args) const | 
 |   { | 
 |     OBSERVER_SCOPED_DEBUG_START_END ("observable %s notify() called", m_name); | 
 |  | 
 |     for (auto &&e : m_observers) | 
 |       { | 
 | 	OBSERVER_SCOPED_DEBUG_START_END ("calling observer %s of observable %s", | 
 | 					 e.name, m_name); | 
 | 	e.func (args...); | 
 |       } | 
 |   } | 
 |  | 
 | private: | 
 |  | 
 |   std::vector<observer> m_observers; | 
 |   const char *m_name; | 
 |  | 
 |   /* Helper method for topological sort using depth-first search algorithm. | 
 |  | 
 |      Visit all dependencies of observer at INDEX in M_OBSERVERS (later referred | 
 |      to as "the observer").  Then append the observer to SORTED_OBSERVERS. | 
 |  | 
 |      If the observer is already visited, do nothing.  */ | 
 |   void visit_for_sorting (std::vector<observer> &sorted_observers, | 
 | 			  std::vector<detail::visit_state> &visit_states, | 
 | 			  int index) | 
 |   { | 
 |     if (visit_states[index] == detail::visit_state::VISITED) | 
 |       return; | 
 |  | 
 |     /* If we are already visiting this observer, it means there's a cycle.  */ | 
 |     gdb_assert (visit_states[index] != detail::visit_state::VISITING); | 
 |  | 
 |     visit_states[index] = detail::visit_state::VISITING; | 
 |  | 
 |     /* For each dependency of this observer...  */ | 
 |     for (const token *dep : m_observers[index].dependencies) | 
 |       { | 
 | 	/* ... find the observer that has token DEP.  If found, visit it.  */ | 
 | 	auto it_dep | 
 | 	  = std::find_if (m_observers.begin (), m_observers.end (), | 
 | 			    [&] (observer o) { return o.token == dep; }); | 
 | 	if (it_dep != m_observers.end ()) | 
 | 	{ | 
 | 	  int i = std::distance (m_observers.begin (), it_dep); | 
 | 	  visit_for_sorting (sorted_observers, visit_states, i); | 
 | 	} | 
 |       } | 
 |  | 
 |     visit_states[index] = detail::visit_state::VISITED; | 
 |     sorted_observers.push_back (m_observers[index]); | 
 |   } | 
 |  | 
 |   /* Sort the observers, so that dependencies come before observers | 
 |      depending on them. | 
 |  | 
 |      Uses depth-first search algorithm for topological sorting, see | 
 |      https://en.wikipedia.org/wiki/Topological_sorting#Depth-first_search .  */ | 
 |   void sort_observers () | 
 |   { | 
 |     std::vector<observer> sorted_observers; | 
 |     std::vector<detail::visit_state> visit_states | 
 |       (m_observers.size (), detail::visit_state::NOT_VISITED); | 
 |  | 
 |     for (size_t i = 0; i < m_observers.size (); i++) | 
 |       visit_for_sorting (sorted_observers, visit_states, i); | 
 |  | 
 |     m_observers = std::move (sorted_observers); | 
 |   } | 
 |  | 
 |   void attach (const func_type &f, const token *t, const char *name, | 
 | 	       const std::vector<const struct token *> &dependencies) | 
 |   { | 
 |  | 
 |     observer_debug_printf ("Attaching observable %s to observer %s", | 
 | 			   name, m_name); | 
 |  | 
 |     m_observers.emplace_back (t, f, name, dependencies); | 
 |  | 
 |     /* The observer has been inserted at the end of the vector, so it will be | 
 |        after any of its potential dependencies attached earlier.  If the | 
 |        observer has a token, it means that other observers can specify it as | 
 |        a dependency, so sorting is necessary to ensure those will be after the | 
 |        newly inserted observer afterwards.  */ | 
 |     if (t != nullptr) | 
 |       sort_observers (); | 
 |   }; | 
 | }; | 
 |  | 
 | } /* namespace observers */ | 
 |  | 
 | } /* namespace gdb */ | 
 |  | 
 | #endif /* GDBSUPPORT_OBSERVABLE_H */ |