diff options
author | Philip Withnall <philip.withnall@collabora.co.uk> | 2010-09-09 16:07:08 +0100 |
---|---|---|
committer | Philip Withnall <philip.withnall@collabora.co.uk> | 2010-09-10 14:01:06 +0100 |
commit | 964ed5760f9dabe6562cca7bdc2f564ee7a8a8a1 (patch) | |
tree | a38371598ec398e34bcd5cb52e22e2036ae53d44 | |
parent | 88df560e88cf14e3fc6b0b6060173dd94e3ecaed (diff) |
Block flushing of Kf.PersonaStore on any pending file operations
If a file operation is still underway, don't allow the Kf.PersonaStore to be
finalized until it's finished. This prevents libfolks from being closed before
changes to relationships.ini have been written out. We also prevent multiple
file operations from happening (pseudo-) concurrently, by cancelling any
pending operation when we schedule a new one.
-rw-r--r-- | backends/key-file/kf-persona-store.vala | 40 | ||||
-rw-r--r-- | folks/persona-store.vala | 17 |
2 files changed, 54 insertions, 3 deletions
diff --git a/backends/key-file/kf-persona-store.vala b/backends/key-file/kf-persona-store.vala index 3a81bdd..153810e 100644 --- a/backends/key-file/kf-persona-store.vala +++ b/backends/key-file/kf-persona-store.vala @@ -33,6 +33,7 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore private File file; private GLib.KeyFile key_file; private uint first_unused_id = 0; + private unowned Cancellable save_key_file_cancellable = null; /** * {@inheritDoc} @@ -173,6 +174,21 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore /** * {@inheritDoc} */ + public override async void flush () + { + /* If there are any ongoing file operations, wait for them to finish + * before returning. We have to iterate the main context manually to + * achieve this, as all the code in this file is run in the main loop (in + * the main thread). We would cause a deadlock if we used anything as + * fancy/useful as a GCond. */ + MainContext context = MainContext.default (); + while (this.save_key_file_cancellable != null) + context.iteration (true); + } + + /** + * {@inheritDoc} + */ public override async void remove_persona (Folks.Persona persona) { debug ("Removing Persona '%s' (IID '%s', group '%s')", persona.uid, @@ -242,20 +258,38 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore internal async void save_key_file () { string key_file_data = this.key_file.to_data (); + Cancellable cancellable = new Cancellable (); debug ("Saving key file '%s'.", this.file.get_path ()); + /* There's no point in having two competing file write operations. + * We can ensure that only one is running by just checking if a + * cancellable is set. This is thread safe because the code in this file + * is all run in the main thread (inside the main loop), so only we touch + * this.save_key_file_cancellable (albeit in many weird and wonderful + * orders due to idle handler queuing). */ + if (this.save_key_file_cancellable != null) + this.save_key_file_cancellable.cancel (); + this.save_key_file_cancellable = cancellable; + try { /* Note: We have to use key_file_data.size () here to get its length * in _bytes_ rather than _characters_. bgo#628930 */ yield this.file.replace_contents_async (key_file_data, - key_file_data.size (), null, false, FileCreateFlags.PRIVATE); + key_file_data.size (), null, false, FileCreateFlags.PRIVATE, + cancellable); } catch (Error e) { - warning ("Could not write updated key file '%s': %s", - this.file.get_path (), e.message); + if (!(e is IOError.CANCELLED)) + { + warning ("Could not write updated key file '%s': %s", + this.file.get_path (), e.message); + } } + + if (this.save_key_file_cancellable == cancellable) + this.save_key_file_cancellable = null; } } diff --git a/folks/persona-store.vala b/folks/persona-store.vala index 48dbf39..0d1a10a 100644 --- a/folks/persona-store.vala +++ b/folks/persona-store.vala @@ -194,6 +194,23 @@ public abstract class Folks.PersonaStore : Object public abstract async void prepare () throws GLib.Error; /** + * Flush any pending changes to the PersonaStore's backing store. + * + * PersonaStores may (transparently) implement caching or I/O queueing which + * means that changes to their {@link Persona}s may not be immediately written + * to the PersonaStore's backing store. Calling this function will force all + * pending changes to be flushed to the backing store. + * + * This must not be called before {@link PersonaStore.prepare}. + * + * @since 0.1.17 + */ + public virtual async void flush () + { + /* Default implementation doesn't have to do anything */ + } + + /** * Add a new {@link Persona} to the PersonaStore. * * The {@link Persona} will be created by the PersonaStore backend from the |