[dconf] Reduce GDBus abuse
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [dconf] Reduce GDBus abuse
- Date: Fri, 17 Dec 2010 20:17:12 +0000 (UTC)
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]