Blame SOURCES/0002-Engine-extend-subscriptions-state-to-account-for-mul.patch

4c06b9
From 0986a258cc1df8c1e2aa17a0c2138d178405f902 Mon Sep 17 00:00:00 2001
4c06b9
From: Daniel Playfair Cal <daniel.playfair.cal@gmail.com>
4c06b9
Date: Wed, 25 Jul 2018 00:52:24 +1000
4c06b9
Subject: [PATCH 2/5] Engine: extend subscriptions state to account for
4c06b9
 multiple client subscriptions to the same path
4c06b9
4c06b9
Remove accidental whitespace change
4c06b9
4c06b9
Simplify branching in watch_fast and unwatch_fast
4c06b9
4c06b9
Indentation fixes
4c06b9
4c06b9
Store the subscription counts directly in the hash table pointer instead of mallocing ints
4c06b9
4c06b9
Add documentation comments for new utility functions
4c06b9
---
4c06b9
 engine/dconf-engine.c | 191 ++++++++++++++++++++++++++++--------------
4c06b9
 engine/dconf-engine.h |  11 ---
4c06b9
 tests/engine.c        |  54 +-----------
4c06b9
 3 files changed, 133 insertions(+), 123 deletions(-)
4c06b9
4c06b9
diff --git a/engine/dconf-engine.c b/engine/dconf-engine.c
4c06b9
index 2a99eab..1963c34 100644
4c06b9
--- a/engine/dconf-engine.c
4c06b9
+++ b/engine/dconf-engine.c
4c06b9
@@ -170,8 +170,14 @@ struct _DConfEngine
4c06b9
 
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
+   * establishing and active, are hash tables storing the number
4c06b9
+   * of subscriptions to each path in the two possible states
4c06b9
+   */
4c06b9
+  /* active on the client side, but awaiting confirmation from the writer */
4c06b9
+  GHashTable         *establishing;
4c06b9
+  /* active on the client side, and with a D-Bus match rule established */
4c06b9
+  GHashTable         *active;
4c06b9
 };
4c06b9
 
4c06b9
 /* When taking the sources lock we check if any of the databases have
4c06b9
@@ -225,6 +231,78 @@ dconf_engine_unlock_queues (DConfEngine *engine)
4c06b9
   g_mutex_unlock (&engine->queue_lock);
4c06b9
 }
4c06b9
 
4c06b9
+/**
4c06b9
+ * Adds the count of subscriptions to @path in @from_table to the
4c06b9
+ * corresponding count in @to_table, creating it if it did not exist.
4c06b9
+ * Removes the count from @from_table.
4c06b9
+ */
4c06b9
+static void
4c06b9
+dconf_engine_move_subscriptions (GHashTable  *from_counts,
4c06b9
+                                 GHashTable  *to_counts,
4c06b9
+                                 const gchar *path)
4c06b9
+{
4c06b9
+  guint from_count = GPOINTER_TO_UINT (g_hash_table_lookup (from_counts, path));
4c06b9
+  guint old_to_count = GPOINTER_TO_UINT (g_hash_table_lookup (to_counts, path));
4c06b9
+  // Detect overflows
4c06b9
+  g_assert (old_to_count <= G_MAXUINT32 - from_count);
4c06b9
+  guint new_to_count = old_to_count + from_count;
4c06b9
+  if (from_count != 0)
4c06b9
+    {
4c06b9
+      g_hash_table_remove (from_counts, path);
4c06b9
+      g_hash_table_replace (to_counts,
4c06b9
+                            g_strdup (path),
4c06b9
+                            GUINT_TO_POINTER (new_to_count));
4c06b9
+    }
4c06b9
+}
4c06b9
+
4c06b9
+/**
4c06b9
+ * Increments the reference count for the subscription to @path, or sets
4c06b9
+ * it to 1 if it didn’t previously exist.
4c06b9
+ * Returns the new reference count.
4c06b9
+ */
4c06b9
+static guint
4c06b9
+dconf_engine_inc_subscriptions (GHashTable  *counts,
4c06b9
+                                const gchar *path)
4c06b9
+{
4c06b9
+  guint old_count = GPOINTER_TO_UINT (g_hash_table_lookup (counts, path));
4c06b9
+  // Detect overflows
4c06b9
+  g_assert (old_count < G_MAXUINT32);
4c06b9
+  guint new_count = old_count + 1;
4c06b9
+  g_hash_table_replace (counts, g_strdup (path), GUINT_TO_POINTER (new_count));
4c06b9
+  return new_count;
4c06b9
+}
4c06b9
+
4c06b9
+/**
4c06b9
+ * Decrements the reference count for the subscription to @path, or
4c06b9
+ * removes it if the new value is 0. The count must exist and be greater
4c06b9
+ * than 0.
4c06b9
+ * Returns the new reference count, or 0 if it does not exist.
4c06b9
+ */
4c06b9
+static guint
4c06b9
+dconf_engine_dec_subscriptions (GHashTable  *counts,
4c06b9
+                                const gchar *path)
4c06b9
+{
4c06b9
+  guint old_count = GPOINTER_TO_UINT (g_hash_table_lookup (counts, path));
4c06b9
+  g_assert (old_count > 0);
4c06b9
+  guint new_count = old_count - 1;
4c06b9
+  if (new_count == 0)
4c06b9
+    g_hash_table_remove (counts, path);
4c06b9
+  else
4c06b9
+    g_hash_table_replace (counts, g_strdup (path), GUINT_TO_POINTER (new_count));
4c06b9
+  return new_count;
4c06b9
+}
4c06b9
+
4c06b9
+/**
4c06b9
+ * Returns the reference count for the subscription to @path, or 0 if it
4c06b9
+ * does not exist.
4c06b9
+ */
4c06b9
+static guint
4c06b9
+dconf_engine_count_subscriptions (GHashTable  *counts,
4c06b9
+                                  const gchar *path)
4c06b9
+{
4c06b9
+  return GPOINTER_TO_UINT (g_hash_table_lookup (counts, path));
4c06b9
+}
4c06b9
+
4c06b9
 DConfEngine *
