Blame SOURCES/0001-Engine-track-in-progress-watch-handles-to-avoid-spur.patch

4c06b9
From d148fffb58935d69a20dcc42b3ce3d998742f0e7 Mon Sep 17 00:00:00 2001
4c06b9
From: Daniel Playfair Cal <daniel.playfair.cal@gmail.com>
4c06b9
Date: Fri, 13 Jul 2018 14:47:45 +0000
4c06b9
Subject: [PATCH] Engine: track in progress watch handles to avoid spurious
4c06b9
 changed signals for the root path
4c06b9
4c06b9
---
4c06b9
 engine/dconf-engine.c | 88 +++++++++++++++++++++++++++++++++++++------
4c06b9
 engine/dconf-engine.h | 11 ++++++
4c06b9
 tests/engine.c        | 66 +++++++++++++++++++++++++++++++-
4c06b9
 3 files changed, 151 insertions(+), 14 deletions(-)
4c06b9
4c06b9
diff --git a/engine/dconf-engine.c b/engine/dconf-engine.c
4c06b9
index bc36e52..c0ff12d 100644
4c06b9
--- a/engine/dconf-engine.c
4c06b9
+++ b/engine/dconf-engine.c
4c06b9
@@ -158,17 +158,20 @@ struct _DConfEngine
4c06b9
   GDestroyNotify      free_func;
4c06b9
   gint                ref_count;
4c06b9
 
4c06b9
-  GMutex              sources_lock; /* This lock is for the sources (ie: refreshing) and state. */
4c06b9
-  guint64             state;        /* Counter that changes every time a source is refreshed. */
4c06b9
-  DConfEngineSource **sources;      /* Array never changes, but each source changes internally. */
4c06b9
+  GMutex              sources_lock;  /* This lock is for the sources (ie: refreshing) and state. */
4c06b9
+  guint64             state;         /* Counter that changes every time a source is refreshed. */
4c06b9
+  DConfEngineSource **sources;       /* Array never changes, but each source changes internally. */
4c06b9
   gint                n_sources;
4c06b9
 
4c06b9
-  GMutex              queue_lock;   /* This lock is for pending, in_flight, queue_cond */
4c06b9
-  GCond               queue_cond;   /* Signalled when the queues empty */
4c06b9
-  GQueue              pending;      /* DConfChangeset */
4c06b9
-  GQueue              in_flight;    /* DConfChangeset */
4c06b9
+  GMutex              queue_lock;    /* This lock is for pending, in_flight, queue_cond */
4c06b9
+  GCond               queue_cond;    /* Signalled when the queues empty */
4c06b9
+  GQueue              pending;       /* DConfChangeset */
4c06b9
+  GQueue              in_flight;     /* DConfChangeset */
4c06b9
 
4c06b9
-  gchar              *last_handled; /* reply tag from last item in in_flight */
4c06b9
+  gchar              *last_handled;  /* reply tag from last item in in_flight */
4c06b9
+
4c06b9
+  GHashTable         *watched_paths; /* list of paths currently being watched for changes */
4c06b9
+  GHashTable         *pending_paths; /* list of paths waiting to enter watched state */
4c06b9
 };
4c06b9
 
4c06b9
 /* When taking the sources lock we check if any of the databases have
4c06b9
@@ -244,6 +247,9 @@ dconf_engine_new (const gchar    *profile,
4c06b9
   dconf_engine_global_list = g_slist_prepend (dconf_engine_global_list, engine);
4c06b9
   g_mutex_unlock (&dconf_engine_global_lock);
4c06b9
 
4c06b9
+  engine->watched_paths = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
4c06b9
+  engine->pending_paths = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
4c06b9
+
4c06b9
   return engine;
4c06b9
 }
4c06b9
 
4c06b9
@@ -799,8 +805,9 @@ typedef struct
4c06b9
 {
4c06b9
   DConfEngineCallHandle handle;
4c06b9
 
4c06b9
-  guint64 state;
4c06b9
-  gint    pending;
4c06b9
+  guint64  state;
4c06b9
+  gint     pending;
4c06b9
+  gchar   *path;
4c06b9
 } OutstandingWatch;
4c06b9
 
4c06b9
 static void
4c06b9
@@ -825,11 +832,13 @@ dconf_engine_watch_established (DConfEngine  *engine,
4c06b9
        * must have changed while our watch requests were on the wire.
4c06b9
        *
4c06b9
        * We don't know what changed, so we can just say that potentially
4c06b9
-       * everything changed.  This case is very rare, anyway...
4c06b9
+       * everything under the path being watched changed.  This case is
4c06b9
+       * very rare, anyway...
4c06b9
        */
