123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- /* Observers
- Copyright (C) 2016-2022 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 COMMON_OBSERVABLE_H
- #define COMMON_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);
- };
- 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;
- /* Use for sorting algorithm, to indicate which observer we have visited. */
- enum class visit_state
- {
- NOT_VISITED,
- VISITING,
- VISITED,
- };
- /* 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<visit_state> &visit_states, int index)
- {
- if (visit_states[index] == visit_state::VISITED)
- return;
- /* If we are already visiting this observer, it means there's a cycle. */
- gdb_assert (visit_states[index] != visit_state::VISITING);
- visit_states[index] = 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] = 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<visit_state> visit_states (m_observers.size (),
- 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 /* COMMON_OBSERVABLE_H */
|