4c06b9
 dconf_engine_new (const gchar    *profile,
4c06b9
                   gpointer        user_data,
4c06b9
@@ -247,8 +325,14 @@ 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
+  engine->establishing = g_hash_table_new_full (g_str_hash,
4c06b9
+                                                g_str_equal,
4c06b9
+                                                g_free,
4c06b9
+                                                NULL);
4c06b9
+  engine->active = g_hash_table_new_full (g_str_hash,
4c06b9
+                                          g_str_equal,
4c06b9
+                                          g_free,
4c06b9
+                                          NULL);
4c06b9
 
4c06b9
   return engine;
4c06b9
 }
4c06b9
@@ -300,8 +384,8 @@ dconf_engine_unref (DConfEngine *engine)
4c06b9
 
4c06b9
       g_free (engine->sources);
4c06b9
 
4c06b9
-      g_hash_table_unref(engine->pending_paths);
4c06b9
-      g_hash_table_unref(engine->watched_paths);
4c06b9
+      g_hash_table_unref (engine->establishing);
4c06b9
+      g_hash_table_unref (engine->active);
4c06b9
 
4c06b9
       if (engine->free_func)
4c06b9
         engine->free_func (engine->user_data);
4c06b9
@@ -847,7 +931,14 @@ dconf_engine_watch_established (DConfEngine  *engine,
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
+  guint num_establishing = dconf_engine_count_subscriptions (engine->establishing,
4c06b9
+                                                             ow->path);
4c06b9
+  if (num_establishing > 0)
4c06b9
+    // Subscription(s): establishing -> active
4c06b9
+    dconf_engine_move_subscriptions (engine->establishing,
4c06b9
+                                     engine->active,
4c06b9
+                                     ow->path);
4c06b9
+
4c06b9
   dconf_engine_call_handle_free (handle);
4c06b9
 }
4c06b9
 
4c06b9
@@ -855,14 +946,17 @@ 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
+  guint num_establishing = dconf_engine_count_subscriptions (engine->establishing, path);
4c06b9
+  guint num_active = dconf_engine_count_subscriptions (engine->active, path);
4c06b9
+  if (num_active > 0)
4c06b9
+    // Subscription: inactive -> active
4c06b9
+    dconf_engine_inc_subscriptions (engine->active, path);
4c06b9
+  else
4c06b9
+    // Subscription: inactive -> establishing
4c06b9
+    num_establishing = dconf_engine_inc_subscriptions (engine->establishing,
4c06b9
+                                                       path);
4c06b9
+  if (num_establishing > 1 || num_active > 0)
4c06b9
+    return;
4c06b9
 
4c06b9
   OutstandingWatch *ow;
4c06b9
   gint i;
4c06b9
@@ -897,23 +991,33 @@ 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
 dconf_engine_unwatch_fast (DConfEngine *engine,
4c06b9
                            const gchar *path)
4c06b9
 {
4c06b9
+  guint num_active = dconf_engine_count_subscriptions (engine->active, path);
4c06b9
+  guint num_establishing = dconf_engine_count_subscriptions (engine->establishing, path);
4c06b9
   gint i;
4c06b9
 
4c06b9
+  // Client code cannot unsubscribe if it is not subscribed
4c06b9
+  g_assert (num_active > 0 || num_establishing > 0);
4c06b9
+  if (num_active == 0)
4c06b9
+    // Subscription: establishing -> inactive
4c06b9
+    num_establishing = dconf_engine_dec_subscriptions (engine->establishing, path);
4c06b9
+  else
4c06b9
+    // Subscription: active -> inactive
4c06b9
+    num_active = dconf_engine_dec_subscriptions (engine->active, path);
4c06b9
+
4c06b9
+  if (num_active > 0 || num_establishing > 0)
4c06b9
+    return;
4c06b9
+
4c06b9
   for (i = 0; i < engine->n_sources; i++)
4c06b9
     if (engine->sources[i]->bus_type)
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
@@ -951,16 +1055,18 @@ void
4c06b9
 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
+  guint num_active = dconf_engine_inc_subscriptions (engine->active, path);
4c06b9
+  if (num_active == 1)
4c06b9
+    dconf_engine_handle_match_rule_sync (engine, "AddMatch", path);
4c06b9
 }
4c06b9
 
4c06b9
 void
4c06b9
 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
+  guint num_active = dconf_engine_dec_subscriptions (engine->active, path);
4c06b9
+  if (num_active == 0)
4c06b9
+    dconf_engine_handle_match_rule_sync (engine, "RemoveMatch", path);
4c06b9
 }
4c06b9
 
4c06b9
 typedef struct
4c06b9
@@ -1418,42 +1524,3 @@ 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 06ed5a7..2485423 100644
4c06b9
--- a/engine/dconf-engine.h
4c06b9
+++ b/engine/dconf-engine.h
4c06b9
@@ -104,17 +104,6 @@ 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 aa1db1c..038c04c 100644
4c06b9
--- a/tests/engine.c
4c06b9
+++ b/tests/engine.c
4c06b9
@@ -1210,13 +1210,16 @@ test_watch_fast (void)
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
+  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
+  /* nothing was done, because there is still a subscription left */
4c06b9
+  dconf_mock_dbus_assert_no_async ();
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
   dconf_mock_dbus_assert_no_async ();
4c06b9
@@ -1286,54 +1289,6 @@ 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
@@ -1819,7 +1774,6 @@ 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