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

545ecd
From 0986a258cc1df8c1e2aa17a0c2138d178405f902 Mon Sep 17 00:00:00 2001
545ecd
From: Daniel Playfair Cal <daniel.playfair.cal@gmail.com>
545ecd
Date: Wed, 25 Jul 2018 00:52:24 +1000
545ecd
Subject: [PATCH 2/5] Engine: extend subscriptions state to account for
545ecd
 multiple client subscriptions to the same path
545ecd
545ecd
Remove accidental whitespace change
545ecd
545ecd
Simplify branching in watch_fast and unwatch_fast
545ecd
545ecd
Indentation fixes
545ecd
545ecd
Store the subscription counts directly in the hash table pointer instead of mallocing ints
545ecd
545ecd
Add documentation comments for new utility functions
545ecd
---
545ecd
 engine/dconf-engine.c | 191 ++++++++++++++++++++++++++++--------------
545ecd
 engine/dconf-engine.h |  11 ---
545ecd
 tests/engine.c        |  54 +-----------
545ecd
 3 files changed, 133 insertions(+), 123 deletions(-)
545ecd
545ecd
diff --git a/engine/dconf-engine.c b/engine/dconf-engine.c
545ecd
index 2a99eab..1963c34 100644
545ecd
--- a/engine/dconf-engine.c
545ecd
+++ b/engine/dconf-engine.c
545ecd
@@ -170,8 +170,14 @@ struct _DConfEngine
545ecd
 
545ecd
   gchar              *last_handled;  /* reply tag from last item in in_flight */
545ecd
 
545ecd
-  GHashTable         *watched_paths; /* list of paths currently being watched for changes */
545ecd
-  GHashTable         *pending_paths; /* list of paths waiting to enter watched state */
545ecd
+  /**
545ecd
+   * establishing and active, are hash tables storing the number
545ecd
+   * of subscriptions to each path in the two possible states
545ecd
+   */
545ecd
+  /* active on the client side, but awaiting confirmation from the writer */
545ecd
+  GHashTable         *establishing;
545ecd
+  /* active on the client side, and with a D-Bus match rule established */
545ecd
+  GHashTable         *active;
545ecd
 };
545ecd
 
545ecd
 /* When taking the sources lock we check if any of the databases have
545ecd
@@ -225,6 +231,78 @@ dconf_engine_unlock_queues (DConfEngine *engine)
545ecd
   g_mutex_unlock (&engine->queue_lock);
545ecd
 }
545ecd
 
545ecd
+/**
545ecd
+ * Adds the count of subscriptions to @path in @from_table to the
545ecd
+ * corresponding count in @to_table, creating it if it did not exist.
545ecd
+ * Removes the count from @from_table.
545ecd
+ */
545ecd
+static void
545ecd
+dconf_engine_move_subscriptions (GHashTable  *from_counts,
545ecd
+                                 GHashTable  *to_counts,
545ecd
+                                 const gchar *path)
545ecd
+{
545ecd
+  guint from_count = GPOINTER_TO_UINT (g_hash_table_lookup (from_counts, path));
545ecd
+  guint old_to_count = GPOINTER_TO_UINT (g_hash_table_lookup (to_counts, path));
545ecd
+  // Detect overflows
545ecd
+  g_assert (old_to_count <= G_MAXUINT32 - from_count);
545ecd
+  guint new_to_count = old_to_count + from_count;
545ecd
+  if (from_count != 0)
545ecd
+    {
545ecd
+      g_hash_table_remove (from_counts, path);
545ecd
+      g_hash_table_replace (to_counts,
545ecd
+                            g_strdup (path),
545ecd
+                            GUINT_TO_POINTER (new_to_count));
545ecd
+    }
545ecd
+}
545ecd
+
545ecd
+/**
545ecd
+ * Increments the reference count for the subscription to @path, or sets
545ecd
+ * it to 1 if it didn’t previously exist.
545ecd
+ * Returns the new reference count.
545ecd
+ */
545ecd
+static guint
545ecd
+dconf_engine_inc_subscriptions (GHashTable  *counts,
545ecd
+                                const gchar *path)
545ecd
+{
545ecd
+  guint old_count = GPOINTER_TO_UINT (g_hash_table_lookup (counts, path));
545ecd
+  // Detect overflows
545ecd
+  g_assert (old_count < G_MAXUINT32);
545ecd
+  guint new_count = old_count + 1;
545ecd
+  g_hash_table_replace (counts, g_strdup (path), GUINT_TO_POINTER (new_count));
545ecd
+  return new_count;
545ecd
+}
545ecd
+
545ecd
+/**
545ecd
+ * Decrements the reference count for the subscription to @path, or
545ecd
+ * removes it if the new value is 0. The count must exist and be greater
545ecd
+ * than 0.
545ecd
+ * Returns the new reference count, or 0 if it does not exist.
545ecd
+ */
545ecd
+static guint
545ecd
+dconf_engine_dec_subscriptions (GHashTable  *counts,
545ecd
+                                const gchar *path)
545ecd
+{
545ecd
+  guint old_count = GPOINTER_TO_UINT (g_hash_table_lookup (counts, path));
545ecd
+  g_assert (old_count > 0);
545ecd
+  guint new_count = old_count - 1;
545ecd
+  if (new_count == 0)
545ecd
+    g_hash_table_remove (counts, path);
545ecd
+  else
545ecd
+    g_hash_table_replace (counts, g_strdup (path), GUINT_TO_POINTER (new_count));
545ecd
+  return new_count;
545ecd
+}
545ecd
+
545ecd
+/**
545ecd
+ * Returns the reference count for the subscription to @path, or 0 if it
545ecd
+ * does not exist.
545ecd
+ */
545ecd
+static guint
545ecd
+dconf_engine_count_subscriptions (GHashTable  *counts,
545ecd
+                                  const gchar *path)
545ecd
+{
545ecd
+  return GPOINTER_TO_UINT (g_hash_table_lookup (counts, path));
545ecd
+}
545ecd
+
545ecd
 DConfEngine *
