summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Bradford <rob@linux.intel.com>2011-06-16 11:59:48 +0100
committerRoss Burton <ross@linux.intel.com>2011-06-28 15:25:45 +0100
commitb2523117a166575572c501acbb5f8ff09922f337 (patch)
treeddbcd0be955fc9e03af71255e0b92164f501c2c7
parent0630a9a047bf235b3518a5a1c1ace4b938088c84 (diff)
downloadgconf-b2523117a166575572c501acbb5f8ff09922f337.tar.gz
gconf-client: Update the internal cache on operations to ensure consistency
-rw-r--r--gconf/gconf-client.c242
1 files changed, 235 insertions, 7 deletions
diff --git a/gconf/gconf-client.c b/gconf/gconf-client.c
index f1df54ac..796c5485 100644
--- a/gconf/gconf-client.c
+++ b/gconf/gconf-client.c
@@ -1036,6 +1036,128 @@ gconf_client_preload (GConfClient* client,
}
}
+#ifdef HAVE_DBUS
+
+/*
+ * Add functions for manipulating the internal cache of values - under the
+ * D-BUS environment we update the internal cache when changes happen to
+ * ensure a consistent state
+ */
+static gboolean
+remove_key_from_cache_foreach (const gchar *cached_key,
+ GConfEntry *entry,
+ const gchar *key)
+{
+ if (strcmp (cached_key, key) == 0)
+ {
+ gconf_entry_free (entry);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+remove_key_from_cache_recursively_foreach (const gchar *cached_key,
+ GConfEntry *entry,
+ const gchar *key)
+{
+ if (gconf_key_is_below (cached_key, key) == 0 || strcmp (cached_key, key) == 0)
+ {
+ /* Not sure why a null entry would be in the cache, but it happens
+ * sometimes, workaround for now to avoid crashing.
+ */
+ if (entry)
+ gconf_entry_free (entry);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+remove_dir_from_cache_foreach (char *cached_dir,
+ gpointer value,
+ char *dir)
+{
+ if (strcmp (dir, cached_dir) == 0)
+ {
+ trace ("'%s' no longer fully cached", dir);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* The dbus version cleans the cache after modifying a value. So we will
+ * remove current dir (where the key is in) from the cache_dirs
+ * when removing the key from the cache_hash.
+ * This is a workaround. It will degrade the performance of querying negative keys in the same dir.
+ */
+static void remove_dir_from_cache (GConfClient *client,
+ const gchar *key)
+{
+ char *dir, *last_slash;
+ dir = g_strdup (key);
+ last_slash = strrchr (dir, '/');
+ g_assert (last_slash != NULL);
+ *last_slash = 0;
+ trace ("Remove dir '%s' from cache since one of keys is changed", dir);
+ g_hash_table_foreach_remove (client->cache_dirs,
+ (GHRFunc) remove_dir_from_cache_foreach,
+ dir);
+ g_free (dir);
+}
+
+static void
+remove_key_from_cache (GConfClient *client,
+ const gchar *key)
+{
+ g_hash_table_foreach_remove (client->cache_hash,
+ (GHRFunc) remove_key_from_cache_foreach,
+ (char *) key);
+ remove_dir_from_cache (client, key);
+}
+
+static void
+remove_key_from_cache_recursively (GConfClient *client,
+ const gchar *key)
+{
+ g_hash_table_foreach_remove (client->cache_hash,
+ (GHRFunc) remove_key_from_cache_recursively_foreach,
+ (char *) key);
+ remove_dir_from_cache (client, key);
+}
+
+static gboolean
+cache_key_value (GConfClient *client,
+ const gchar *key,
+ const GConfValue *val)
+{
+ GConfEntry *entry;
+
+ entry = gconf_entry_new (key, val);
+ return gconf_client_cache (client, TRUE, entry, TRUE);
+}
+
+static void
+cache_key_value_and_notify (GConfClient *client,
+ const gchar *key,
+ GConfValue *val,
+ gboolean free_value)
+{
+ if (cache_key_value (client, key, val))
+ {
+ if (key_being_monitored (client, key))
+ gconf_client_queue_notify (client, key);
+ }
+
+ if (free_value)
+ gconf_value_free (val);
+}
+
+#endif
+
/*
* Basic key-manipulation facilities
*/
@@ -1052,6 +1174,11 @@ gconf_client_set (GConfClient* client,
PUSH_USE_ENGINE (client);
gconf_engine_set (client->engine, key, val, &error);
POP_USE_ENGINE (client);
+
+#ifdef HAVE_DBUS
+ if (!error)
+ cache_key_value_and_notify (client, key, (GConfValue *) val, FALSE);
+#endif
handle_error(client, error, err);
}
@@ -1066,7 +1193,12 @@ gconf_client_unset (GConfClient* client,
PUSH_USE_ENGINE (client);
gconf_engine_unset(client->engine, key, &error);
POP_USE_ENGINE (client);
-
+
+#ifdef HAVE_DBUS
+ if (!error)
+ remove_key_from_cache (client, key);
+#endif
+
handle_error(client, error, err);
if (error != NULL)
@@ -1088,7 +1220,12 @@ gconf_client_recursive_unset (GConfClient *client,
PUSH_USE_ENGINE (client);
gconf_engine_recursive_unset(client->engine, key, flags, &error);
POP_USE_ENGINE (client);
-
+
+#ifdef HAVE_DBUS
+ if (!error)
+ remove_key_from_cache_recursively (client, key);
+#endif
+
handle_error(client, error, err);
if (error != NULL)
@@ -1836,6 +1973,9 @@ gconf_client_get_pair (GConfClient* client, const gchar* key,
}
}
+#ifdef HAVE_DBUS
+#define MAKE_VALUE(v, TYPE, type, val) GConfValue *v = gconf_value_new (GCONF_VALUE_##TYPE); gconf_value_set_##type (v, val)
+#endif
/*
* For the set functions, we just set normally, and wait for the
@@ -1865,6 +2005,14 @@ gconf_client_set_float (GConfClient* client, const gchar* key,
result = gconf_engine_set_float (client->engine, key, val, &error);
POP_USE_ENGINE (client);
+#ifdef HAVE_DBUS
+ if (result)
+ {
+ MAKE_VALUE (v, FLOAT, float, val);
+ cache_key_value_and_notify (client, key, v, TRUE);
+ }
+#endif
+
if (result)
return TRUE;
else
@@ -1889,7 +2037,15 @@ gconf_client_set_int (GConfClient* client, const gchar* key,
PUSH_USE_ENGINE (client);
result = gconf_engine_set_int (client->engine, key, val, &error);
POP_USE_ENGINE (client);
-
+
+#ifdef HAVE_DBUS
+ if (result)
+ {
+ MAKE_VALUE (v, INT, int, val);
+ cache_key_value_and_notify (client, key, v, TRUE);
+ }
+#endif
+
if (result)
return TRUE;
else
@@ -1915,7 +2071,15 @@ gconf_client_set_string (GConfClient* client, const gchar* key,
PUSH_USE_ENGINE (client);
result = gconf_engine_set_string(client->engine, key, val, &error);
POP_USE_ENGINE (client);
-
+
+#ifdef HAVE_DBUS
+ if (result)
+ {
+ MAKE_VALUE (v, STRING, string, val);
+ cache_key_value_and_notify (client, key, v, TRUE);
+ }
+#endif
+
if (result)
return TRUE;
else
@@ -1941,6 +2105,14 @@ gconf_client_set_bool (GConfClient* client, const gchar* key,
result = gconf_engine_set_bool (client->engine, key, val, &error);
POP_USE_ENGINE (client);
+#ifdef HAVE_DBUS
+ if (result)
+ {
+ MAKE_VALUE (v, BOOL, bool, val);
+ cache_key_value_and_notify (client, key, v, TRUE);
+ }
+#endif
+
if (result)
return TRUE;
else
@@ -1966,7 +2138,15 @@ gconf_client_set_schema (GConfClient* client, const gchar* key,
PUSH_USE_ENGINE (client);
result = gconf_engine_set_schema(client->engine, key, val, &error);
POP_USE_ENGINE (client);
-
+
+#ifdef HAVE_DBUS
+ if (result)
+ {
+ MAKE_VALUE (v, SCHEMA, schema, val);
+ cache_key_value_and_notify (client, key, v, TRUE);
+ }
+#endif
+
if (result)
return TRUE;
else
@@ -1994,6 +2174,14 @@ gconf_client_set_list (GConfClient* client, const gchar* key,
result = gconf_engine_set_list(client->engine, key, list_type, list, &error);
POP_USE_ENGINE (client);
+#ifdef HAVE_DBUS
+ if (result)
+ {
+ GConfValue *v = gconf_value_list_from_primitive_list (list_type, list, err);
+ cache_key_value_and_notify (client, key, v, TRUE);
+ }
+#endif
+
if (result)
return TRUE;
else
@@ -2023,6 +2211,20 @@ gconf_client_set_pair (GConfClient* client, const gchar* key,
address_of_car, address_of_cdr, &error);
POP_USE_ENGINE (client);
+#ifdef HAVE_DBUS
+ if (result)
+ {
+ GConfValue *v = gconf_value_pair_from_primitive_pair (car_type, cdr_type, address_of_car, address_of_cdr, &error);
+ if (!v)
+ {
+ handle_error (client, error, err);
+ return FALSE;
+ }
+
+ cache_key_value_and_notify (client, key, v, TRUE);
+ }
+#endif
+
if (result)
return TRUE;
else
@@ -2654,8 +2856,34 @@ gconf_client_flush_notifies (GConfClient *client)
}
else
{
- trace ("Key '%s' was in notify queue but not in cache; we must have stopped monitoring it; not notifying",
- tmp->data);
+#ifdef HAVE_DBUS
+ /* The dbus version cleans the cache after modifying a value so a get
+ * directly after a set doesn't return a stale value. That means we
+ * have to check if the key is supposed to be monitored here, we can't
+ * just rely on it being in the cache.
+ */
+ if (key_being_monitored (client, tmp->data))
+ {
+ trace ("Key %s was in notify queue but not in cache, but is being monitored",
+ tmp->data);
+
+ entry = gconf_client_get_entry (client, tmp->data, NULL, TRUE, NULL);
+ if (entry != NULL)
+ {
+ notify_one_entry (client, entry);
+ gconf_entry_unref (entry);
+ last_entry = NULL;
+ }
+ }
+ else
+ {
+ trace ("Key '%s' was in notify queue but not in cache; we must have stopped monitoring it; not notifying",
+ tmp->data);
+ }
+#else
+ trace ("Key '%s' was in notify queue but not in cache; we must have stopped monitoring it; not notifying",
+ tmp->data);
+#endif
}
tmp = tmp->next;