Blame SOURCES/0004-Engine-Add-locks-around-access-to-subscription-count.patch

545ecd
From 21711aa40bed4e61bba7d5f9fee141825fe76823 Mon Sep 17 00:00:00 2001
545ecd
From: Daniel Playfair Cal <daniel.playfair.cal@gmail.com>
545ecd
Date: Thu, 26 Jul 2018 00:00:09 +1000
545ecd
Subject: [PATCH 4/5] Engine: Add locks around access to subscription counts to
545ecd
 ensure that each state transition is atomic
545ecd
545ecd
Update comment about threading, documenting the new lock
545ecd
545ecd
Add documentation comments for new utility functions
545ecd
---
545ecd
 engine/dconf-engine.c | 47 ++++++++++++++++++++++++++++++++++++++++---
545ecd
 1 file changed, 44 insertions(+), 3 deletions(-)
545ecd
545ecd
diff --git a/engine/dconf-engine.c b/engine/dconf-engine.c
545ecd
index 2911724..ad891e6 100644
545ecd
--- a/engine/dconf-engine.c
545ecd
+++ b/engine/dconf-engine.c
545ecd
@@ -128,7 +128,7 @@
545ecd
  * it is willing to deal with receiving the change notifies in those
545ecd
  * threads.
545ecd
  *
545ecd
- * Thread-safety is implemented using two locks.
545ecd
+ * Thread-safety is implemented using three locks.
545ecd
  *
545ecd
  * The first lock (sources_lock) protects the sources.  Although the
545ecd
  * sources are only ever read from, it is necessary to lock them because
545ecd
@@ -143,8 +143,15 @@
545ecd
  * The second lock (queue_lock) protects the various queues that are
545ecd
  * used to implement the "fast" writes described above.
545ecd
  *
545ecd
- * If both locks are held at the same time then the sources lock must
545ecd
- * have been acquired first.
545ecd
+ * The third lock (subscription_count_lock) protects the two hash tables
545ecd
+ * that are used to keep track of the number of subscriptions held by
545ecd
+ * the client library to each path.
545ecd
+ *
545ecd
+ * If sources_lock and queue_lock are held at the same time then then
545ecd
+ * sources_lock must have been acquired first.
545ecd
+ *
545ecd
+ * subscription_count_lock is never held at the same time as
545ecd
+ * sources_lock or queue_lock
545ecd
  */
545ecd
 
545ecd
 #define MAX_IN_FLIGHT 2
545ecd
@@ -174,6 +181,8 @@ struct _DConfEngine
545ecd
    * establishing and active, are hash tables storing the number
545ecd
    * of subscriptions to each path in the two possible states
545ecd
    */
545ecd
+  /* This lock ensures that transactions involving subscription counts are atomic */
545ecd
+  GMutex              subscription_count_lock;
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
@@ -303,6 +312,25 @@ dconf_engine_count_subscriptions (GHashTable  *counts,
545ecd
   return GPOINTER_TO_UINT (g_hash_table_lookup (counts, path));
545ecd
 }
545ecd
 
545ecd
+/**
545ecd
+ * Acquires the subscription counts lock, which must be held when
545ecd
+ * reading or writing to the subscription counts.
545ecd
+ */
545ecd
+static void
545ecd
+dconf_engine_lock_subscription_counts (DConfEngine *engine)
545ecd
+{
545ecd
+  g_mutex_lock (&engine->subscription_count_lock);
545ecd
+}
545ecd
+
545ecd
+/**
545ecd
+ * Releases the subscription counts lock
545ecd
+ */
545ecd
+static void
545ecd
+dconf_engine_unlock_subscription_counts (DConfEngine *engine)
545ecd
+{
545ecd
+  g_mutex_unlock (&engine->subscription_count_lock);
545ecd
+}
545ecd
+
545ecd
 DConfEngine *