545ecd
 dconf_engine_new (const gchar    *profile,
545ecd
                   gpointer        user_data,
545ecd
@@ -247,8 +325,14 @@ dconf_engine_new (const gchar    *profile,
545ecd
   dconf_engine_global_list = g_slist_prepend (dconf_engine_global_list, engine);
545ecd
   g_mutex_unlock (&dconf_engine_global_lock);
545ecd
 
545ecd
-  engine->watched_paths = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
545ecd
-  engine->pending_paths = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
545ecd
+  engine->establishing = g_hash_table_new_full (g_str_hash,
545ecd
+                                                g_str_equal,
545ecd
+                                                g_free,
545ecd
+                                                NULL);
545ecd
+  engine->active = g_hash_table_new_full (g_str_hash,
545ecd
+                                          g_str_equal,
545ecd
+                                          g_free,
545ecd
+                                          NULL);
545ecd
 
545ecd
   return engine;
545ecd
 }
545ecd
@@ -300,8 +384,8 @@ dconf_engine_unref (DConfEngine *engine)
545ecd
 
545ecd
       g_free (engine->sources);
545ecd
 
545ecd
-      g_hash_table_unref(engine->pending_paths);
545ecd
-      g_hash_table_unref(engine->watched_paths);
545ecd
+      g_hash_table_unref (engine->establishing);
545ecd
+      g_hash_table_unref (engine->active);
545ecd
 
545ecd
       if (engine->free_func)
545ecd
         engine->free_func (engine->user_data);
545ecd
@@ -847,7 +931,14 @@ dconf_engine_watch_established (DConfEngine  *engine,
545ecd
       dconf_engine_change_notify (engine, ow->path, changes, NULL, FALSE, NULL, engine->user_data);
545ecd
     }
545ecd
 
545ecd
-  dconf_engine_set_watching (engine, ow->path, TRUE, TRUE);
545ecd
+  guint num_establishing = dconf_engine_count_subscriptions (engine->establishing,
545ecd
+                                                             ow->path);
545ecd
+  if (num_establishing > 0)
545ecd
+    // Subscription(s): establishing -> active
545ecd
+    dconf_engine_move_subscriptions (engine->establishing,
545ecd
+                                     engine->active,
545ecd
+                                     ow->path);
545ecd
+
545ecd
   dconf_engine_call_handle_free (handle);
545ecd
 }
545ecd
 
