[dconf] Reduce GDBus abuse



commit 4e6c1dd0ca1103d6548e8f29629e687391d2a875
Author: Ryan Lortie <desrt desrt ca>
Date:   Tue Nov 9 13:35:24 2010 -0500

    Reduce GDBus abuse
    
    Use our own private thread instead

 client/dconf-client.vala         |   26 +-
 client/engine.vapi               |   14 +-
 engine/dconf-engine.c            |  106 +++++---
 engine/dconf-engine.h            |   46 +++-
 gsettings/Makefile.am            |    1 +
 gsettings/dconfcontext.c         |   35 +++
 gsettings/dconfcontext.h         |    8 +
 gsettings/dconfsettingsbackend.c |  619 ++++++++++++++++++++------------------
 8 files changed, 505 insertions(+), 350 deletions(-)
---
diff --git a/client/dconf-client.vala b/client/dconf-client.vala
index 2be8be9..810348e 100644
--- a/client/dconf-client.vala
+++ b/client/dconf-client.vala
@@ -11,23 +11,23 @@ namespace DConf {
 		void call_sync (EngineMessage dcem, out string tag, Cancellable? cancellable) throws Error {
 			DBusConnection connection;
 
-			if (dcem.bus_type == 'e') {
+			if (dcem.bus_types[0] == 'e') {
 				if (session == null) {
 					session = Bus.get_sync (BusType.SESSION, cancellable);
 				}
 				connection = session;
 			} else {
-				assert (dcem.bus_type == 'y');
+				assert (dcem.bus_types[0] == 'y');
 				if (system == null) {
 					system = Bus.get_sync (BusType.SYSTEM, cancellable);
 				}
 				connection = system;
 			}
 
-			foreach (var message in dcem.body) {
-				var reply = connection.call_sync (dcem.destination, dcem.object_path, dcem  interface, dcem.method,
+			foreach (var message in dcem.parameters) {
+				var reply = connection.call_sync (dcem.bus_name, dcem.object_path, dcem.interface_name, dcem.method_name,
 				                                  message, dcem.reply_type, DBusCallFlags.NONE, -1, cancellable);
-				if (dcem.tagged) {
+				if (dcem.reply_type != VariantType.UNIT) {
 					reply.get ("(s)", out tag);
 				}
 			}
@@ -36,23 +36,23 @@ namespace DConf {
 		async void call_async (EngineMessage dcem, out string tag, Cancellable? cancellable) throws Error {
 			DBusConnection connection;
 
-			if (dcem.bus_type == 'e') {
+			if (dcem.bus_types[0] == 'e') {
 				if (session == null) {
 					session = yield Bus.get (BusType.SESSION, cancellable);
 				}
 				connection = session;
 			} else {
-				assert (dcem.bus_type == 'y');
+				assert (dcem.bus_types[0] == 'y');
 				if (system == null) {
 					system = yield Bus.get (BusType.SYSTEM, cancellable);
 				}
 				connection = system;
 			}
 
-			foreach (var message in dcem.body) {
-				var reply = yield connection.call (dcem.destination, dcem.object_path, dcem  interface, dcem.method,
+			foreach (var message in dcem.parameters) {
+				var reply = yield connection.call (dcem.bus_name, dcem.object_path, dcem.interface_name, dcem.method_name,
 				                                   message, dcem.reply_type, DBusCallFlags.NONE, -1, cancellable);
-				if (dcem.tagged) {
+				if (dcem.reply_type != VariantType.UNIT) {
 					reply.get ("(s)", out tag);
 				}
 			}
@@ -356,10 +356,10 @@ namespace DConf {
 
 		static Variant? service_func (EngineMessage dcem) {
 			try {
-				assert (dcem.bus_type == 'e');
+				assert (dcem.bus_types[0] == 'e');
 				var connection = Bus.get_sync (BusType.SESSION, null);
-				return connection.call_sync (dcem.destination, dcem.object_path, dcem  interface, dcem.method,
-				                             dcem.body, dcem.reply_type, DBusCallFlags.NONE, -1, null);
+				return connection.call_sync (dcem.bus_name, dcem.object_path, dcem.interface_name, dcem.method_name,
+				                             dcem.parameters[0], dcem.reply_type, DBusCallFlags.NONE, -1, null);
 			} catch {
 				return null;
 			}
diff --git a/client/engine.vapi b/client/engine.vapi
index eb3e7bc..8ed9c92 100644
--- a/client/engine.vapi
+++ b/client/engine.vapi
@@ -17,14 +17,16 @@ namespace DConf {
 	}
 
 	struct EngineMessage {
-		int bus_type;
-		string destination;
+		string bus_name;
 		string object_path;
-		string @interface;
-		string method;
-		bool tagged;
+		string interface_name;
+		string method_name;
+		int n_messages;
+		[CCode (array_length_cname = "n_messages")]
+		GLib.Variant[] parameters;
+		[CCode (array_length_cname = "n_messages")]
+		char[] bus_types;
 		GLib.VariantType reply_type;
-		GLib.Variant body;
 	}
 
 	[CCode (has_target = false)]
diff --git a/engine/dconf-engine.c b/engine/dconf-engine.c
index 2b8bd70..2a3ccbd 100644
--- a/engine/dconf-engine.c
+++ b/engine/dconf-engine.c
@@ -36,20 +36,26 @@ static DConfEngineServiceFunc dconf_engine_service_func;
 void
 dconf_engine_message_destroy (DConfEngineMessage *dcem)
 {
-  if (dcem->body)
-    {
-      g_variant_unref (dcem->body);
-      dcem->body = NULL;
-    }
+  gint i;
+
+  for (i = 0; dcem->parameters[i]; i++)
+    g_variant_unref (dcem->parameters[i]);
+  g_free (dcem->parameters);
 }
 
 void
 dconf_engine_message_copy (DConfEngineMessage *orig,
                            DConfEngineMessage *copy)
 {
+  gint i, n;
+
   *copy = *orig;
-  if (orig->body)
-    copy->body = g_variant_ref (orig->body);
+
+  for (n = 0; orig->parameters[n]; n++);
+  copy->parameters = g_new (GVariant *, n + 1);
+  for (i = 0; i < n; i++)
+    copy->parameters[i] = g_variant_ref (orig->parameters[i]);
+  copy->parameters[i] = NULL;
 }
 
 void
@@ -71,20 +77,25 @@ dconf_engine_get_session_dir (void)
       if (session_dir == NULL)
         {
           DConfEngineMessage dcem;
+          GVariant *parameters[2];
           GVariant *result;
 
-          dcem.bus_type = 'e';
-          dcem.destination = "ca.desrt.dconf";
+          dcem.bus_types = "e";
+          dcem.bus_name = "ca.desrt.dconf";
           dcem.object_path = "/ca/desrt/dconf/Writer";
-          dcem.interface = "org.freedesktop.DBus.Properties";
-          dcem.method = "Get";
+          dcem.interface_name = "org.freedesktop.DBus.Properties";
+          dcem.method_name = "Get";
           dcem.reply_type = G_VARIANT_TYPE ("(v)");
-          dcem.body = g_variant_new ("((ss))",
-                                     "ca.desrt.dconf.WriterInfo",
-                                     "ShmDirectory");
+          parameters[0] = g_variant_new ("(ss)",
+                                         "ca.desrt.dconf.WriterInfo",
+                                         "ShmDirectory");
+          parameters[1] = NULL;
+          dcem.parameters = parameters;
 
           result = dconf_engine_service_func (&dcem);
 
+          g_variant_unref (parameters[0]);
+
           if (result != NULL)
             {
               GVariant *str;
@@ -117,6 +128,7 @@ struct _DConfEngine
 
   GvdbTable **gvdbs;
   gchar     **object_paths;
+  gchar      *bus_types;
   gchar     **names;
   gint        n_dbs;
 };
@@ -339,6 +351,8 @@ dconf_engine_new (const gchar *profile)
 
   engine->object_paths = g_new (gchar *, engine->n_dbs);
   engine->gvdbs = g_new0 (GvdbTable *, engine->n_dbs);
+  engine->bus_types = g_strdup ("eyyyyyyyyyyyyy");
+  engine->state = 0;
 
   for (i = 0; i < engine->n_dbs; i++)
     if (engine->names[i])
@@ -375,6 +389,7 @@ dconf_engine_free (DConfEngine *engine)
     }
 
   g_free (engine->object_paths);
+  g_free (engine->bus_types);
   g_free (engine->names);
   g_free (engine->gvdbs);
 
@@ -431,19 +446,36 @@ dconf_engine_read_no_default (DConfEngine  *engine,
 static void
 dconf_engine_make_match_rule (DConfEngine        *engine,
                               DConfEngineMessage *dcem,
-                              const gchar        *name)
+                              const gchar        *name,
+                              const gchar        *method_name)
 {
-  gchar *rule;
+  gint i;
 
-  rule = g_strdup_printf ("type='signal',interface='ca.desrt.dconf.Writer',"
-                          "path='%s',arg0path='%s'",
-                          engine->object_paths[0], name);
-  dcem->bus_type = 'e';
-  dcem->destination = "org.freedesktop.DBus";
+  dcem->bus_name = "org.freedesktop.DBus";
   dcem->object_path = "/org/freedesktop/DBus";
-  dcem->interface = "org.freedesktop.DBus";
-  dcem->body = g_variant_ref_sink (g_variant_new ("((s))", rule));
-  g_free (rule);
+  dcem->interface_name = "org.freedesktop.DBus";
+  dcem->method_name = method_name;
+
+  dcem->parameters = g_new (GVariant *, engine->n_dbs + 1);
+  for (i = 0; i < engine->n_dbs; i++)
+    {
+      gchar *rule;
+
+      rule = g_strdup_printf ("type='signal',"
+                              "interface='ca.desrt.dconf.Writer',"
+                              "path='%s',"
+                              "arg0path='%s'",
+                              engine->object_paths[i],
+                              name);
+      dcem->parameters[i] = g_variant_new ("(s)", rule);
+      g_variant_ref_sink (dcem->parameters[i]);
+      g_free (rule);
+    }
+  dcem->parameters[i] = NULL;
+
+  dcem->bus_types = engine->bus_types;
+  dcem->n_messages = engine->n_dbs;
+  dcem->reply_type = G_VARIANT_TYPE_UNIT;
 }
 
 void
@@ -451,8 +483,7 @@ dconf_engine_watch (DConfEngine        *engine,
                     const gchar        *name,
                     DConfEngineMessage *dcem)
 {
-  dconf_engine_make_match_rule (engine, dcem, name);
-  dcem->method = "AddMatch";
+  dconf_engine_make_match_rule (engine, dcem, name, "AddMatch");
 }
 
 void
@@ -460,8 +491,7 @@ dconf_engine_unwatch (DConfEngine        *engine,
                       const gchar        *name,
                       DConfEngineMessage *dcem)
 {
-  dconf_engine_make_match_rule (engine, dcem, name);
-  dcem->method = "RemoveMatch";
+  dconf_engine_make_match_rule (engine, dcem, name, "RemoveMatch");
 }
 
 gboolean
@@ -487,23 +517,27 @@ fake_maybe (GVariant *value)
 static void
 dconf_engine_dcem (DConfEngine        *engine,
                    DConfEngineMessage *dcem,
-                   const gchar        *method,
+                   const gchar        *method_name,
                    const gchar        *format_string,
                    ...)
 {
   va_list ap;
 
-  dcem->bus_type = 'e';
-  dcem->destination = "ca.desrt.dconf";
+  dcem->bus_name = "ca.desrt.dconf";
   dcem->object_path = engine->object_paths[0];
-  dcem->interface = "ca.desrt.dconf.Writer";
-  dcem->reply_type = G_VARIANT_TYPE ("(s)");
-  dcem->method = method;
-  dcem->tagged = TRUE;
+  dcem->interface_name = "ca.desrt.dconf.Writer";
+  dcem->method_name = method_name;
+  dcem->parameters = g_new (GVariant *, 2);
+  dcem->n_messages = 1;
 
   va_start (ap, format_string);
-  dcem->body = g_variant_ref_sink (g_variant_new_variant (g_variant_new_va (format_string, NULL, &ap)));
+  dcem->parameters[0] = g_variant_new_va (format_string, NULL, &ap);
+  g_variant_ref_sink (dcem->parameters[0]);
+  dcem->parameters[1] = NULL;
   va_end (ap);
+
+  dcem->bus_types = engine->bus_types;
+  dcem->reply_type = G_VARIANT_TYPE ("(s)");
 }
 
 gboolean
diff --git a/engine/dconf-engine.h b/engine/dconf-engine.h
index 229c1a1..5de5f3c 100644
--- a/engine/dconf-engine.h
+++ b/engine/dconf-engine.h
@@ -28,18 +28,46 @@
 
 typedef struct _DConfEngine DConfEngine;
 
+/**
+ * DConfEngineMessage:
+ *
+ * This structure represents a number of DBus method call messages that #DConfEngine would like to send.
+ *
+ * #DConfEngine itself is unaware of a particular DBus or main loop implementation.  As such, all requests are
+ * synchronous and non-blocking, but most of them produce a #DConfEngineMessage describing messages that must be
+ * sent in order for the operation to be completed.
+ *
+ * @bus_name, @object_path, @interface_name, @method_name specify the respective header fields of the method
+ * call.  These are always equal for all of the calls contained within a single #DConfEngineMessage.
+ *
+ * @reply_type is the expected reply type of the method call.  This is also the same for all calls contained
+ * within a single #DConfEngineMessage.
+ *
+ * @n_messages is the number of messages to send.
+ *
+ * @bus_types and @parameters are both arrays, of length @n_messages.  Each element of @bus_type is the bus type
+ * to send each method call on and each of @parameters is the body of that call.  The reason that there may be
+ * several messages is that a single dconf "watch" operation may need to send multiple DBus "AddMatch" calls
+ * (and usually to multiple busses).
+ *
+ * Each element in @bus_types is either 'y' for system bus or 'e' for session bus.
+ *
+ * A #DConfEngineMessage is always stack-allocated by the caller.  It must be cleared using
+ * dconf_engine_message_destroy() when done.  It may be copied using dconf_engine_message_copy().
+ */
 typedef struct
 {
-  gint         bus_type;
-  const gchar *destination;
-  const gchar *object_path;
-  const gchar *interface;
-  const gchar *method;
-  gboolean     tagged;
-  const GVariantType *reply_type;
-  GVariant    *body;
-} DConfEngineMessage;
+  const gchar         *bus_name;
+  const gchar         *object_path;
+  const gchar         *interface_name;
+  const gchar         *method_name;
+
+  gint                 n_messages;
+  GVariant           **parameters;
+  const gchar         *bus_types;
 
+  const GVariantType  *reply_type;
+} DConfEngineMessage;
 
 typedef GVariant *    (*DConfEngineServiceFunc)                         (DConfEngineMessage      *message);
 void                    dconf_engine_message_copy                       (DConfEngineMessage      *orig,
diff --git a/gsettings/Makefile.am b/gsettings/Makefile.am
index 92c1154..b26ae71 100644
--- a/gsettings/Makefile.am
+++ b/gsettings/Makefile.am
@@ -9,6 +9,7 @@ libdconfsettings_so_SOURCES = \
 	../engine/dconf-engine.c	\
 	../common/dconf-shmdir.c	\
 	../gvdb/gvdb-reader.c		\
+	dconfcontext.c			\
 	dconfsettingsbackend.c
 
 uninstall-hook:
diff --git a/gsettings/dconfcontext.c b/gsettings/dconfcontext.c
new file mode 100644
index 0000000..339507e
--- /dev/null
+++ b/gsettings/dconfcontext.c
@@ -0,0 +1,35 @@
+#include "dconfcontext.h"
+
+static gpointer
+dconf_context_thread (gpointer data)
+{
+  GMainContext *context = data;
+  GMainLoop *loop;
+
+  g_main_context_push_thread_default (context);
+  loop = g_main_loop_new (context, FALSE);
+  g_main_loop_run (loop);
+
+  g_assert_not_reached ();
+}
+
+GMainContext *
+dconf_context_get (void)
+{
+  static GMainContext *context;
+  static gsize initialised;
+
+  if (g_once_init_enter (&initialised))
+    {
+      GThread *thread;
+
+      context = g_main_context_new ();
+      thread = g_thread_create (dconf_context_thread,
+                                context, FALSE, NULL);
+      g_assert (thread != NULL);
+
+      g_once_init_leave (&initialised, 1);
+    }
+
+  return context;
+}
diff --git a/gsettings/dconfcontext.h b/gsettings/dconfcontext.h
new file mode 100644
index 0000000..ddfb102
--- /dev/null
+++ b/gsettings/dconfcontext.h
@@ -0,0 +1,8 @@
+#ifndef _dconfcontext_h_
+#define _dconfcontext_h_
+
+#include <glib.h>
+
+GMainContext *          dconf_context_get                               (void);
+
+#endif
diff --git a/gsettings/dconfsettingsbackend.c b/gsettings/dconfsettingsbackend.c
index 8b2829b..a70f2e8 100644
--- a/gsettings/dconfsettingsbackend.c
+++ b/gsettings/dconfsettingsbackend.c
@@ -26,22 +26,25 @@
 
 #include <string.h>
 
+#include "dconfcontext.h"
+
 typedef GSettingsBackendClass DConfSettingsBackendClass;
 typedef struct _Outstanding Outstanding;
 
 typedef struct
 {
   GSettingsBackend backend;
-  GStaticMutex lock;
-
-  DConfEngine *engine;
 
   GDBusConnection *session_bus;
-  gchar *session_anti_expose;
   GDBusConnection *system_bus;
-  gchar *system_anti_expose;
+  guint session_subscription;
+  guint system_subscription;
 
   Outstanding *outstanding;
+  gchar *anti_expose_tag;
+
+  DConfEngine *engine;
+  GStaticMutex lock;
   GCond *sync_cond;
 } DConfSettingsBackend;
 
@@ -50,120 +53,263 @@ G_DEFINE_TYPE (DConfSettingsBackend,
                dconf_settings_backend,
                G_TYPE_SETTINGS_BACKEND)
 
-
-struct _Outstanding
+static void
+dconf_settings_backend_signal (GDBusConnection *connection,
+                               const gchar     *sender_name,
+                               const gchar     *object_path,
+                               const gchar     *interface_name,
+                               const gchar     *signal_name,
+                               GVariant        *parameters,
+                               gpointer         user_data)
 {
-  Outstanding *next;
+  DConfSettingsBackend *dcsb = user_data;
+  const gchar *anti_expose;
+  const gchar **rels;
+  const gchar *path;
+  gchar bus_type;
 
-  DConfEngineMessage dcem;
-  volatile guint32 serial;
+  if (connection == dcsb->session_bus)
+    {
+      anti_expose = dcsb->anti_expose_tag;
+      bus_type = 'e';
+    }
 
-  gchar *set_key;
-  GVariant *set_value;
+  else if (connection == dcsb->system_bus)
+    {
+      anti_expose = NULL;
+      bus_type = 'y';
+    }
 
-  GTree *tree;
-};
+  else
+    g_assert_not_reached ();
 
-static volatile guint32 *
-dconf_settings_backend_new_outstanding (DConfSettingsBackend *dcsb,
-                                        DConfEngineMessage   *dcem,
-                                        const gchar          *set_key,
-                                        GVariant             *set_value,
-                                        GTree                *tree)
+  if (dconf_engine_decode_notify (dcsb->engine, anti_expose,
+                                  &path, &rels, bus_type,
+                                  sender_name, interface_name,
+                                  signal_name, parameters))
+    {
+      GSettingsBackend *backend = G_SETTINGS_BACKEND (dcsb);
+
+      if (!g_str_has_suffix (path, "/"))
+        g_settings_backend_changed (backend, path, NULL);
+
+      else if (rels[0] == NULL)
+        g_settings_backend_path_changed (backend, path, NULL);
+
+      else
+        g_settings_backend_keys_changed (backend, path, rels, NULL);
+
+      g_free (rels);
+    }
+}
+
+static void
+dconf_settings_backend_send (DConfSettingsBackend *dcsb,
+                             DConfEngineMessage   *dcem,
+                             GAsyncReadyCallback   callback,
+                             gpointer              user_data)
 {
-  Outstanding *outstanding;
+  GDBusConnection *connection;
+  GError *error = NULL;
+  gint i;
 
-  outstanding = g_slice_new (Outstanding);
-  outstanding->set_key = NULL;
-  outstanding->dcem = *dcem;
+  for (i = 0; i < dcem->n_messages; i++)
+    {
+      switch (dcem->bus_types[i])
+        {
+        case 'e':
+          if (dcsb->session_bus == NULL && callback)
+            {
+              dcsb->session_bus =
+                g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+
+              if (dcsb->session_bus != NULL)
+                dcsb->session_subscription =
+                  g_dbus_connection_signal_subscribe (dcsb->session_bus, NULL,
+                                                      "ca.desrt.dconf.Writer",
+                                                      NULL, NULL, NULL,
+                                                      G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
+                                                      dconf_settings_backend_signal,
+                                                      dcsb, NULL);
+            }
+          connection = dcsb->session_bus;
+          break;
 
-  outstanding->set_key = g_strdup (set_key);
-  outstanding->serial = 0;
+        case 'y':
+          if (dcsb->system_bus == NULL && callback)
+            {
+              dcsb->system_bus =
+                g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+
+              if (dcsb->system_bus != NULL)
+                dcsb->system_subscription =
+                  g_dbus_connection_signal_subscribe (dcsb->system_bus, NULL,
+                                                      "ca.desrt.dconf.Writer",
+                                                      NULL, NULL, NULL,
+                                                      G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
+                                                      dconf_settings_backend_signal,
+                                                      dcsb, NULL);
+            }
+          connection = dcsb->system_bus;
+          break;
 
-  if (set_value)
-    outstanding->set_value = g_variant_ref_sink (set_value);
-  else
-    outstanding->set_value = NULL;
+        default:
+          g_assert_not_reached ();
+        }
 
-  if (tree)
-    outstanding->tree = g_tree_ref (tree);
-  else
-    outstanding->tree = NULL;
+      if (connection == NULL && callback != NULL)
+        callback (NULL, NULL, user_data);
+
+      if (connection != NULL)
+        g_dbus_connection_call (connection,
+                                dcem->bus_name,
+                                dcem->object_path,
+                                dcem->interface_name,
+                                dcem->method_name,
+                                dcem->parameters[i],
+                                dcem->reply_type,
+                                0, -1, NULL, callback, user_data);
+    }
+}
 
-  g_static_mutex_lock (&dcsb->lock);
-  outstanding->next = dcsb->outstanding;
-  dcsb->outstanding = outstanding;
-  g_static_mutex_unlock (&dcsb->lock);
+static GVariant *
+dconf_settings_backend_send_finish (GObject      *source,
+                                    GAsyncResult *result)
+{
+  if (source == NULL)
+    return NULL;
 
-  return &outstanding->serial;
+  return g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
+                                        result, NULL);
 }
 
-static gboolean
-dconf_settings_backend_remove_outstanding (DConfSettingsBackend  *dcsb,
-                                           guint                  bus_type,
-                                           GDBusMessage          *message,
-                                           gchar                **anti_expose)
+struct _Outstanding
 {
-  gboolean found = FALSE;
-  Outstanding **node;
-  guint32 serial;
+  Outstanding *next;
 
-  if G_LIKELY (dcsb->outstanding == NULL)
-    return FALSE;
+  DConfSettingsBackend *dcsb;
+  DConfEngineMessage    dcem;
 
-  serial = g_dbus_message_get_reply_serial (message);
+  gchar    *set_key;
+  GVariant *set_value;
+  GTree    *tree;
+};
 
-  if (serial == 0)
-    return FALSE;
+static void
+dconf_settings_backend_outstanding_returned (GObject      *source,
+                                             GAsyncResult *result,
+                                             gpointer      user_data)
+{
+  Outstanding *outstanding = user_data;
+  DConfSettingsBackend *dcsb;
+  GVariant *reply;
 
-  g_static_mutex_lock (&dcsb->lock);
+  dcsb = outstanding->dcsb;
 
-  /* this could be made more asymptotically efficient by using a queue
-   * or a double-linked list with a 'tail' pointer but the usual case
-   * here will be one outstanding item and very rarely more than a few.
-   *
-   * so we scan...
+  /* One way or another we no longer need this hooked into the list.
    */
-  for (node = &dcsb->outstanding; *node; node = &(*node)->next)
-    if ((*node)->serial == serial)
-      {
-        Outstanding *tmp;
+  g_static_mutex_lock (&dcsb->lock);
+  {
+    Outstanding **tmp;
 
-        tmp = *node;
-        *node = tmp->next;
+    for (tmp = &dcsb->outstanding; tmp; tmp = &(*tmp)->next)
+      if (*tmp == outstanding)
+        {
+          *tmp = outstanding->next;
+          break;
+        }
 
-        if (dcsb->outstanding == NULL && dcsb->sync_cond != NULL)
-          g_cond_signal (dcsb->sync_cond);
+    if (dcsb->outstanding == NULL && dcsb->sync_cond)
+      g_cond_broadcast (dcsb->sync_cond);
+  }
+  g_static_mutex_unlock (&dcsb->lock);
 
-        g_static_mutex_unlock (&dcsb->lock);
+  reply = dconf_settings_backend_send_finish (source, result);
 
-        g_free (tmp->set_key);
+  if (reply)
+    {
+      /* Success.
+       *
+       * We want to ensure that we don't emit an extra change
+       * notification signal when we see the signal that the service is
+       * about to send, so store the tag so we know to ignore it when
+       * the signal comes.
+       *
+       * No thread safety issue here since this variable is only
+       * accessed from the worker thread.
+       */
+      g_free (dcsb->anti_expose_tag);
+      g_variant_get_child (reply, 0, "s", &dcsb->anti_expose_tag);
+      g_variant_unref (reply);
+    }
+  else
+    {
+      /* An error of some kind.
+       *
+       * We already removed the outstanding entry from the list, so the
+       * unmodified database is now visible to the client.  Change
+       * notify so that they see it.
+       */
+      if (outstanding->set_key)
+        g_settings_backend_changed (G_SETTINGS_BACKEND (dcsb),
+                                    outstanding->set_key, NULL);
 
-        if (tmp->set_value)
-          g_variant_unref (tmp->set_value);
+      else
+        g_settings_backend_changed_tree (G_SETTINGS_BACKEND (dcsb),
+                                         outstanding->tree, NULL);
+    }
+
+  dconf_engine_message_destroy (&outstanding->dcem);
+  g_object_unref (outstanding->dcsb);
+  g_free (outstanding->set_key);
+
+  if (outstanding->set_value)
+    g_variant_unref (outstanding->set_value);
+
+  if (outstanding->tree)
+    g_tree_unref (outstanding->tree);
+
+  g_slice_free (Outstanding, outstanding);
+}
 
-        if (tmp->tree)
-          g_tree_unref (tmp->tree);
+static gboolean
+dconf_settings_backend_send_outstanding (gpointer data)
+{
+  Outstanding *outstanding = data;
 
-        if (*anti_expose)
-          {
-            g_free (*anti_expose);
-            *anti_expose = NULL;
-          }
+  dconf_settings_backend_send (outstanding->dcsb,
+                               &outstanding->dcem,
+                               dconf_settings_backend_outstanding_returned,
+                               outstanding);
+
+  return FALSE;
+}
+
+static void
+dconf_settings_backend_queue (DConfSettingsBackend *dcsb,
+                              DConfEngineMessage   *dcem,
+                              const gchar          *set_key,
+                              GVariant             *set_value,
+                              GTree                *tree)
+{
+  Outstanding *outstanding;
 
-        dconf_engine_interpret_reply (&tmp->dcem,
-                                      g_dbus_message_get_sender (message),
-                                      g_dbus_message_get_body (message),
-                                      anti_expose, NULL);
-        g_slice_free (Outstanding, tmp);
+  outstanding = g_slice_new (Outstanding);
+  outstanding->dcsb = g_object_ref (dcsb);
+  outstanding->dcem = *dcem;
 
-        found = TRUE;
-        break;
-      }
+  outstanding->set_key = g_strdup (set_key);
+  outstanding->set_value = set_value ? g_variant_ref_sink (set_value) : NULL;
+  outstanding->tree = tree ? g_tree_ref (tree) : NULL;
 
+  g_static_mutex_lock (&dcsb->lock);
+  outstanding->next = dcsb->outstanding;
+  dcsb->outstanding = outstanding;
   g_static_mutex_unlock (&dcsb->lock);
 
-  return found;
+  g_main_context_invoke (dconf_context_get (),
+                         dconf_settings_backend_send_outstanding,
+                         outstanding);
 }
 
 static gboolean
@@ -244,87 +390,6 @@ dconf_settings_backend_scan_outstanding (DConfSettingsBackend  *backend,
   return found;
 }
 
-static void
-dconf_settings_backend_incoming_signal (DConfSettingsBackend  *dcsb,
-                                        guint                  bt,
-                                        GDBusMessage          *message,
-                                        gchar                **anti_expose)
-{
-  const gchar **rels;
-  const gchar *path;
-
-  if (dconf_engine_decode_notify (dcsb->engine, *anti_expose,
-                                  &path, &rels, bt,
-                                  g_dbus_message_get_sender (message),
-                                  g_dbus_message_get_interface (message),
-                                  g_dbus_message_get_member (message),
-                                  g_dbus_message_get_body (message)))
-    {
-      GSettingsBackend *backend = G_SETTINGS_BACKEND (dcsb);
-
-      if (!g_str_has_suffix (path, "/"))
-        g_settings_backend_changed (backend, path, NULL);
-
-      else if (rels[0] == NULL)
-        g_settings_backend_path_changed (backend, path, NULL);
-
-      else
-        g_settings_backend_keys_changed (backend, path, rels, NULL);
-
-      g_free (*anti_expose);
-      *anti_expose = NULL;
-      g_free (rels);
-    }
-}
-
-static GDBusMessage *
-dconf_settings_backend_filter (GDBusConnection *connection,
-                               GDBusMessage    *message,
-                               gboolean         is_incoming,
-                               gpointer         user_data)
-{
-  DConfSettingsBackend *dcsb = user_data;
-  guint bus_type;
-  gchar **ae;
-
-  if (!is_incoming)
-    return message;
-
-  if (connection == dcsb->session_bus)
-    {
-      ae = &dcsb->session_anti_expose;
-      bus_type = 'e';
-    }
-  else if (connection == dcsb->system_bus)
-    {
-      ae = &dcsb->system_anti_expose;
-      bus_type = 'y';
-    }
-
-  else
-    g_assert_not_reached ();
-
-  switch (g_dbus_message_get_message_type (message))
-    {
-    case G_DBUS_MESSAGE_TYPE_METHOD_RETURN:
-      if (dconf_settings_backend_remove_outstanding (dcsb, bus_type,
-                                                     message, ae))
-        {
-          /* consume message */
-          g_object_unref (message);
-          return NULL;
-        }
-      else
-        return message;
-
-    case G_DBUS_MESSAGE_TYPE_SIGNAL:
-      dconf_settings_backend_incoming_signal (dcsb, bus_type, message, ae);
-
-    default:
-      return message;
-    }
-}
-
 static GVariant *
 dconf_settings_backend_read (GSettingsBackend   *backend,
                              const gchar        *key,
@@ -346,86 +411,6 @@ dconf_settings_backend_read (GSettingsBackend   *backend,
     return dconf_engine_read_default (dcsb->engine, key);
 }
 
-static void
-dconf_settings_backend_send (GDBusConnection    *bus,
-                             DConfEngineMessage *dcem,
-                             volatile guint32   *serial)
-{
-  GDBusMessage *message;
-  GVariant *body;
-
-  message = g_dbus_message_new_method_call (dcem->destination,
-                                            dcem->object_path,
-                                            dcem->interface,
-                                            dcem->method);
-  body = g_variant_get_child_value (dcem->body, 0);
-  g_dbus_message_set_body (message, body);
-  g_variant_unref (body);
-
-  if (serial)
-    g_dbus_connection_send_message (bus, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, serial, NULL);
-  else
-    g_dbus_connection_send_message_with_reply_sync (bus, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE,
-                                                    -1, NULL, NULL, NULL);
-
-  g_variant_unref (dcem->body);
-  g_object_unref (message);
-}
-
-static gboolean
-dconf_settings_backend_get_bus (DConfSettingsBackend  *dcsb,
-                                GDBusConnection      **bus,
-                                DConfEngineMessage    *dcem)
-{
-  switch (dcem->bus_type)
-    {
-    case 'e':
-      if (dcsb->session_bus == NULL)
-        {
-          g_static_mutex_lock (&dcsb->lock);
-          if (dcsb->session_bus == NULL)
-            {
-              dcsb->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION,
-                                                  NULL, NULL);
-              g_dbus_connection_add_filter (dcsb->session_bus,
-                                            dconf_settings_backend_filter,
-                                            dcsb, NULL);
-            }
-
-          g_static_mutex_unlock (&dcsb->lock);
-        }
-
-      *bus = dcsb->session_bus;
-      break;
-
-    case 'y':
-      if (dcsb->system_bus == NULL)
-        {
-          g_static_mutex_lock (&dcsb->lock);
-          if (dcsb->system_bus == NULL)
-            {
-              dcsb->system_bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM,
-                                                  NULL, NULL);
-              g_dbus_connection_add_filter (dcsb->session_bus,
-                                            dconf_settings_backend_filter,
-                                            dcsb, NULL);
-            }
-          g_static_mutex_unlock (&dcsb->lock);
-        }
-
-      *bus = dcsb->system_bus;
-      break;
-
-    default:
-      g_assert_not_reached ();
-    }
-
-  if (*bus == NULL && dcem->body)
-    g_variant_unref (dcem->body);
-
-  return *bus != NULL;
-}
-
 static gboolean
 dconf_settings_backend_write (GSettingsBackend *backend,
                               const gchar      *key,
@@ -433,20 +418,12 @@ dconf_settings_backend_write (GSettingsBackend *backend,
                               gpointer          origin_tag)
 {
   DConfSettingsBackend *dcsb = (DConfSettingsBackend *) backend;
-  volatile guint32 *serial;
   DConfEngineMessage dcem;
-  GDBusConnection *bus;
 
   if (!dconf_engine_write (dcsb->engine, key, value, &dcem, NULL))
     return FALSE;
 
-  if (!dconf_settings_backend_get_bus (dcsb, &bus, &dcem))
-    return FALSE;
-
-  serial = dconf_settings_backend_new_outstanding (dcsb, &dcem, key,
-                                                   value, NULL);
-  dconf_settings_backend_send (bus, &dcem, serial);
-
+  dconf_settings_backend_queue (dcsb, &dcem, key, value, NULL);
   g_settings_backend_changed (backend, key, origin_tag);
 
   return TRUE;
@@ -458,32 +435,25 @@ dconf_settings_backend_write_tree (GSettingsBackend *backend,
                                    gpointer          origin_tag)
 {
   DConfSettingsBackend *dcsb = (DConfSettingsBackend *) backend;
-  gboolean success = FALSE;
-  volatile guint32 *serial;
   DConfEngineMessage dcem;
-  GDBusConnection *bus;
   const gchar **keys;
   GVariant **values;
+  gboolean success;
   gchar *prefix;
 
   g_settings_backend_flatten_tree (tree, &prefix, &keys, &values);
 
-  if (dconf_engine_write_many (dcsb->engine,
-                               prefix, keys, values, &dcem, NULL))
+  if ((success = dconf_engine_write_many (dcsb->engine, prefix,
+                                          keys, values, &dcem, NULL)))
     {
-      if (dconf_settings_backend_get_bus (dcsb, &bus, &dcem))
-        {
-          serial = dconf_settings_backend_new_outstanding (dcsb, &dcem,
-                                                           NULL, NULL, tree);
-
-          dconf_settings_backend_send (bus, &dcem, serial);
-
-          g_settings_backend_keys_changed (backend, prefix, keys, origin_tag);
-
-          success = TRUE;
-        }
+      dconf_settings_backend_queue (dcsb, &dcem, NULL, NULL, tree);
+      g_settings_backend_keys_changed (backend, prefix, keys, origin_tag);
     }
 
+  g_free (prefix);
+  g_free (values);
+  g_free (keys);
+
   return success;
 }
 
@@ -504,18 +474,97 @@ dconf_settings_backend_get_writable (GSettingsBackend *backend,
   return dconf_engine_is_writable (dcsb->engine, name);
 }
 
+typedef struct
+{
+  DConfSettingsBackend *dcsb;
+  guint64 state;
+  gchar name[1];
+} OutstandingWatch;
+
+static OutstandingWatch *
+outstanding_watch_new (DConfSettingsBackend *dcsb,
+                       const gchar          *name)
+{
+  OutstandingWatch *watch;
+  gsize length;
+
+  length = strlen (name);
+  watch = g_malloc (G_STRUCT_OFFSET (OutstandingWatch, name) + length + 1);
+  watch->dcsb = g_object_ref (dcsb);
+  watch->state = dconf_engine_get_state (dcsb->engine);
+  strcpy (watch->name, name);
+
+  return watch;
+}
+
+static void
+outstanding_watch_free (OutstandingWatch *watch)
+{
+  g_object_unref (watch->dcsb);
+  g_free (watch);
+}
+
+static void
+add_match_done (GObject      *source,
+                GAsyncResult *result,
+                gpointer      user_data)
+{
+  OutstandingWatch *watch = user_data;
+  GError *error = NULL;
+  GVariant *reply;
+
+  reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
+                                         result, &error);
+
+  if (reply == NULL)
+    {
+      g_critical ("DBus AddMatch for dconf path '%s': %s",
+                  watch->name, error->message);
+      outstanding_watch_free (watch);
+      g_error_free (error);
+
+      return;
+    }
+
+  else
+    g_variant_unref (reply); /* it is just an empty tuple */
+
+  /* In the normal case we can just free everything and be done.
+   *
+   * There is a fleeting chance, however, that the database has changed
+   * in the meantime.  In that case we can only assume that the subject
+   * of this watch changed in that time period and emit a signal to that
+   * effect.
+   */
+  if (dconf_engine_get_state (watch->dcsb->engine) != watch->state)
+    g_settings_backend_path_changed (G_SETTINGS_BACKEND (watch->dcsb),
+                                     watch->name, NULL);
+
+  outstanding_watch_free (watch);
+}
+
+static gboolean
+dconf_settings_backend_subscribe_context_func (gpointer data)
+{
+  OutstandingWatch *watch = data;
+  DConfEngineMessage dcem;
+
+  dconf_engine_watch (watch->dcsb->engine, watch->name, &dcem);
+  dconf_settings_backend_send (watch->dcsb, &dcem, add_match_done, watch);
+  dconf_engine_message_destroy (&dcem);
+
+  return FALSE;
+}
+
 static void
 dconf_settings_backend_subscribe (GSettingsBackend *backend,
                                   const gchar      *name)
 {
   DConfSettingsBackend *dcsb = (DConfSettingsBackend *) backend;
-  DConfEngineMessage dcem;
-  GDBusConnection *bus;
-
-  dconf_engine_watch (dcsb->engine, name, &dcem);
 
-  if (dconf_settings_backend_get_bus (dcsb, &bus, &dcem))
-    dconf_settings_backend_send (bus, &dcem, NULL);
+  g_main_context_invoke (dconf_context_get (),
+                         dconf_settings_backend_subscribe_context_func,
+                         outstanding_watch_new (dcsb, name));
 }
 
 static void
@@ -524,12 +573,10 @@ dconf_settings_backend_unsubscribe (GSettingsBackend *backend,
 {
   DConfSettingsBackend *dcsb = (DConfSettingsBackend *) backend;
   DConfEngineMessage dcem;
-  GDBusConnection *bus;
 
   dconf_engine_unwatch (dcsb->engine, name, &dcem);
-
-  if (dconf_settings_backend_get_bus (dcsb, &bus, &dcem))
-    dconf_settings_backend_send (bus, &dcem, NULL);
+  dconf_settings_backend_send (dcsb, &dcem, NULL, NULL);
+  dconf_engine_message_destroy (&dcem);
 }
 
 static void
@@ -557,13 +604,13 @@ dconf_settings_backend_sync (GSettingsBackend *backend)
 static GVariant *
 dconf_settings_backend_service_func (DConfEngineMessage *dcem)
 {
-  g_assert (dcem->bus_type == 'e');
+  g_assert (dcem->bus_types[0] == 'e');
 
   return g_dbus_connection_call_sync (g_bus_get_sync (G_BUS_TYPE_SESSION,
                                                       NULL, NULL),
-                                      dcem->destination, dcem->object_path,
-                                      dcem->interface, dcem->method,
-                                      dcem->body, dcem->reply_type,
+                                      dcem->bus_name, dcem->object_path,
+                                      dcem->interface_name, dcem->method_name,
+                                      dcem->parameters[0], dcem->reply_type,
                                       0, -1, NULL, NULL);
 }
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]