545ecd
 dconf_engine_new (const gchar    *profile,
545ecd
                   gpointer        user_data,
545ecd
@@ -325,6 +353,7 @@ 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
+  g_mutex_init (&engine->subscription_count_lock);
545ecd
   engine->establishing = g_hash_table_new_full (g_str_hash,
545ecd
                                                 g_str_equal,
545ecd
                                                 g_free,
545ecd
@@ -387,6 +416,8 @@ dconf_engine_unref (DConfEngine *engine)
545ecd
       g_hash_table_unref (engine->establishing);
545ecd
       g_hash_table_unref (engine->active);
545ecd
 
545ecd
+      g_mutex_clear (&engine->subscription_count_lock);
545ecd
+
545ecd
       if (engine->free_func)
545ecd
         engine->free_func (engine->user_data);
545ecd
 
545ecd
@@ -932,6 +963,7 @@ 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_lock_subscription_counts (engine);
545ecd
   guint num_establishing = dconf_engine_count_subscriptions (engine->establishing,
545ecd
                                                              ow->path);
545ecd
   g_debug ("watch_established: \"%s\" (establishing: %d)", ow->path, num_establishing);
545ecd
@@ -941,6 +973,7 @@ dconf_engine_watch_established (DConfEngine  *engine,
545ecd
                                      engine->active,
545ecd
                                      ow->path);
545ecd
 
545ecd
+  dconf_engine_unlock_subscription_counts (engine);
545ecd
   dconf_engine_call_handle_free (handle);
545ecd
 }
545ecd
 
545ecd
@@ -948,6 +981,7 @@ void
545ecd
 dconf_engine_watch_fast (DConfEngine *engine,
545ecd
                          const gchar *path)
545ecd
 {
545ecd
+  dconf_engine_lock_subscription_counts (engine);
545ecd
   guint num_establishing = dconf_engine_count_subscriptions (engine->establishing, path);
545ecd
   guint num_active = dconf_engine_count_subscriptions (engine->active, path);
545ecd
   g_debug ("watch_fast: \"%s\" (establishing: %d, active: %d)", path, num_establishing, num_active);
545ecd
@@ -958,6 +992,7 @@ dconf_engine_watch_fast (DConfEngine *engine,
545ecd
     // Subscription: inactive -> establishing
545ecd
     num_establishing = dconf_engine_inc_subscriptions (engine->establishing,
545ecd
                                                        path);
545ecd
+  dconf_engine_unlock_subscription_counts (engine);
545ecd
   if (num_establishing > 1 || num_active > 0)
545ecd
     return;
545ecd
 
545ecd
@@ -1000,6 +1035,7 @@ void
545ecd
 dconf_engine_unwatch_fast (DConfEngine *engine,
545ecd
                            const gchar *path)
545ecd
 {
545ecd
+  dconf_engine_lock_subscription_counts (engine);
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
@@ -1014,6 +1050,7 @@ dconf_engine_unwatch_fast (DConfEngine *engine,
545ecd
     // Subscription: active -> inactive
545ecd
     num_active = dconf_engine_dec_subscriptions (engine->active, path);
545ecd
 
545ecd
+  dconf_engine_unlock_subscription_counts (engine);
545ecd
   if (num_active > 0 || num_establishing > 0)
545ecd
     return;
545ecd
 
545ecd
@@ -1059,7 +1096,9 @@ void
545ecd
 dconf_engine_watch_sync (DConfEngine *engine,
545ecd
                          const gchar *path)
545ecd
 {
545ecd
+  dconf_engine_lock_subscription_counts (engine);
545ecd
   guint num_active = dconf_engine_inc_subscriptions (engine->active, path);
545ecd
+  dconf_engine_unlock_subscription_counts (engine);
545ecd
   g_debug ("watch_sync: \"%s\" (active: %d)", path, num_active - 1);
545ecd
   if (num_active == 1)
545ecd
     dconf_engine_handle_match_rule_sync (engine, "AddMatch", path);
545ecd
@@ -1069,7 +1108,9 @@ void
545ecd
 dconf_engine_unwatch_sync (DConfEngine *engine,
545ecd
                            const gchar *path)
545ecd
 {
545ecd
+  dconf_engine_lock_subscription_counts (engine);
545ecd
   guint num_active = dconf_engine_dec_subscriptions (engine->active, path);
545ecd
+  dconf_engine_unlock_subscription_counts (engine);
545ecd
   g_debug ("unwatch_sync: \"%s\" (active: %d)", path, num_active + 1);
545ecd
   if (num_active == 0)
545ecd
     dconf_engine_handle_match_rule_sync (engine, "RemoveMatch", path);
545ecd
-- 
545ecd
2.20.1
545ecd