545ecd
@@ -855,14 +946,17 @@ void
545ecd
 dconf_engine_watch_fast (DConfEngine *engine,
545ecd
                          const gchar *path)
545ecd
 {
545ecd
-  if (dconf_engine_is_watching (engine, path, TRUE))
545ecd
-    {
545ecd
-      /**
545ecd
-       * Either there is already a match rule in place for this exact path,
545ecd
-       * or there is already a request in progress to add a match.
545ecd
-       */
545ecd
-      return;
545ecd
-    }
545ecd
+  guint num_establishing = dconf_engine_count_subscriptions (engine->establishing, path);
545ecd
+  guint num_active = dconf_engine_count_subscriptions (engine->active, path);
545ecd
+  if (num_active > 0)
545ecd
+    // Subscription: inactive -> active
545ecd
+    dconf_engine_inc_subscriptions (engine->active, path);
545ecd
+  else
545ecd
+    // Subscription: inactive -> establishing
545ecd
+    num_establishing = dconf_engine_inc_subscriptions (engine->establishing,
545ecd
+                                                       path);
545ecd
+  if (num_establishing > 1 || num_active > 0)
545ecd
+    return;
545ecd
 
545ecd
   OutstandingWatch *ow;
545ecd
   gint i;
545ecd
@@ -897,23 +991,33 @@ dconf_engine_watch_fast (DConfEngine *engine,
545ecd
                                          "/org/freedesktop/DBus", "org.freedesktop.DBus", "AddMatch",
545ecd
                                          dconf_engine_make_match_rule (engine->sources[i], path),
545ecd
                                          &ow->handle, NULL);
545ecd
-
545ecd
-  dconf_engine_set_watching (engine, ow->path, TRUE, FALSE);
545ecd
 }
545ecd
 
545ecd
 void
545ecd
 dconf_engine_unwatch_fast (DConfEngine *engine,
545ecd
                            const gchar *path)
545ecd
 {
545ecd
+  guint num_active = dconf_engine_count_subscriptions (engine->active, path);
545ecd
+  guint num_establishing = dconf_engine_count_subscriptions (engine->establishing, path);
545ecd
   gint i;
545ecd
 
545ecd
+  // Client code cannot unsubscribe if it is not subscribed
545ecd
+  g_assert (num_active > 0 || num_establishing > 0);
545ecd
+  if (num_active == 0)
545ecd
+    // Subscription: establishing -> inactive
545ecd
+    num_establishing = dconf_engine_dec_subscriptions (engine->establishing, path);
545ecd
+  else
545ecd
+    // Subscription: active -> inactive
545ecd
+    num_active = dconf_engine_dec_subscriptions (engine->active, path);
545ecd
+
545ecd
+  if (num_active > 0 || num_establishing > 0)
545ecd
+    return;
545ecd
+
545ecd
   for (i = 0; i < engine->n_sources; i++)
545ecd
     if (engine->sources[i]->bus_type)
545ecd
       dconf_engine_dbus_call_async_func (engine->sources[i]->bus_type, "org.freedesktop.DBus",
545ecd
                                          "/org/freedesktop/DBus", "org.freedesktop.DBus", "RemoveMatch",
545ecd
                                          dconf_engine_make_match_rule (engine->sources[i], path), NULL, NULL);
545ecd
-
545ecd
-  dconf_engine_set_watching (engine, path, FALSE, FALSE);
545ecd
 }
545ecd
 
545ecd
 static void
545ecd
@@ -951,16 +1055,18 @@ void
545ecd
 dconf_engine_watch_sync (DConfEngine *engine,
545ecd
                          const gchar *path)
545ecd
 {
545ecd
-  dconf_engine_handle_match_rule_sync (engine, "AddMatch", path);
545ecd
-  dconf_engine_set_watching (engine, path, TRUE, TRUE);
545ecd
+  guint num_active = dconf_engine_inc_subscriptions (engine->active, path);
545ecd
+  if (num_active == 1)
545ecd
+    dconf_engine_handle_match_rule_sync (engine, "AddMatch", path);
545ecd
 }
545ecd
 
545ecd
 void
545ecd
 dconf_engine_unwatch_sync (DConfEngine *engine,
545ecd
                            const gchar *path)