4c06b9
-      dconf_engine_change_notify (engine, "/", changes, NULL, FALSE, NULL, engine->user_data);
4c06b9
+      dconf_engine_change_notify (engine, ow->path, changes, NULL, FALSE, NULL, engine->user_data);
4c06b9
     }
4c06b9
 
4c06b9
+  dconf_engine_set_watching (engine, ow->path, TRUE, TRUE);
4c06b9
   dconf_engine_call_handle_free (handle);
4c06b9
 }
4c06b9
 
4c06b9
@@ -837,6 +846,15 @@ void
4c06b9
 dconf_engine_watch_fast (DConfEngine *engine,
4c06b9
                          const gchar *path)
4c06b9
 {
4c06b9
+  if (dconf_engine_is_watching (engine, path, TRUE))
4c06b9
+    {
4c06b9
+      /**
4c06b9
+       * Either there is already a match rule in place for this exact path,
4c06b9
+       * or there is already a request in progress to add a match.
4c06b9
+       */
4c06b9
+      return;
4c06b9
+    }
4c06b9
+
4c06b9
   OutstandingWatch *ow;
4c06b9
   gint i;
4c06b9
 
4c06b9
@@ -855,6 +873,7 @@ dconf_engine_watch_fast (DConfEngine *engine,
4c06b9
   ow = dconf_engine_call_handle_new (engine, dconf_engine_watch_established,
4c06b9
                                      G_VARIANT_TYPE_UNIT, sizeof (OutstandingWatch));
4c06b9
   ow->state = dconf_engine_get_state (engine);
4c06b9
+  ow->path = g_strdup (path);
4c06b9
 
4c06b9
   /* We start getting async calls returned as soon as we start dispatching them,
4c06b9
    * so we must not touch the 'ow' struct after we send the first one.
4c06b9
@@ -869,6 +888,8 @@ dconf_engine_watch_fast (DConfEngine *engine,
4c06b9
                                          "/org/freedesktop/DBus", "org.freedesktop.DBus", "AddMatch",
4c06b9
                                          dconf_engine_make_match_rule (engine->sources[i], path),
4c06b9
                                          &ow->handle, NULL);
4c06b9
+
4c06b9
+  dconf_engine_set_watching (engine, ow->path, TRUE, FALSE);
4c06b9
 }
4c06b9
 
4c06b9
 void
4c06b9
@@ -882,6 +903,8 @@ dconf_engine_unwatch_fast (DConfEngine *engine,
4c06b9
       dconf_engine_dbus_call_async_func (engine->sources[i]->bus_type, "org.freedesktop.DBus",
4c06b9
                                          "/org/freedesktop/DBus", "org.freedesktop.DBus", "RemoveMatch",
4c06b9
                                          dconf_engine_make_match_rule (engine->sources[i], path), NULL, NULL);
4c06b9
+
4c06b9
+  dconf_engine_set_watching (engine, path, FALSE, FALSE);
4c06b9
 }
4c06b9
 
4c06b9
 static void
4c06b9
@@ -920,6 +943,7 @@ dconf_engine_watch_sync (DConfEngine *engine,
4c06b9
                          const gchar *path)
4c06b9
 {
4c06b9
   dconf_engine_handle_match_rule_sync (engine, "AddMatch", path);
4c06b9
+  dconf_engine_set_watching (engine, path, TRUE, TRUE);
4c06b9
 }
4c06b9
 
4c06b9
 void
4c06b9
@@ -927,6 +951,7 @@ dconf_engine_unwatch_sync (DConfEngine *engine,
4c06b9
                            const gchar *path)
4c06b9
 {
4c06b9
   dconf_engine_handle_match_rule_sync (engine, "RemoveMatch", path);
4c06b9
+  dconf_engine_set_watching (engine, path, FALSE, FALSE);
4c06b9
 }
4c06b9
 
4c06b9
 typedef struct
4c06b9
@@ -1384,3 +1409,42 @@ dconf_engine_sync (DConfEngine *engine)
4c06b9
     g_cond_wait (&engine->queue_cond, &engine->queue_lock);
4c06b9
   dconf_engine_unlock_queues (engine);
4c06b9
 }
4c06b9
+
4c06b9
+void
4c06b9
+dconf_engine_set_watching (DConfEngine    *engine,
4c06b9
+                           const gchar    *path,
4c06b9
+                           const gboolean  is_watching,
4c06b9
+                           const gboolean  is_established)
4c06b9
+{
4c06b9
+  if (is_watching)
4c06b9
+    {
4c06b9
+      if (is_established)
4c06b9
+        {
4c06b9
+          g_hash_table_add (engine->watched_paths, g_strdup (path));
4c06b9
+          g_hash_table_remove (engine->pending_paths, path);
4c06b9
+        }
4c06b9
+      else
4c06b9
+        {
4c06b9
+          g_hash_table_add (engine->pending_paths, g_strdup (path));
4c06b9
+          g_hash_table_remove (engine->watched_paths, path);
4c06b9
+        }
4c06b9
+    }
4c06b9
+  else
4c06b9
+    {
4c06b9
+      g_hash_table_remove (engine->watched_paths, path);
4c06b9
+      g_hash_table_remove (engine->pending_paths, path);
4c06b9
+    }
4c06b9
+}
4c06b9
+
4c06b9
+gboolean
4c06b9
+dconf_engine_is_watching (DConfEngine *engine, const gchar *path, const gboolean only_established)
4c06b9
+{
4c06b9
+  gconstpointer key = (gconstpointer) path;
4c06b9
+  if (g_hash_table_contains (engine->watched_paths, key))
4c06b9
+    return TRUE;
4c06b9
+
4c06b9
+  if (!only_established && g_hash_table_contains (engine->pending_paths, key))
4c06b9
+    return TRUE;
4c06b9
+
4c06b9
+  return FALSE;
4c06b9
+}
4c06b9
diff --git a/engine/dconf-engine.h b/engine/dconf-engine.h
4c06b9
index 2485423..06ed5a7 100644
4c06b9
--- a/engine/dconf-engine.h
4c06b9
+++ b/engine/dconf-engine.h
4c06b9
@@ -104,6 +104,17 @@ DConfEngine *           dconf_engine_new                                (const g
4c06b9
 G_GNUC_INTERNAL
4c06b9
 void                    dconf_engine_unref                              (DConfEngine             *engine);
4c06b9
 
4c06b9
+G_GNUC_INTERNAL
4c06b9
+void                    dconf_engine_set_watching                       (DConfEngine             *engine,
4c06b9
+                                                                         const gchar             *path,
4c06b9
+                                                                         const gboolean           is_watching,
4c06b9
+                                                                         const gboolean           is_established);
4c06b9
+
4c06b9
+G_GNUC_INTERNAL
4c06b9
+gboolean                dconf_engine_is_watching                        (DConfEngine             *engine,
4c06b9
+                                                                         const gchar             *path,
4c06b9
+                                                                         const gboolean           only_established);
4c06b9
+
4c06b9
 /* Read API: always handled immediately */
4c06b9
 G_GNUC_INTERNAL
4c06b9
 guint64                 dconf_engine_get_state                          (DConfEngine             *engine);
4c06b9
diff --git a/tests/engine.c b/tests/engine.c
4c06b9
index a804b9a..aa1db1c 100644
4c06b9
--- a/tests/engine.c
4c06b9
+++ b/tests/engine.c
4c06b9
@@ -1153,7 +1153,7 @@ test_watch_fast (void)
4c06b9
   DConfEngine *engine;
4c06b9
   GvdbTable *table;
4c06b9
   GVariant *triv;
4c06b9
-  guint64 a, b;
4c06b9
+  guint64 a, b, c;
4c06b9
 
4c06b9
   change_log = g_string_new (NULL);
4c06b9
 
4c06b9
@@ -1202,7 +1202,20 @@ test_watch_fast (void)
4c06b9
   dconf_mock_dbus_assert_no_async ();
4c06b9
   b = dconf_engine_get_state (engine);
4c06b9
   g_assert_cmpuint (a, !=, b);
4c06b9
-  g_assert_cmpstr (change_log->str, ==, "/:1::nil;");
4c06b9
+  g_assert_cmpstr (change_log->str, ==, "/a/b/c:1::nil;");
4c06b9
+  /* Try to establish a watch again for the same path */
4c06b9
+  dconf_engine_watch_fast (engine, "/a/b/c");
4c06b9
+  g_assert (!dconf_engine_has_outstanding (engine));
4c06b9
+  dconf_engine_sync (engine);
4c06b9
+  c = dconf_engine_get_state (engine);
4c06b9
+  g_assert_cmpuint (b, ==, c);
4c06b9
+  /* The watch result was not sent, because the path was already watched */
4c06b9
+  dconf_mock_dbus_assert_no_async();
4c06b9
+  c = dconf_engine_get_state (engine);
4c06b9
+  g_assert_cmpuint (b, ==, c);
4c06b9
+  /* Since the path was already being watched,
4c06b9
+   * do not expect a second false change notification */
4c06b9
+  g_assert_cmpstr (change_log->str, ==, "/a/b/c:1::nil;");
4c06b9
   dconf_engine_unwatch_fast (engine, "/a/b/c");
4c06b9
   dconf_mock_dbus_async_reply (triv, NULL);
4c06b9
   dconf_mock_dbus_async_reply (triv, NULL);
4c06b9
@@ -1273,6 +1286,54 @@ test_watch_sync (void)
4c06b9
   match_request_type = NULL;
4c06b9
 }
4c06b9
 
4c06b9
+static void
4c06b9
+test_watching (void)
4c06b9
+{
4c06b9
+  DConfEngine *engine;
4c06b9
+  const gchar *apple = "apple";
4c06b9
+  const gchar *orange = "orange";
4c06b9
+  const gchar *banana = "banana";
4c06b9
+
4c06b9
+  engine = dconf_engine_new (SRCDIR "/profile/dos", NULL, NULL);
4c06b9
+
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, apple, TRUE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, apple, FALSE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, orange, TRUE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, orange, FALSE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, banana, TRUE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, banana, FALSE));
4c06b9
+
4c06b9
+  dconf_engine_set_watching (engine, apple, FALSE, FALSE);
4c06b9
+  dconf_engine_set_watching (engine, orange, TRUE, FALSE);
4c06b9
+  dconf_engine_set_watching (engine, banana, TRUE, TRUE);
4c06b9
+
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, apple, TRUE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, apple, FALSE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, orange, TRUE));
4c06b9
+  g_assert (dconf_engine_is_watching(engine, orange, FALSE));
4c06b9
+  g_assert (dconf_engine_is_watching(engine, banana, TRUE));
4c06b9
+  g_assert (dconf_engine_is_watching(engine, banana, FALSE));
4c06b9
+
4c06b9
+  dconf_engine_set_watching (engine, orange, TRUE, TRUE);
4c06b9
+  dconf_engine_set_watching (engine, banana, FALSE, FALSE);
4c06b9
+
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, apple, TRUE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, apple, FALSE));
4c06b9
+  g_assert (dconf_engine_is_watching(engine, orange, TRUE));
4c06b9
+  g_assert (dconf_engine_is_watching(engine, orange, FALSE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, banana, TRUE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, banana, FALSE));
4c06b9
+
4c06b9
+  dconf_engine_set_watching (engine, orange, FALSE, FALSE);
4c06b9
+
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, apple, TRUE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, apple, FALSE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, orange, TRUE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, orange, FALSE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, banana, TRUE));
4c06b9
+  g_assert (!dconf_engine_is_watching(engine, banana, FALSE));
4c06b9
+}
4c06b9
+
4c06b9
 static void
4c06b9
 test_change_fast (void)
4c06b9
 {
4c06b9
@@ -1758,6 +1819,7 @@ main (int argc, char **argv)
4c06b9
   g_test_add_func ("/engine/read", test_read);
4c06b9
   g_test_add_func ("/engine/watch/fast", test_watch_fast);
4c06b9
   g_test_add_func ("/engine/watch/sync", test_watch_sync);
4c06b9
+  g_test_add_func ("/engine/watch/watching", test_watching);
4c06b9
   g_test_add_func ("/engine/change/fast", test_change_fast);
4c06b9
   g_test_add_func ("/engine/change/sync", test_change_sync);
4c06b9
   g_test_add_func ("/engine/signals", test_signals);
4c06b9
-- 
4c06b9
2.20.1
4c06b9