[dconf/dbus1] dbus-1: add support for watching keys
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [dconf/dbus1] dbus-1: add support for watching keys
- Date: Sun, 2 Jan 2011 19:06:07 +0000 (UTC)
commit 5ee7a71d40a98b46233b218d22d07fe850a02aef
Author: Ryan Lortie <desrt desrt ca>
Date: Sun Jan 2 14:05:38 2011 -0500
dbus-1: add support for watching keys
Add code to the test case to ensure that it's working.
dbus-1/dconf-dbus-1.c | 216 ++++++++++++++++++++++++++++++++-----------------
dbus-1/dconf-dbus-1.h | 17 +++-
tests/dbus1.c | 56 ++++---------
3 files changed, 168 insertions(+), 121 deletions(-)
---
diff --git a/dbus-1/dconf-dbus-1.c b/dbus-1/dconf-dbus-1.c
index c3a75bc..7aafc05 100644
--- a/dbus-1/dconf-dbus-1.c
+++ b/dbus-1/dconf-dbus-1.c
@@ -29,6 +29,7 @@ struct _DConfDBusClient
{
DBusConnection *session_bus;
DBusConnection *system_bus;
+ GSList *watches;
guint session_filter;
guint system_filter;
gint ref_count;
@@ -162,48 +163,6 @@ dconf_dbus_client_add_value_to_iter (DBusMessageIter *iter,
}
}
-#if 0
-static void
-dconf_settings_backend_signal (DBusConnection *connection,
- const gchar *sender_name,
- const gchar *object_path,
- const gchar *interface_name,
- const gchar *signal_name,
- GVariant *parameters,
- gpointer user_data)
-{
- DConfDBusClient *dcdbc = user_data;
- const gchar *anti_expose;
- const gchar **rels;
- const gchar *path;
- gchar bus_type;
-
- if (connection == dcdbc->session_bus)
- {
- anti_expose = dcdbc->anti_expose_tag;
- bus_type = 'e';
- }
-
- else if (connection == dcdbc->system_bus)
- {
- anti_expose = NULL;
- bus_type = 'y';
- }
-
- else
- g_assert_not_reached ();
-
- if (dconf_engine_decode_notify (dcdbc->engine, anti_expose,
- &path, &rels, bus_type,
- sender_name, interface_name,
- signal_name, parameters))
- {
- /** XXX EMIT CHANGES XXX **/
- g_free (rels);
- }
-}
-#endif
-
static void
dconf_settings_backend_send (DConfDBusClient *dcdbc,
DConfEngineMessage *dcem,
@@ -460,6 +419,43 @@ dconf_settings_backend_scan_outstanding (DConfDBusClient *backend,
return found;
}
+/* Watches are reference counted because they can be held both by the
+ * list of watches and by the pending watch registration. In the normal
+ * case, the registration completes before the watch is unsubscribed
+ * from but it might be the case that the watch is unsubscribed from
+ * before the AddMatch completes. For that reason, either thing could
+ * be responsible for freeing the watch structure; we solve that
+ * ambiguity using a reference count.
+ *
+ * We just initially set it to 2, since these are the only two users.
+ * That way we can skip having the ref() function.
+ */
+typedef struct
+{
+ DConfDBusClient *dcdbc;
+ gchar *name;
+ DConfDBusNotify notify;
+ gpointer user_data;
+ guint64 initial_state;
+ gint ref_count;
+} Watch;
+
+
+
+static void
+dconf_dbus_emit_change (DConfDBusClient *dcdbc,
+ const gchar *key)
+{
+ GSList *iter;
+
+ for (iter = dcdbc->watches; iter; iter = iter->next)
+ {
+ Watch *watch = iter->data;
+
+ if (g_str_has_prefix (key, watch->name))
+ watch->notify (dcdbc, key, watch->user_data);
+ }
+}
GVariant *
dconf_dbus_client_read (DConfDBusClient *dcdbc,
@@ -484,46 +480,49 @@ dconf_dbus_client_write (DConfDBusClient *dcdbc,
return FALSE;
dconf_settings_backend_queue (dcdbc, &dcem, key, value, NULL);
- /* XXX emit */
+ dconf_dbus_emit_change (dcdbc, key);
return TRUE;
}
-typedef struct
-{
- DConfDBusClient *dcdbc;
- guint64 state;
- gchar name[1];
-} OutstandingWatch;
-
-static OutstandingWatch *
-outstanding_watch_new (DConfDBusClient *dcdbc,
- const gchar *name)
+static Watch *
+watch_new (DConfDBusClient *dcdbc,
+ const gchar *name,
+ DConfDBusNotify notify,
+ gpointer user_data)
{
- OutstandingWatch *watch;
- gsize length;
+ Watch *watch;
- length = strlen (name);
- watch = g_malloc (G_STRUCT_OFFSET (OutstandingWatch, name) + length + 1);
+ watch = g_slice_new (Watch);
watch->dcdbc = dconf_dbus_client_ref (dcdbc);
- watch->state = dconf_engine_get_state (dcdbc->engine);
- strcpy (watch->name, name);
+ watch->user_data = user_data;
+ watch->name = g_strdup (name);
+ watch->notify = notify;
+
+ watch->initial_state = dconf_engine_get_state (dcdbc->engine);
+ watch->ref_count = 2;
+
+ dcdbc->watches = g_slist_prepend (dcdbc->watches, watch);
return watch;
}
static void
-outstanding_watch_free (OutstandingWatch *watch)
+watch_unref (Watch *watch)
{
- dconf_dbus_client_unref (watch->dcdbc);
- g_free (watch);
+ if (--watch->ref_count == 0)
+ {
+ dconf_dbus_client_unref (watch->dcdbc);
+ g_free (watch->name);
+ g_slice_free (Watch, watch);
+ }
}
static void
add_match_done (DBusPendingCall *pending,
gpointer user_data)
{
- OutstandingWatch *watch = user_data;
+ Watch *watch = user_data;
GError *error = NULL;
GVariant *reply;
@@ -531,10 +530,16 @@ add_match_done (DBusPendingCall *pending,
if (reply == NULL)
{
+ /* This is extremely unlikely to happen and it happens
+ * asynchronous to the user's call. Since the user doesn't know
+ * that it happened, we pretend that it didn't (ie: we leave the
+ * watch structure in the list).
+ */
+
g_critical ("DBus AddMatch for dconf path '%s': %s",
watch->name, error->message);
- outstanding_watch_free (watch);
g_error_free (error);
+ watch_unref (watch);
return;
}
@@ -542,27 +547,29 @@ add_match_done (DBusPendingCall *pending,
else
g_variant_unref (reply); /* it is just an empty tuple */
- /* In the normal case we can just free everything and be done.
+ /* In the normal case we're 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->dcdbc->engine) != watch->state)
- /* XXX emit watch->name */
+ if (dconf_engine_get_state (watch->dcdbc->engine) != watch->initial_state)
+ watch->notify (watch->dcdbc, watch->name, watch->user_data);
- outstanding_watch_free (watch);
+ watch_unref (watch);
}
void
dconf_dbus_client_subscribe (DConfDBusClient *dcdbc,
- const gchar *name)
+ const gchar *name,
+ DConfDBusNotify notify,
+ gpointer user_data)
{
- OutstandingWatch *watch;
DConfEngineMessage dcem;
+ Watch *watch;
- watch = outstanding_watch_new (dcdbc, name);
+ watch = watch_new (dcdbc, name, notify, user_data);
dconf_engine_watch (dcdbc->engine, name, &dcem);
dconf_settings_backend_send (dcdbc, &dcem, add_match_done, watch);
dconf_engine_message_destroy (&dcem);
@@ -570,13 +577,30 @@ dconf_dbus_client_subscribe (DConfDBusClient *dcdbc,
void
dconf_dbus_client_unsubscribe (DConfDBusClient *dcdbc,
- const gchar *name)
+ DConfDBusNotify notify,
+ gpointer user_data)
{
DConfEngineMessage dcem;
+ GSList **ptr;
- dconf_engine_unwatch (dcdbc->engine, name, &dcem);
- dconf_settings_backend_send (dcdbc, &dcem, NULL, NULL);
- dconf_engine_message_destroy (&dcem);
+ for (ptr = &dcdbc->watches; *ptr; ptr = &(*ptr)->next)
+ {
+ Watch *watch = (*ptr)->data;
+
+ if (watch->notify == notify && watch->user_data == user_data)
+ {
+ *ptr = g_slist_remove (*ptr, *ptr);
+
+ dconf_engine_unwatch (dcdbc->engine, watch->name, &dcem);
+ dconf_settings_backend_send (dcdbc, &dcem, NULL, NULL);
+ dconf_engine_message_destroy (&dcem);
+ watch_unref (watch);
+
+ return;
+ }
+ }
+
+ g_warning ("No matching watch found to unsubscribe");
}
gboolean
@@ -591,6 +615,41 @@ dconf_settings_backend_service_func (DConfEngineMessage *dcem)
g_assert_not_reached ();
}
+static DBusHandlerResult
+dconf_dbus_client_filter (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ DConfDBusClient *dcdbc = user_data;
+
+ if (dbus_message_is_signal (message, "ca.desrt.dconf.Writer", "Notify") &&
+ dbus_message_has_signature (message, "sass"))
+ {
+ DBusMessageIter iter, sub;
+ const gchar *path, *tag;
+
+ dbus_message_iter_init (message, &iter);
+ dbus_message_iter_get_basic (&iter, &path);
+ dbus_message_iter_recurse (&iter, &sub);
+ dbus_message_iter_get_basic (&iter, &tag);
+
+ /* XXX todo: anti-expose */
+
+ while (dbus_message_iter_get_arg_type (&sub) == 's')
+ {
+ const gchar *item;
+ gchar *full;
+
+ dbus_message_iter_get_basic (&iter, &item);
+ full = g_strconcat (path, item, NULL);
+ dconf_dbus_emit_change (dcdbc, full);
+ g_free (full);
+ }
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
DConfDBusClient *
dconf_dbus_client_new (const gchar *profile,
DBusConnection *system,
@@ -606,6 +665,9 @@ dconf_dbus_client_new (const gchar *profile,
dcdbc->session_bus = dbus_connection_ref (session);
dcdbc->ref_count = 1;
+ dbus_connection_add_filter (system, dconf_dbus_client_filter, dcdbc, NULL);
+ dbus_connection_add_filter (session, dconf_dbus_client_filter, dcdbc, NULL);
+
return dcdbc;
}
@@ -614,6 +676,10 @@ dconf_dbus_client_unref (DConfDBusClient *dcdbc)
{
if (--dcdbc->ref_count == 0)
{
+ dbus_connection_remove_filter (dcdbc->session_bus,
+ dconf_dbus_client_filter, dcdbc);
+ dbus_connection_remove_filter (dcdbc->system_bus,
+ dconf_dbus_client_filter, dcdbc);
dbus_connection_unref (dcdbc->session_bus);
dbus_connection_unref (dcdbc->system_bus);
diff --git a/dbus-1/dconf-dbus-1.h b/dbus-1/dconf-dbus-1.h
index d137fc8..649f24d 100644
--- a/dbus-1/dconf-dbus-1.h
+++ b/dbus-1/dconf-dbus-1.h
@@ -25,9 +25,13 @@
typedef struct _DConfDBusClient DConfDBusClient;
-DConfDBusClient * dconf_dbus_client_new (const gchar *profile,
- DBusConnection *system,
- DBusConnection *session);
+typedef void (* DConfDBusNotify) (DConfDBusClient *dcdbc,
+ const gchar *key,
+ gpointer user_data);
+
+DConfDBusClient * dconf_dbus_client_new (const gchar *profile,
+ DBusConnection *system,
+ DBusConnection *session);
void dconf_dbus_client_unref (DConfDBusClient *dcdbc);
DConfDBusClient * dconf_dbus_client_ref (DConfDBusClient *dcdbc);
@@ -37,9 +41,12 @@ gboolean dconf_dbus_client_write (DConfDB
const gchar *key,
GVariant *value);
void dconf_dbus_client_subscribe (DConfDBusClient *dcdbc,
- const gchar *name);
+ const gchar *name,
+ DConfDBusNotify notify,
+ gpointer user_data);
void dconf_dbus_client_unsubscribe (DConfDBusClient *dcdbc,
- const gchar *name);
+ DConfDBusNotify notify,
+ gpointer user_data);
gboolean dconf_dbus_client_has_pending (DConfDBusClient *dcdbc);
#endif /* _dconf_dbus_1_h_ */
diff --git a/tests/dbus1.c b/tests/dbus1.c
index 497d26a..ea54182 100644
--- a/tests/dbus1.c
+++ b/tests/dbus1.c
@@ -54,7 +54,7 @@ do_write_tree (GTree *tree)
static void
do_sync (void)
{
- g_assert_not_reached ();
+/* g_assert_not_reached (); */
}
#define RANDOM_ELEMENT(array) \
@@ -156,53 +156,22 @@ apply_change_tree (GHashTable *table,
static GHashTable *implicit;
static GHashTable *explicit;
-#if 0
-/* interpose */
-void
-g_settings_backend_changed (GSettingsBackend *backend_,
- const gchar *key,
- gpointer origin_tag)
+static void
+watch_func (DConfDBusClient *client,
+ const gchar *key,
+ gpointer user_data)
{
GVariant *value;
/* ensure that we see no dupes from the bus */
- g_assert (origin_tag == do_write);
- g_assert (backend == backend_);
+/* g_assert (origin_tag == do_write); */
+ g_assert (client == backend);
value = do_read (key);
apply_change (implicit, key, value);
g_variant_unref (value);
}
-/* interpose */
-void
-g_settings_backend_keys_changed (GSettingsBackend *backend_,
- const gchar *path,
- const gchar * const *items,
- gpointer origin_tag)
-{
- gint i;
-
- /* ensure that we see no dupes from the bus */
- g_assert (origin_tag == do_write);
- g_assert (backend == backend_);
-
- for (i = 0; items[i]; i++)
- {
- GVariant *value;
- gchar *key;
-
- key = g_strconcat (path, items[i], NULL);
- value = do_read (key);
-
- apply_change (implicit, key, value);
-
- g_variant_unref (value);
- g_free (key);
- }
-}
-#endif
-
static void
setup (void)
{
@@ -218,7 +187,7 @@ setup (void)
backend = dconf_dbus_client_new ("test",
dbus_bus_get (DBUS_BUS_SYSTEM, 0),
dbus_bus_get (DBUS_BUS_SESSION, 0));
- dconf_dbus_client_subscribe (backend, "/");
+ dconf_dbus_client_subscribe (backend, "/", watch_func, NULL);
implicit = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, free_variant);
@@ -271,8 +240,7 @@ verify_consistency (void)
else
g_print ("(%d)", g_hash_table_size (explicit));
- /*g_assert (g_hash_table_size (explicit) == g_hash_table_size
- * (implicit)); */
+ g_assert (g_hash_table_size (explicit) == g_hash_table_size (implicit));
g_hash_table_iter_init (&iter, explicit);
while (g_hash_table_iter_next (&iter, &key, &value))
{
@@ -280,6 +248,12 @@ verify_consistency (void)
{
GVariant *other;
+ ghash_time -= g_get_monotonic_time ();
+ other = g_hash_table_lookup (implicit, key);
+ ghash_time += g_get_monotonic_time ();
+ g_assert (g_variant_equal (value, other));
+
+
dconf_time -= g_get_monotonic_time ();
other = do_read (key);
dconf_time += g_get_monotonic_time ();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]