545ecd
 {
545ecd
-  dconf_engine_handle_match_rule_sync (engine, "RemoveMatch", path);
545ecd
-  dconf_engine_set_watching (engine, path, FALSE, FALSE);
545ecd
+  guint num_active = dconf_engine_dec_subscriptions (engine->active, path);
545ecd
+  if (num_active == 0)
545ecd
+    dconf_engine_handle_match_rule_sync (engine, "RemoveMatch", path);
545ecd
 }
545ecd
 
545ecd
 typedef struct
545ecd
@@ -1418,42 +1524,3 @@ dconf_engine_sync (DConfEngine *engine)
545ecd
     g_cond_wait (&engine->queue_cond, &engine->queue_lock);
545ecd
   dconf_engine_unlock_queues (engine);
545ecd
 }
545ecd
-
545ecd
-void
545ecd
-dconf_engine_set_watching (DConfEngine    *engine,
545ecd
-                           const gchar    *path,
545ecd
-                           const gboolean  is_watching,
545ecd
-                           const gboolean  is_established)
545ecd
-{
545ecd
-  if (is_watching)
545ecd
-    {
545ecd
-      if (is_established)
545ecd
-        {
545ecd
-          g_hash_table_add (engine->watched_paths, g_strdup (path));
545ecd
-          g_hash_table_remove (engine->pending_paths, path);
545ecd
-        }
545ecd
-      else
545ecd
-        {
545ecd
-          g_hash_table_add (engine->pending_paths, g_strdup (path));
545ecd
-          g_hash_table_remove (engine->watched_paths, path);
545ecd
-        }
545ecd
-    }
545ecd
-  else
545ecd
-    {
545ecd
-      g_hash_table_remove (engine->watched_paths, path);
545ecd
-      g_hash_table_remove (engine->pending_paths, path);
545ecd
-    }
545ecd
-}
545ecd
-
545ecd
-gboolean
545ecd
-dconf_engine_is_watching (DConfEngine *engine, const gchar *path, const gboolean only_established)
545ecd
-{
545ecd
-  gconstpointer key = (gconstpointer) path;
545ecd
-  if (g_hash_table_contains (engine->watched_paths, key))
545ecd
-    return TRUE;
545ecd
-
545ecd
-  if (!only_established && g_hash_table_contains (engine->pending_paths, key))
545ecd
-    return TRUE;
545ecd
-
545ecd
-  return FALSE;
545ecd
-}
545ecd
diff --git a/engine/dconf-engine.h b/engine/dconf-engine.h
545ecd
index 06ed5a7..2485423 100644
545ecd
--- a/engine/dconf-engine.h
545ecd
+++ b/engine/dconf-engine.h
545ecd
@@ -104,17 +104,6 @@ DConfEngine *           dconf_engine_new                                (const g
545ecd
 G_GNUC_INTERNAL
545ecd
 void                    dconf_engine_unref                              (DConfEngine             *engine);
545ecd
 
545ecd
-G_GNUC_INTERNAL
545ecd
-void                    dconf_engine_set_watching                       (DConfEngine             *engine,
545ecd
-                                                                         const gchar             *path,
545ecd
-                                                                         const gboolean           is_watching,
545ecd
-                                                                         const gboolean           is_established);
545ecd
-
545ecd
-G_GNUC_INTERNAL
545ecd
-gboolean                dconf_engine_is_watching                        (DConfEngine             *engine,
545ecd
-                                                                         const gchar             *path,
545ecd
-                                                                         const gboolean           only_established);
545ecd
-
545ecd
 /* Read API: always handled immediately */
545ecd
 G_GNUC_INTERNAL
545ecd
 guint64                 dconf_engine_get_state                          (DConfEngine             *engine);
545ecd
diff --git a/tests/engine.c b/tests/engine.c
545ecd
index aa1db1c..038c04c 100644
545ecd
--- a/tests/engine.c
545ecd
+++ b/tests/engine.c
545ecd
@@ -1210,13 +1210,16 @@ test_watch_fast (void)
545ecd
   c = dconf_engine_get_state (engine);
545ecd
   g_assert_cmpuint (b, ==, c);
545ecd
   /* The watch result was not sent, because the path was already watched */
545ecd
-  dconf_mock_dbus_assert_no_async();
545ecd
+  dconf_mock_dbus_assert_no_async ();
545ecd
   c = dconf_engine_get_state (engine);
545ecd
   g_assert_cmpuint (b, ==, c);
545ecd
   /* Since the path was already being watched,
545ecd
    * do not expect a second false change notification */
545ecd
   g_assert_cmpstr (change_log->str, ==, "/a/b/c:1::nil;");
545ecd
   dconf_engine_unwatch_fast (engine, "/a/b/c");
545ecd
+  /* nothing was done, because there is still a subscription left */
545ecd
+  dconf_mock_dbus_assert_no_async ();
545ecd
+  dconf_engine_unwatch_fast (engine, "/a/b/c");
545ecd
   dconf_mock_dbus_async_reply (triv, NULL);
545ecd
   dconf_mock_dbus_async_reply (triv, NULL);
545ecd
   dconf_mock_dbus_assert_no_async ();
545ecd
@@ -1286,54 +1289,6 @@ test_watch_sync (void)
545ecd
   match_request_type = NULL;
545ecd
 }
