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

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