private static const string _use_address_books =
"FOLKS_BACKEND_EDS_USE_ADDRESS_BOOKS";
private bool _is_prepared = false;
+ private bool _prepare_pending = false; /* used for unprepare() too */
private bool _is_quiescent = false;
private HashMap<string, PersonaStore> _persona_stores;
private Map<string, PersonaStore> _persona_stores_ro;
{
lock (this._is_prepared)
{
- if (!this._is_prepared)
+ if (this._is_prepared || this._prepare_pending)
{
+ return;
+ }
+
+ try
+ {
+ this._prepare_pending = true;
+
this._create_avatars_cache_dir ();
E.BookClient.get_sources (out this._ab_sources);
this._is_quiescent = true;
this.notify_property ("is-quiescent");
}
+ finally
+ {
+ this._prepare_pending = false;
+ }
}
}
{
lock (this._is_prepared)
{
- if (this._is_prepared)
+ if (!this._is_prepared || this._prepare_pending)
{
+ return;
+ }
+
+ try
+ {
+ this._prepare_pending = true;
+
foreach (var persona_store in this._persona_stores.values)
{
this._remove_address_book (persona_store);
this._is_prepared = false;
this.notify_property ("is-prepared");
}
+ finally
+ {
+ this._prepare_pending = false;
+ }
}
}
private HashMap<string, Persona> _personas;
private Map<string, Persona> _personas_ro;
private bool _is_prepared = false;
+ private bool _prepare_pending = false;
private bool _is_quiescent = false;
private E.BookClient _addressbook;
private E.BookClientView _ebookview;
/* FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=652637 */
lock (this._is_prepared)
{
- if (this._is_prepared)
+ if (this._is_prepared == true || this._prepare_pending == true)
{
return;
}
+ this._prepare_pending = true;
+
try
{
/* Listen for removal signals for the address book. There's no
* and the second is an error message. */
_("Couldn't open address book ‘%s’: %s"), this.id, e1.message);
}
+ finally
+ {
+ this._prepare_pending = false;
+ }
if (this._addressbook.is_opened () == false)
{
/* Remove the persona store on error */
this.removed ();
+ this._prepare_pending = false;
throw new PersonaStoreError.INVALID_ARGUMENT (
/* Translators: the parameter is an address book URI. */
/* Translators: the parameteter is an error message. */
_("Couldn't get address book capabilities: %s"), e2.message);
}
+ finally
+ {
+ this._prepare_pending = false;
+ }
/* Get the set of capabilities supported by the address book.
* Specifically, we're looking for do-initial-query, which signifies
/* Translators: the parameteter is an error message. */
_("Couldn't get address book capabilities: %s"), e4.message);
}
+ finally
+ {
+ this._prepare_pending = false;
+ }
bool got_view = false;
try
_("Couldn't get view for address book ‘%s’: %s"),
this.id, e3.message);
}
+ finally
+ {
+ this._prepare_pending = false;
+ }
this._is_prepared = true;
+ this._prepare_pending = false;
this.notify_property ("is-prepared");
/* If the address book isn't going to do an initial query (i.e.
public class Folks.Backends.Kf.Backend : Folks.Backend
{
private bool _is_prepared = false;
+ private bool _prepare_pending = false; /* used for unprepare() too */
private bool _is_quiescent = false;
private HashMap<string, PersonaStore> _persona_stores;
private Map<string, PersonaStore> _persona_stores_ro;
{
lock (this._is_prepared)
{
- if (!this._is_prepared)
+ if (this._is_prepared || this._prepare_pending)
{
+ return;
+ }
+
+ try
+ {
+ this._prepare_pending = true;
+
File file;
unowned string path = Environment.get_variable (
"FOLKS_BACKEND_KEY_FILE_PATH");
this._is_quiescent = true;
this.notify_property ("is-quiescent");
}
+ finally
+ {
+ this._prepare_pending = false;
+ }
}
}
*/
public override async void unprepare () throws GLib.Error
{
- foreach (var persona_store in this._persona_stores.values)
+ lock (this._is_prepared)
{
- this.persona_store_removed (persona_store);
- }
+ if (!this._is_prepared || this._prepare_pending == true)
+ {
+ return;
+ }
- this._persona_stores.clear ();
- this.notify_property ("persona-stores");
+ try
+ {
+ this._prepare_pending = true;
- this._is_quiescent = false;
- this.notify_property ("is-quiescent");
+ foreach (var persona_store in this._persona_stores.values)
+ {
+ this.persona_store_removed (persona_store);
+ }
+
+ this._persona_stores.clear ();
+ this.notify_property ("persona-stores");
+
+ this._is_quiescent = false;
+ this.notify_property ("is-quiescent");
- this._is_prepared = false;
- this.notify_property ("is-prepared");
+ this._is_prepared = false;
+ this.notify_property ("is-prepared");
+ }
+ finally
+ {
+ this._prepare_pending = false;
+ }
+ }
}
private void _store_removed_cb (Folks.PersonaStore store)
private GLib.KeyFile _key_file;
private unowned Cancellable _save_key_file_cancellable = null;
private bool _is_prepared = false;
+ private bool _prepare_pending = false;
private bool _is_quiescent = false;
private const string[] _always_writeable_properties =
{
lock (this._is_prepared)
{
- if (!this._is_prepared)
+ if (this._is_prepared || this._prepare_pending)
{
+ return;
+ }
+
+ try
+ {
+ this._prepare_pending = true;
+
var filename = this._file.get_path ();
this._key_file = new GLib.KeyFile ();
this._is_quiescent = true;
this.notify_property ("is-quiescent");
}
+ finally
+ {
+ this._prepare_pending = false;
+ }
}
}
private HashMap<string, Persona> _personas;
private Map<string, Persona> _personas_ro;
private bool _is_prepared = false;
+ private bool _prepare_pending = false;
private bool _is_quiescent = false;
private ClientService _service;
private ClientContactView _contact_view;
{
lock (this._is_prepared)
{
- if (!this._is_prepared)
+ if (!this._is_prepared && !this._prepare_pending)
{
+ this._prepare_pending = true;
+
/* Take a reference to the PersonaStore while waiting for the
* async call to return. See: bgo#665039. */
this.ref ();
if (caps == null)
{
this.unref ();
+ this._prepare_pending = false;
return;
}
if (!has_contacts)
{
this.unref ();
+ this._prepare_pending = false;
return;
}
if (contact_view == null)
{
this.unref ();
+ this._prepare_pending = false;
return;
}
this._contact_view = contact_view;
this._is_prepared = true;
+ this._prepare_pending = false;
this.notify_property ("is-prepared");
/* FIXME: for lsw Stores with 0 contacts or badly
public class Folks.Backends.Sw.Backend : Folks.Backend
{
private bool _is_prepared = false;
+ private bool _prepare_pending = false;
private bool _is_quiescent = false;
private Client _client;
private HashMap<string, PersonaStore> _persona_stores;
{
lock (this._is_prepared)
{
- if (!this._is_prepared)
+ if (!this._is_prepared && !this._prepare_pending)
{
+ this._prepare_pending = true;
+
/* Hold a ref. on the Backend while we wait for the callback from
* this._client.get_services() to prevent the Backend being
* destroyed in the mean time. See: bgo#665039. */
this.add_service (service_name);
this._is_prepared = true;
+ this._prepare_pending = false;
this.notify_property ("is-prepared");
this._is_quiescent = true;
*/
public override async void unprepare () throws GLib.Error
{
- foreach (var store in this._persona_stores.values)
+ lock (this._is_prepared)
{
- store.removed.disconnect (this.store_removed_cb);
- this.persona_store_removed (store);
- }
+ if (!this._is_prepared || this._prepare_pending)
+ {
+ return;
+ }
- this._client = null;
+ this._prepare_pending = true;
- this._persona_stores.clear ();
- this.notify_property ("persona-stores");
+ foreach (var store in this._persona_stores.values)
+ {
+ store.removed.disconnect (this.store_removed_cb);
+ this.persona_store_removed (store);
+ }
+
+ this._client = null;
- this._is_quiescent = false;
- this.notify_property ("is-quiescent");
+ this._persona_stores.clear ();
+ this.notify_property ("persona-stores");
- this._is_prepared = false;
- this.notify_property ("is-prepared");
+ this._is_quiescent = false;
+ this.notify_property ("is-quiescent");
+
+ this._is_prepared = false;
+ this._prepare_pending = false;
+ this.notify_property ("is-prepared");
+ }
}
private void add_service (string service_name)
{
lock (this._is_prepared)
{
- if (!this._is_prepared && !this._prepare_pending)
+ if (this._is_prepared || this._prepare_pending)
+ {
+ return;
+ }
+
+ try
{
this._prepare_pending = true;
}
this._is_prepared = true;
- this._prepare_pending = false;
this.notify_property ("is-prepared");
}
+ finally
+ {
+ this._prepare_pending = false;
+ }
}
}
{
private AccountManager _account_manager;
private bool _is_prepared = false;
+ private bool _prepare_pending = false; /* used by unprepare() too */
private bool _is_quiescent = false;
private HashMap<string, PersonaStore> _persona_stores;
private Map<string, PersonaStore> _persona_stores_ro;
{
lock (this._is_prepared)
{
- if (!this._is_prepared)
+ if (this._is_prepared || this._prepare_pending)
+ {
+ return;
+ }
+
+ try
{
this._account_manager = AccountManager.dup ();
yield this._account_manager.prepare_async (null);
this._is_quiescent = true;
this.notify_property ("is-quiescent");
}
+ finally
+ {
+ this._prepare_pending = false;
+ }
}
}
*/
public override async void unprepare () throws GLib.Error
{
- if (!this._is_prepared)
- return;
+ lock (this._is_prepared)
+ {
+ if (!this._is_prepared || this._prepare_pending)
+ {
+ return;
+ }
- this._account_manager.account_enabled.disconnect (
- this._account_enabled_cb);
- this._account_manager.account_validity_changed.disconnect (
- this._account_validity_changed_cb);
- this._account_manager = null;
+ try
+ {
+ this._account_manager.account_enabled.disconnect (
+ this._account_enabled_cb);
+ this._account_manager.account_validity_changed.disconnect (
+ this._account_validity_changed_cb);
+ this._account_manager = null;
- foreach (var persona_store in this._persona_stores.values)
- {
- this.persona_store_removed (persona_store);
- }
+ foreach (var persona_store in this._persona_stores.values)
+ {
+ this.persona_store_removed (persona_store);
+ }
- this._persona_stores.clear ();
- this.notify_property ("persona-stores");
+ this._persona_stores.clear ();
+ this.notify_property ("persona-stores");
- this._is_quiescent = false;
- this.notify_property ("is-quiescent");
+ this._is_quiescent = false;
+ this.notify_property ("is-quiescent");
- this._is_prepared = false;
- this.notify_property ("is-prepared");
+ this._is_prepared = false;
+ this.notify_property ("is-prepared");
+ }
+ finally
+ {
+ this._prepare_pending = false;
+ }
+ }
}
private void _account_validity_changed_cb (Account account, bool valid)
private HashMap<string, Persona> _personas;
private Map<string, Persona> _personas_ro;
private bool _is_prepared = false;
+ private bool _prepare_pending = false;
private bool _is_quiescent = false;
private static const int _default_timeout = 100;
private Resources _resources_object;
{
lock (this._is_prepared)
{
- if (!this._is_prepared)
+ if (this._is_prepared || this._prepare_pending)
{
+ return;
+ }
+
+ try
+ {
+ this._prepare_pending = true;
+
try
{
this._connection =
throw new PersonaStoreError.INVALID_ARGUMENT (e3.message);
}
}
+ finally
+ {
+ this._prepare_pending = false;
+ }
}
}
public class Folks.Backends.Tr.Backend : Folks.Backend
{
private bool _is_prepared = false;
+ private bool _prepare_pending = false; /* used by unprepare() too */
private bool _is_quiescent = false;
private HashMap<string, PersonaStore> _persona_stores;
private Map<string, PersonaStore> _persona_stores_ro;
{
lock (this._is_prepared)
{
- if (!this._is_prepared)
+ if (this._is_prepared || this._prepare_pending)
{
+ return;
+ }
+
+ try
+ {
+ this._prepare_pending = true;
+
this._add_default_persona_store ();
this._is_prepared = true;
this._is_quiescent = true;
this.notify_property ("is-quiescent");
}
+ finally
+ {
+ this._prepare_pending = false;
+ }
}
}
*/
public override async void unprepare () throws GLib.Error
{
- foreach (var persona_store in this._persona_stores.values)
+ lock (this._is_prepared)
{
- this.persona_store_removed (persona_store);
- }
+ if (!this._is_prepared || this._prepare_pending)
+ {
+ return;
+ }
- this._persona_stores.clear ();
- this.notify_property ("persona-stores");
+ try
+ {
+ this._prepare_pending = true;
+
+ foreach (var persona_store in this._persona_stores.values)
+ {
+ this.persona_store_removed (persona_store);
+ }
- this._is_quiescent = false;
- this.notify_property ("is-quiescent");
+ this._persona_stores.clear ();
+ this.notify_property ("persona-stores");
+
+ this._is_quiescent = false;
+ this.notify_property ("is-quiescent");
- this._is_prepared = false;
- this.notify_property ("is-prepared");
+ this._is_prepared = false;
+ this.notify_property ("is-prepared");
+ }
+ finally
+ {
+ this._prepare_pending = false;
+ }
+ }
}
/**
*
* This function is guaranteed to be idempotent (since version 0.3.0).
*
+ * Concurrent calls to this function from different threads will block until
+ * preparation has completed. However, concurrent calls to this function from
+ * a single thread might not, i.e. the first call will block but subsequent
+ * calls might return before the first one. (Though they will be safe in every
+ * other respect.)
+ *
* @since 0.1.11
*/
public abstract async void prepare () throws GLib.Error;
*
* If this function throws an error, the Backend will not be functional.
*
+ * Concurrent calls to this function from different threads will block until
+ * preparation has completed. However, concurrent calls to this function from
+ * a single thread might not, i.e. the first call will block but subsequent
+ * calls might return before the first one. (Though they will be safe in every
+ * other respect.)
+ *
* @since 0.3.2
*/
public abstract async void unprepare () throws GLib.Error;
*
* This function is guaranteed to be idempotent (since version 0.3.0).
*
+ * Concurrent calls to this function from different threads will block until
+ * preparation has completed. However, concurrent calls to this function from
+ * a single thread might not, i.e. the first call will block but subsequent
+ * calls might return before the first one. (Though they will be safe in every
+ * other respect.)
+ *
* @since 0.1.11
*/
public async void prepare () throws GLib.Error
lock (this._is_prepared)
{
- if (!this._is_prepared && !this._prepare_pending)
+ if (this._is_prepared || this._prepare_pending)
+ {
+ return;
+ }
+
+ try
{
this._prepare_pending = true;
+
yield this._backend_store.load_backends ();
+
this._is_prepared = true;
- this._prepare_pending = false;
this.notify_property ("is-prepared");
}
+ finally
+ {
+ this._prepare_pending = false;
+ }
}
}
*
* This function is guaranteed to be idempotent (since version 0.3.0).
*
+ * Concurrent calls to this function from different threads will block until
+ * preparation has completed. However, concurrent calls to this function from
+ * a single thread might not, i.e. the first call will block but subsequent
+ * calls might return before the first one. (Though they will be safe in every
+ * other respect.)
+ *
* @since 0.1.11
*/
public abstract async void prepare () throws GLib.Error;