+2021-04-27  Michael Weghorn  <m.weghorn@posteo.de>
+           Simon Marchi  <simon.marchi@polymtl.ca>
+
+       * unittests/observable-selftests.c (dependency_test_counters):
+       New.
+       (observer_token0, observer_token1, observer_token2,
+       observer_token3, observer_token4, observer_token5): New.
+       (struct dependency_observer_data): New struct.
+       (observer_dependency_test_callback): New function.
+       (test_observers): New.
+       (run_dependency_test): New function.
+       (test_dependency): New.
+       (_initialize_observer_selftest): Register dependency test.
+
 2021-04-26  Simon Marchi  <simon.marchi@polymtl.ca>
 
        PR gdb/27773
 
 static int test_second_observer = 0;
 static int test_third_observer = 0;
 
+/* Counters for observers used for dependency tests.  */
+static std::vector<int> dependency_test_counters;
+
+/* Tokens for observers used for dependency tests.  */
+static gdb::observers::token observer_token0;
+static gdb::observers::token observer_token1;
+static gdb::observers::token observer_token2;
+static gdb::observers::token observer_token3;
+static gdb::observers::token observer_token4;
+static gdb::observers::token observer_token5;
+
+/* Data for one observer used for checking that dependencies work as expected;
+   dependencies are specified using their indices into the 'test_observers'
+   vector here for simplicity and mapped to corresponding tokens later.  */
+struct dependency_observer_data
+{
+  gdb::observers::token *token;
+
+  /* Name of the observer to use on attach.  */
+  const char *name;
+
+  /* Indices of observers that this one directly depends on.  */
+  std::vector<int> direct_dependencies;
+
+  /* Indices for all dependencies, including transitive ones.  */
+  std::vector<int> all_dependencies;
+
+  /* Function to attach to the observable for this observer.  */
+  std::function<void (int)> callback;
+};
+
+static void observer_dependency_test_callback (size_t index);
+
+/* Data for observers to use for dependency tests, using some sample
+   dependencies between the observers.  */
+static std::vector<dependency_observer_data> test_observers = {
+  {&observer_token0, "test0", {}, {},
+   [] (int) { observer_dependency_test_callback (0); }},
+  {&observer_token1, "test1", {0}, {0},
+   [] (int) { observer_dependency_test_callback (1); }},
+  {&observer_token2, "test2", {1}, {0, 1},
+   [] (int) { observer_dependency_test_callback (2); }},
+  {&observer_token3, "test3", {1}, {0, 1},
+   [] (int) { observer_dependency_test_callback (3); }},
+  {&observer_token4, "test4", {2, 3, 5}, {0, 1, 2, 3, 5},
+   [] (int) { observer_dependency_test_callback (4); }},
+  {&observer_token5, "test5", {0}, {0},
+   [] (int) { observer_dependency_test_callback (5); }},
+  {nullptr, "test6", {4}, {0, 1, 2, 3, 4, 5},
+   [] (int) { observer_dependency_test_callback (6); }},
+  {nullptr, "test7", {0}, {0},
+   [] (int) { observer_dependency_test_callback (7); }},
+};
+
 static void
 test_first_notification_function (int arg)
 {
   SELF_CHECK (three == test_third_observer);
 }
 
+/* Function for each observer to run when being notified during the dependency
+   tests.  Verify that the observer's dependencies have been notified before the
+   observer itself by checking their counters, then increase the observer's own
+   counter.  */
+static void
+observer_dependency_test_callback (size_t index)
+{
+  /* Check that dependencies have already been notified.  */
+  for (int i : test_observers[index].all_dependencies)
+    SELF_CHECK (dependency_test_counters[i] == 1);
+
+  /* Increase own counter.  */
+  dependency_test_counters[index]++;
+}
+
+/* Run a dependency test by attaching the observers in the specified order
+   then notifying them.  */
+static void
+run_dependency_test (std::vector<int> insertion_order)
+{
+  gdb::observers::observable<int> dependency_test_notification
+    ("dependency_test_notification");
+
+  /* Reset counters.  */
+  dependency_test_counters = std::vector<int> (test_observers.size (), 0);
+
+  /* Attach all observers in the given order, specifying dependencies.  */
+  for (int i : insertion_order)
+    {
+      const dependency_observer_data &o = test_observers[i];
+
+      /* Get tokens for dependencies using their indices.  */
+      std::vector<const gdb::observers::token *> dependency_tokens;
+      for (int index : o.all_dependencies)
+       dependency_tokens.emplace_back (test_observers[index].token);
+
+      if (o.token != nullptr)
+       dependency_test_notification.attach
+         (o.callback, *o.token, o.name, dependency_tokens);
+      else
+       dependency_test_notification.attach (o.callback, o.name,
+                                            dependency_tokens);
+    }
+
+  /* Notify observers, they check that their dependencies were notified.  */
+  dependency_test_notification.notify (1);
+}
+
+static void
+test_dependency ()
+{
+  /* Run dependency tests with different insertion orders.  */
+  run_dependency_test ({0, 1, 2, 3, 4, 5, 6, 7});
+  run_dependency_test ({7, 6, 5, 4, 3, 2, 1, 0});
+  run_dependency_test ({0, 3, 2, 1, 7, 6, 4, 5});
+}
+
 static void
 run_tests ()
 {
 {
   selftests::register_test ("gdb::observers",
                            selftests::observers::run_tests);
+  selftests::register_test ("gdb::observers dependency",
+                           selftests::observers::test_dependency);
 }
 
+2021-04-27  Michael Weghorn  <m.weghorn@posteo.de>
+           Simon Marchi  <simon.marchi@polymtl.ca>
+
+       * observable.h (class observable): Extend to allow specifying
+       dependencies between observers, keep vector holding observers
+       sorted so that dependencies are notified before observers
+       depending on them.
+
 2021-04-24  Simon Marchi  <simon.marchi@polymtl.ca>
 
        * observable.h (observer_debug_printf,
 
 private:
   struct observer
   {
-    observer (const struct token *token, func_type func, const char *name)
-      : token (token), func (func), name (name)
+    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:
 
   DISABLE_COPY_AND_ASSIGN (observable);
 
-  /* Attach F as an observer to this observable.  F cannot be
-     detached.
+  /* 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)
+  void attach (const func_type &f, 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 (nullptr, f, name);
+    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.
+  /* 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)
+  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);
+    attach (f, &t, name, dependencies);
   }
 
   /* Remove observers associated with T from this observable.  T is
 
   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 */