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

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