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

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