diff options
author | Daniel Playfair Cal <daniel.playfair.cal@gmail.com> | 2017-12-12 00:18:18 +1100 |
---|---|---|
committer | Daniel Playfair Cal <daniel.playfair.cal@gmail.com> | 2020-12-19 12:39:35 +1100 |
commit | 5ed0724cd763cf79dcc8f6802a6aa093d635cac7 (patch) | |
tree | e74764e806eba70ab76d3fc69927758413e2b736 /engine | |
parent | 4f90c7927335712edd875cbea395c01fb48f748b (diff) | |
download | dconf-5ed0724cd763cf79dcc8f6802a6aa093d635cac7.tar.gz |
engine: remove spurious local change notifications
When used with the "fast" (optimistic concurrency) API, the engine library
emits a change notification local to a process after a change is initiated
from that process. Previously, it would emit this notification even if the
newly written value was the same as the previous value (according to that
process's view of the state). After this change, the local change
notification is not sent unless the new value is different from the
current value (as seen by that process).
Diffstat (limited to 'engine')
-rw-r--r-- | engine/dconf-engine.c | 84 |
1 files changed, 83 insertions, 1 deletions
diff --git a/engine/dconf-engine.c b/engine/dconf-engine.c index cbb2a00..6a1f247 100644 --- a/engine/dconf-engine.c +++ b/engine/dconf-engine.c @@ -24,6 +24,7 @@ #include "../common/dconf-enums.h" #include "../common/dconf-paths.h" +#include "../common/dconf-gvdb-utils.h" #include "../gvdb/gvdb-reader.h" #include <string.h> #include <stdlib.h> @@ -822,6 +823,54 @@ dconf_engine_list (DConfEngine *engine, return list; } +static gboolean +dconf_engine_dir_has_writable_contents (DConfEngine *engine, + const gchar *dir) +{ + DConfChangeset *database; + GHashTable *current_state; + + /* Read the on disk state */ + if (engine->n_sources == 0 || !engine->sources[0]->writable) + // If there are no writable sources, there won't be any pending writes either + return FALSE; + + dconf_engine_acquire_sources (engine); + database = dconf_gvdb_utils_changeset_from_table (engine->sources[0]->values); + dconf_engine_release_sources (engine); + + /* Apply pending and in_flight changes to the on disk state */ + dconf_engine_lock_queue (engine); + + if (engine->in_flight != NULL) + dconf_changeset_change (database, engine->in_flight); + + if (engine->pending != NULL) + { + /** + * We don't want to seal the pending changeset because it may still be changed, + * and sealing the changeset would be a side effect of passing engine->pending + * directly into dconf_changeset_change. + */ + DConfChangeset *changes = dconf_changeset_filter_changes (database, engine->pending); + if (changes != NULL) + { + dconf_changeset_change (database, changes); + dconf_changeset_unref (changes); + } + } + + dconf_engine_unlock_queue (engine); + + /* Check if there are writable contents at the given directory in the current state */ + current_state = dconf_gvdb_utils_table_from_changeset (database); + gboolean result = g_hash_table_contains (current_state, dir); + + g_hash_table_unref (current_state); + dconf_changeset_unref (database); + return result; +} + typedef void (* DConfEngineCallHandleCallback) (DConfEngine *engine, gpointer handle, GVariant *parameter, @@ -1129,6 +1178,34 @@ dconf_engine_prepare_change (DConfEngine *engine, */ static void dconf_engine_manage_queue (DConfEngine *engine); +/** + * a #DConfChangesetPredicate which determines whether the given path and + * value is already present in the given engine. "Already present" means + * that setting that path to that value would have no effect on the + * engine, including for directory resets. + */ +static gboolean +dconf_engine_path_has_value_predicate (const gchar *path, + GVariant *new_value, + gpointer user_data) +{ + DConfEngine *engine = user_data; + + // Path reset are handled specially + if (g_str_has_suffix (path, "/")) + return !dconf_engine_dir_has_writable_contents (engine, path); + + g_autoptr(GVariant) current_value = dconf_engine_read ( + engine, + DCONF_READ_USER_VALUE, + NULL, + path + ); + return ((current_value == NULL && new_value == NULL) || + (current_value != NULL && new_value != NULL && + g_variant_equal (current_value, new_value))); +} + static void dconf_engine_emit_changes (DConfEngine *engine, DConfChangeset *changeset, @@ -1274,6 +1351,10 @@ dconf_engine_change_fast (DConfEngine *engine, if (dconf_changeset_is_empty (changeset)) return TRUE; + gboolean has_no_effect = dconf_changeset_all (changeset, + dconf_engine_path_has_value_predicate, + engine); + if (!dconf_engine_changeset_changes_only_writable_keys (engine, changeset, error)) return FALSE; @@ -1298,7 +1379,8 @@ dconf_engine_change_fast (DConfEngine *engine, dconf_engine_unlock_queue (engine); /* Emit the signal after dropping the lock to avoid deadlock on re-entry. */ - dconf_engine_emit_changes (engine, changeset, origin_tag); + if (!has_no_effect) + dconf_engine_emit_changes (engine, changeset, origin_tag); return TRUE; } |