545ecd
 
545ecd
-static void
545ecd
-test_watching (void)
545ecd
-{
545ecd
-  DConfEngine *engine;
545ecd
-  const gchar *apple = "apple";
545ecd
-  const gchar *orange = "orange";
545ecd
-  const gchar *banana = "banana";
545ecd
-
545ecd
-  engine = dconf_engine_new (SRCDIR "/profile/dos", NULL, NULL);
545ecd
-
545ecd
-  g_assert (!dconf_engine_is_watching(engine, apple, TRUE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, apple, FALSE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, orange, TRUE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, orange, FALSE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, banana, TRUE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, banana, FALSE));
545ecd
-
545ecd
-  dconf_engine_set_watching (engine, apple, FALSE, FALSE);
545ecd
-  dconf_engine_set_watching (engine, orange, TRUE, FALSE);
545ecd
-  dconf_engine_set_watching (engine, banana, TRUE, TRUE);
545ecd
-
545ecd
-  g_assert (!dconf_engine_is_watching(engine, apple, TRUE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, apple, FALSE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, orange, TRUE));
545ecd
-  g_assert (dconf_engine_is_watching(engine, orange, FALSE));
545ecd
-  g_assert (dconf_engine_is_watching(engine, banana, TRUE));
545ecd
-  g_assert (dconf_engine_is_watching(engine, banana, FALSE));
545ecd
-
545ecd
-  dconf_engine_set_watching (engine, orange, TRUE, TRUE);
545ecd
-  dconf_engine_set_watching (engine, banana, FALSE, FALSE);
545ecd
-
545ecd
-  g_assert (!dconf_engine_is_watching(engine, apple, TRUE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, apple, FALSE));
545ecd
-  g_assert (dconf_engine_is_watching(engine, orange, TRUE));
545ecd
-  g_assert (dconf_engine_is_watching(engine, orange, FALSE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, banana, TRUE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, banana, FALSE));
545ecd
-
545ecd
-  dconf_engine_set_watching (engine, orange, FALSE, FALSE);
545ecd
-
545ecd
-  g_assert (!dconf_engine_is_watching(engine, apple, TRUE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, apple, FALSE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, orange, TRUE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, orange, FALSE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, banana, TRUE));
545ecd
-  g_assert (!dconf_engine_is_watching(engine, banana, FALSE));
545ecd
-}
545ecd
-
545ecd
 static void
545ecd
 test_change_fast (void)
545ecd
 {
545ecd
@@ -1819,7 +1774,6 @@ main (int argc, char **argv)
545ecd
   g_test_add_func ("/engine/read", test_read);
545ecd
   g_test_add_func ("/engine/watch/fast", test_watch_fast);
545ecd
   g_test_add_func ("/engine/watch/sync", test_watch_sync);
545ecd
-  g_test_add_func ("/engine/watch/watching", test_watching);
545ecd
   g_test_add_func ("/engine/change/fast", test_change_fast);
545ecd
   g_test_add_func ("/engine/change/sync", test_change_sync);
545ecd
   g_test_add_func ("/engine/signals", test_signals);
545ecd
-- 
545ecd
2.20.1
545ecd