[folks] documentation: Document which yielding methods are safe to call concurrently
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [folks] documentation: Document which yielding methods are safe to call concurrently
- Date: Sun, 30 Dec 2012 00:53:11 +0000 (UTC)
commit e21932e8d233f68e0b065882eb06b15089a2935a
Author: Philip Withnall <philip tecnocode co uk>
Date: Sun Dec 30 00:48:38 2012 +0000
documentation: Document which yielding methods are safe to call concurrently
This is a follow-up commit to ce55fa2bf2f5f8cf95532da585d835bafeeb3347.
I went through all methods in folks which yield to another async method,
and tried to work out whether the caller was safe to run multiple times
concurrently (e.g. begin a second asynchronous call to it between a previous
async call beginning and finishing).
Iâve marked all such methods as safe (or not safe) as appropriate. I
havenât made any attempt to make the unsafe methods safe, except in one
case in backend-store.vala.
backends/eds/lib/edsf-persona-store.vala | 4 +-
backends/key-file/kf-persona-store.vala | 2 +
backends/libsocialweb/lib/swf-persona-store.vala | 4 ++
backends/telepathy/lib/tpf-persona-store.vala | 13 +++++
backends/tracker/lib/trf-persona-store.vala | 58 +++++++++++++++++++++-
folks/anti-linkable.vala | 6 ++
folks/avatar-cache.vala | 8 +++
folks/backend-store.vala | 32 +++++++++++-
folks/individual-aggregator.vala | 21 ++++++++
folks/object-cache.vala | 5 ++
10 files changed, 149 insertions(+), 4 deletions(-)
---
diff --git a/backends/eds/lib/edsf-persona-store.vala b/backends/eds/lib/edsf-persona-store.vala
index ee21d06..b943085 100644
--- a/backends/eds/lib/edsf-persona-store.vala
+++ b/backends/eds/lib/edsf-persona-store.vala
@@ -1033,7 +1033,9 @@ public class Edsf.PersonaStore : Folks.PersonaStore
SourceFunc? _open_address_book_callback = null; /* non-null iff yielded */
/* Guarantees that either the address book will be open once the method
- * returns, or an error will be thrown. */
+ * returns, or an error will be thrown.
+ *
+ * This method is not safe to run multiple times concurrently. */
private async void _open_address_book () throws GLib.Error
{
Error? err_out = null;
diff --git a/backends/key-file/kf-persona-store.vala b/backends/key-file/kf-persona-store.vala
index c989ae3..cfe1061 100644
--- a/backends/key-file/kf-persona-store.vala
+++ b/backends/key-file/kf-persona-store.vala
@@ -436,6 +436,8 @@ public class Folks.Backends.Kf.PersonaStore : Folks.PersonaStore
return this._key_file;
}
+ /* This is safe to call multiple times concurrently (in the same thread).
+ * Previous calls will be cancelled when a new call begins. */
internal async void save_key_file ()
{
var key_file_data = this._key_file.to_data ();
diff --git a/backends/libsocialweb/lib/swf-persona-store.vala b/backends/libsocialweb/lib/swf-persona-store.vala
index f23f414..40e8f85 100644
--- a/backends/libsocialweb/lib/swf-persona-store.vala
+++ b/backends/libsocialweb/lib/swf-persona-store.vala
@@ -222,6 +222,8 @@ public class Swf.PersonaStore : Folks.PersonaStore
"Personas cannot be removed from this store.");
}
+ /* This is safe to call multiple times concurrently (assuming libsocialweb
+ * itself is safe). */
private async string[]? _get_static_capabilities () throws GLib.Error
{
/* Take a reference to the PersonaStore while waiting for the async call
@@ -267,6 +269,8 @@ public class Swf.PersonaStore : Folks.PersonaStore
return caps;
}
+ /* This is safe to call multiple times concurrently (assuming libsocialweb
+ * itself is safe). */
private async ClientContactView? _contacts_query_open_view (string query,
HashTable<weak string, weak string> parameters)
{
diff --git a/backends/telepathy/lib/tpf-persona-store.vala b/backends/telepathy/lib/tpf-persona-store.vala
index 1dfbfcf..0c6c5d1 100644
--- a/backends/telepathy/lib/tpf-persona-store.vala
+++ b/backends/telepathy/lib/tpf-persona-store.vala
@@ -619,6 +619,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
this._logger = null;
}
+ /* This method is not safe to call multiple times concurrently. */
private async void _initialise_favourite_contacts () throws GLib.Error
{
if (this._logger == null)
@@ -898,6 +899,9 @@ public class Tpf.PersonaStore : Folks.PersonaStore
/**
* If our account is disconnected, we want to continue to export a static
* view of personas from the cache. old_personas will be notified as removed.
+ *
+ * This method is safe to call multiple times concurrently. Previous calls
+ * will be cancelled by subsequent calls.
*/
private async void _load_cache (HashSet<Persona>? old_personas)
{
@@ -967,6 +971,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
this.notify_property ("always-writeable-properties");
}
+ /* This method is safe to call multiple times concurrently. */
public override async void flush ()
{
debug ("Flushing Tpf.PersonaStore %p (â%sâ).", this, this.id);
@@ -990,6 +995,8 @@ public class Tpf.PersonaStore : Folks.PersonaStore
/**
* When we're about to disconnect, store the current set of personas to the
* cache file so that we can access them once offline.
+ *
+ * This method is safe to call multiple times concurrently.
*/
private async void _store_cache (HashSet<Persona> old_personas)
{
@@ -1283,6 +1290,8 @@ public class Tpf.PersonaStore : Folks.PersonaStore
}
}
+ /* This method is safe to call multiple times concurrently for the same (or
+ * different) contact ID, assuming Telepathy is safe. */
private async Persona _ensure_persona_for_id (string contact_id)
throws GLib.Error
{
@@ -1295,6 +1304,9 @@ public class Tpf.PersonaStore : Folks.PersonaStore
*
* See { link Folks.PersonaStore.add_persona_from_details}.
*
+ * This method is safe to call multiple times concurrently for the same (or
+ * different) contact IDs (assuming Telepathy is safe).
+ *
* @throws Folks.PersonaStoreError.INVALID_ARGUMENT if the ``contact`` key was
* not provided in ``details``
* @throws Folks.PersonaStoreError.STORE_OFFLINE if the CM is offline
@@ -1746,6 +1758,7 @@ public class Tpf.PersonaStore : Folks.PersonaStore
return templates;
}
+ /* This method is safe to call multiple times concurrently. */
private async void _populate_counters ()
{
if (this._log == null)
diff --git a/backends/tracker/lib/trf-persona-store.vala b/backends/tracker/lib/trf-persona-store.vala
index eec04e8..dd0a7ab 100644
--- a/backends/tracker/lib/trf-persona-store.vala
+++ b/backends/tracker/lib/trf-persona-store.vala
@@ -887,7 +887,8 @@ public class Trf.PersonaStore : Folks.PersonaStore
/**
* Remove a { link Persona} from the PersonaStore.
*
- * See { link Folks.PersonaStore.remove_persona}.
+ * See { link Folks.PersonaStore.remove_persona}. This method is not safe to
+ * call multiple times concurrently on the same persona.
*
* @throws Folks.PersonaStoreError currently unused
*/
@@ -907,6 +908,9 @@ public class Trf.PersonaStore : Folks.PersonaStore
yield this._tracker_update (q.printf (urn, urn), "remove_persona");
}
+ /* This method is not safe to call multiple times concurrently, since one call
+ * could be part-way through removing attributes of the URN while a subsequent
+ * call is attempting to retrieve the URN. */
private async string _remove_attributes_from_persona (Folks.Persona persona,
char remove_flag)
{
@@ -915,6 +919,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
return urn;
}
+ /* This method is safe to call multiple times concurrently. */
private async void _build_update_query_set (
Tracker.Sparql.Builder builder,
Set<AbstractFieldDetails<string>> properties,
@@ -982,6 +987,9 @@ public class Trf.PersonaStore : Folks.PersonaStore
* check to if the deleted nco:Person
* is the only one holding a link, if so we
* remove the resource.
+ *
+ * This method is not safe to call multiple times concurrently, since the
+ * deletions will race.
*/
private async void _remove_attributes (string urn, char remove_flag)
{
@@ -1169,6 +1177,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
(Trf.OntologyDefs.NCO_FEMALE);
}
+ /* This is safe to call multiple times concurrently. */
private async void _build_predicates_table ()
{
if (PersonaStore._prefix_tracker_id != null)
@@ -1405,6 +1414,8 @@ public class Trf.PersonaStore : Folks.PersonaStore
return added_personas;
}
+ /* This method is not safe to call multiple times concurrently on the same
+ * persona, since the queries and updates will race. */
private async void _do_update (Persona p, Event e, bool adding = true)
{
if (e.pred_id ==
@@ -1673,6 +1684,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
}
}
+ /* This method is safe to call multiple times concurrently. */
private async string _get_property
(int subject_tracker_id, string property,
string subject_type = Trf.OntologyDefs.NCO_PERSON)
@@ -1688,6 +1700,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
return yield this._single_value_query (query);
}
+ /* This method is safe to call multiple times concurrently. */
private async string _get_nao_property_by_person_id (int nco_person_id,
string prop_name)
{
@@ -1704,6 +1717,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
return yield this._single_value_query (query);
}
+ /* This method is safe to call multiple times concurrently. */
private async string[] _get_nao_property_by_prop_id (int nao_prop_id)
{
const string query_t = "SELECT " +
@@ -1722,6 +1736,8 @@ public class Trf.PersonaStore : Folks.PersonaStore
/*
* This should be kept in sync with Trf.AfflInfoFields
+ *
+ * This method is safe to call multiple times concurrently.
*/
private async Trf.AfflInfo _get_affl_info (
string person_id, string affiliation_id)
@@ -1842,6 +1858,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
return affl_info;
}
+ /* This method is safe to call multiple times concurrently. */
private async string? _insert_persona (string query, string persona_var)
throws PersonaStoreError
{
@@ -1895,6 +1912,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
return null;
}
+ /* This method is safe to call multiple times concurrently. */
private async string _single_value_query (string query)
{
Gee.HashSet<string> rows = yield this._multi_value_query (query);
@@ -1905,6 +1923,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
return "";
}
+ /* This method is safe to call multiple times concurrently. */
private async Gee.HashSet<string> _multi_value_query (string query)
{
Gee.HashSet<string> ret = new Gee.HashSet<string> ();
@@ -1933,6 +1952,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
return ret;
}
+ /* This method is safe to call multiple times concurrently. */
private async string _urn_from_tracker_id (string tracker_id)
{
const string query = "SELECT fn:concat('<', tracker:uri(%s), '>') " +
@@ -1940,6 +1960,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
return yield this._single_value_query (query.printf (tracker_id));
}
+ /* This method is safe to call multiple times concurrently. */
internal async void _set_nickname (Trf.Persona persona, string nickname)
{
const string query_t = "DELETE { "+
@@ -1964,6 +1985,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
yield this._tracker_update (query, "change_nickname");
}
+ /* This method is safe to call multiple times concurrently. */
internal async void _set_local_ids (Trf.Persona persona,
Set<string> local_ids)
{
@@ -1973,6 +1995,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
"_set_local_ids");
}
+ /* This method is safe to call multiple times concurrently. */
internal async void _set_web_service_addrs (Trf.Persona persona,
MultiMap<string, WebServiceFieldDetails> ws_obj)
{
@@ -1982,6 +2005,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
"_set_web_service_addrs");
}
+ /* This method is safe to call multiple times concurrently. */
private async void _set_tracker_property(Trf.Persona persona,
string prop_name, string prop_value, string callers_name)
{
@@ -2008,6 +2032,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
yield this._tracker_update (query, callers_name);
}
+ /* This method is safe to call multiple times concurrently. */
internal async void _set_is_favourite (Folks.Persona persona,
bool is_favourite)
{
@@ -2041,6 +2066,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
yield this._tracker_update (query, "change_is_favourite");
}
+ /* This method may not be safe to call multiple times concurrently. */
internal async void _set_emails (Folks.Persona persona,
Set<EmailFieldDetails> emails)
{
@@ -2048,6 +2074,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
Trf.Attrib.EMAILS);
}
+ /* This method may not be safe to call multiple times concurrently. */
internal async void _set_phones (Folks.Persona persona,
Set<PhoneFieldDetails> phone_numbers)
{
@@ -2055,6 +2082,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
Trf.Attrib.PHONES);
}
+ /* This method may not be safe to call multiple times concurrently. */
internal async void _set_unique_attrib_set (Folks.Persona persona,
Set<AbstractFieldDetails<string>> properties, Trf.Attrib attrib)
{
@@ -2092,6 +2120,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
yield this._tracker_update (builder.result, query_name);
}
+ /* This method is probably not safe to call multiple times concurrently. */
internal async void _set_urls (Folks.Persona persona,
Set<UrlFieldDetails> urls)
{
@@ -2099,6 +2128,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
Trf.Attrib.URLS);
}
+ /* This method is probably not safe to call multiple times concurrently. */
internal async void _set_im_addresses (Folks.Persona persona,
MultiMap<string, ImFieldDetails> im_addresses)
{
@@ -2117,6 +2147,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
yield this._set_attrib_set (persona, ims, Trf.Attrib.IM_ADDRESSES);
}
+ /* This method is probably not safe to call multiple times concurrently. */
internal async void _set_postal_addresses (Folks.Persona persona,
Set<PostalAddressFieldDetails> postal_addresses)
{
@@ -2124,6 +2155,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
Trf.Attrib.POSTAL_ADDRESSES);
}
+ /* This method is safe to call multiple times concurrently. */
internal async void _set_roles (Folks.Persona persona,
Set<RoleFieldDetails> roles)
{
@@ -2176,6 +2208,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
yield this._tracker_update (del_q + builder.result, "_set_roles");
}
+ /* This method is safe to call multiple times concurrently. */
internal async void _set_notes (Folks.Persona persona,
Set<NoteFieldDetails> notes)
{
@@ -2213,6 +2246,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
yield this._tracker_update (del_q + builder.result, "_set_notes");
}
+ /* This method is safe to call multiple times concurrently. */
internal async void _set_birthday (Folks.Persona persona,
owned DateTime bday)
{
@@ -2240,6 +2274,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
yield this._tracker_update (query, "_set_birthday");
}
+ /* This method is safe to call multiple times concurrently. */
internal async void _set_gender (Folks.Persona persona,
owned Gender gender)
{
@@ -2281,6 +2316,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
yield this._tracker_update (query, "_set_gender");
}
+ /* This method is not safe to call multiple times concurrently. */
internal async void _set_avatar (Folks.Persona persona,
LoadableIcon? avatar)
{
@@ -2343,6 +2379,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
yield this._tracker_update (query, "_set_avatar");
}
+ /* This method is safe to call multiple times concurrently. */
internal async void _set_structured_name (Folks.Persona persona,
StructuredName? sname)
{
@@ -2386,6 +2423,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
yield this._tracker_update (query, "_set_structured_name");
}
+ /* This method is safe to call multiple times concurrently. */
internal async void _set_full_name (Folks.Persona persona,
string full_name)
{
@@ -2413,6 +2451,8 @@ public class Trf.PersonaStore : Folks.PersonaStore
/* NOTE:
* - first we nuke old attribs
* - we create new affls with the new attribs
+ *
+ * This method is probably not safe to call multiple times concurrently.
*/
private async void _set_attrib_set (Folks.Persona persona,
Set<Object> attribs, Trf.Attrib what)
@@ -2547,6 +2587,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
yield this._tracker_update (builder.result, "set_attrib");
}
+ /* This method is safe to call multiple times concurrently. */
private async bool _tracker_update (string query, string caller)
{
bool ret = false;
@@ -2577,12 +2618,14 @@ public class Trf.PersonaStore : Folks.PersonaStore
return ret;
}
+ /* This method is safe to call multiple times concurrently. */
private async Gee.HashSet<string> _affiliations_from_persona (string urn)
{
return yield this._linked_resources (urn, Trf.OntologyDefs.NCO_PERSON,
Trf.OntologyDefs.NCO_HAS_AFFILIATION);
}
+ /* This method is safe to call multiple times concurrently. */
private async Gee.HashSet<string> _phones_from_affiliation (string affl)
{
return yield this._linked_resources (affl,
@@ -2590,6 +2633,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
Trf.OntologyDefs.NCO_HAS_PHONE);
}
+ /* This method is safe to call multiple times concurrently. */
private async Gee.HashSet<string> _postals_from_affiliation (string affl)
{
return yield this._linked_resources (affl,
@@ -2597,6 +2641,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
Trf.OntologyDefs.NCO_HAS_POSTAL_ADDRESS);
}
+ /* This method is safe to call multiple times concurrently. */
private async Gee.HashSet<string> _imaddrs_from_affiliation (string affl)
{
return yield this._linked_resources (affl,
@@ -2604,6 +2649,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
Trf.OntologyDefs.NCO_HAS_IMADDRESS);
}
+ /* This method is safe to call multiple times concurrently. */
private async Gee.HashSet<string> _emails_from_affiliation (string affl)
{
return yield this._linked_resources (affl,
@@ -2614,6 +2660,8 @@ public class Trf.PersonaStore : Folks.PersonaStore
/**
* Retrieve the list of linked resources of a given subject
*
+ * This method is safe to call multiple times concurrently.
+ *
* @param resource the urn of the resource in <urn> format
* @return number of resources linking to this resource
*/
@@ -2640,6 +2688,9 @@ public class Trf.PersonaStore : Folks.PersonaStore
* This means that _delete_resource shold be called before
* removing the resources that hold a link to it (which also
* makes sense from the signaling perspective).
+ *
+ * This method is not safe to call multiple times concurrently, as the
+ * resource count check races with deletion.
*/
private async bool _delete_resource (string resource_urn,
bool check_count = true)
@@ -2672,6 +2723,8 @@ public class Trf.PersonaStore : Folks.PersonaStore
/**
* Retrieve the list of linked resources of a given subject
*
+ * This method is safe to call multiple times concurrently.
+ *
* @param urn the urn of the subject in <urn> format
* @param subject_type i.e: nco:Person, nco:Affiliation, etc
* @param linking_predicate i.e.: nco:hasAffiliation
@@ -2691,6 +2744,7 @@ public class Trf.PersonaStore : Folks.PersonaStore
return yield this._multi_value_query (query);
}
+ /* This method is safe to call multiple times concurrently. */
private async string _urn_from_persona (Folks.Persona persona)
{
var id = ((Trf.Persona) persona).tracker_id;
@@ -2700,6 +2754,8 @@ public class Trf.PersonaStore : Folks.PersonaStore
/**
* Helper method to figure out if a constrained property
* already exists.
+ *
+ * This method is safe to call multiple times concurrently.
*/
private async string _urn_from_property (string class_name,
string property_name,
diff --git a/folks/anti-linkable.vala b/folks/anti-linkable.vala
index 23d42c8..39b4e63 100644
--- a/folks/anti-linkable.vala
+++ b/folks/anti-linkable.vala
@@ -105,6 +105,9 @@ public interface Folks.AntiLinkable : Folks.Persona
* Any attempt to anti-link a persona with itself is not an error, but is
* ignored.
*
+ * This method is safe to call multiple times concurrently (e.g. begin one
+ * asynchronous call, then begin another before the first has finished).
+ *
* @param other_personas the personas to anti-link to this one
* @throws PropertyError if setting the anti-links failed
* @since 0.7.3
@@ -135,6 +138,9 @@ public interface Folks.AntiLinkable : Folks.Persona
* The UIDs of all personas in ``other_personas`` will be removed from this
* persona's anti-links set and the changes propagated to backends.
*
+ * This method is safe to call multiple times concurrently (e.g. begin one
+ * asynchronous call, then begin another before the first has finished).
+ *
* @param other_personas the personas to remove anti-links from this one
* @throws PropertyError if setting the anti-links failed
* @since 0.7.3
diff --git a/folks/avatar-cache.vala b/folks/avatar-cache.vala
index 3536f0a..78ff67b 100644
--- a/folks/avatar-cache.vala
+++ b/folks/avatar-cache.vala
@@ -130,6 +130,10 @@ public class Folks.AvatarCache : Object
* example, this ID could be the UID of a persona. The URI of the cached
* avatar file will be returned.
*
+ * This method may be called multiple times concurrently for the same avatar
+ * ID (e.g. an asynchronous call may be made, and a subsequent asynchronous
+ * call may begin before the first has finished).
+ *
* @param id the globally unique ID for the avatar
* @param avatar the avatar data to cache
* @return a URI for the file storing the cached avatar
@@ -155,6 +159,10 @@ public class Folks.AvatarCache : Object
try
{
+ /* In order for this to be concurrency-safe, we assume that
+ * replace_async() does an atomic substitution of the new file for
+ * the old when the stream is closed. (i.e. It's
+ * concurrency-safe). */
dest_avatar_stream =
yield dest_avatar_file.replace_async (null, false,
FileCreateFlags.PRIVATE);
diff --git a/folks/backend-store.vala b/folks/backend-store.vala
index a63563a..ae17d65 100644
--- a/folks/backend-store.vala
+++ b/folks/backend-store.vala
@@ -250,6 +250,10 @@ public class Folks.BackendStore : Object {
* called for the first time. If it isn't called explicitly,
* { link BackendStore.load_backends} will call it.
*
+ * This method is safe to call multiple times concurrently (e.g. an
+ * asynchronous call may begin between a subsequent asynchronous call
+ * beginning and finishing).
+ *
* @since 0.3.0
*/
public async void prepare ()
@@ -275,6 +279,8 @@ public class Folks.BackendStore : Object {
* ``FOLKS_BACKEND_PATH`` environment variable, if it's set. If it's not set,
* backends will be searched for in a path set at compilation time.
*
+ * This method is not safe to call multiple times concurrently.
+ *
* @throws GLib.Error currently unused
*/
public async void load_backends () throws GLib.Error
@@ -379,6 +385,8 @@ public class Folks.BackendStore : Object {
Internal.profiling_end ("loading backends in BackendStore");
}
+ /* This method is not safe to call multiple times concurrently, since there's
+ * a race in updating this._prepared_backends. */
private async void _backend_load_if_needed (Backend backend)
{
if (this._backend_is_enabled (backend.name))
@@ -402,6 +410,8 @@ public class Folks.BackendStore : Object {
}
}
+ /* This method is not safe to call multiple times concurrently, since there's
+ * a race in updating this._prepared_backends. */
private async bool _backend_unload_if_needed (Backend backend)
{
var unloaded = false;
@@ -539,6 +549,10 @@ public class Folks.BackendStore : Object {
* to load it when { link BackendStore.load_backends} is called. This will
* not load the backend if it's not currently loaded.
*
+ * This method is safe to call multiple times concurrently (e.g. an
+ * asynchronous call may begin after a previous asynchronous call for the same
+ * backend name has begun and before it has finished).
+ *
* @param name the name of the backend to enable
* @since 0.3.2
*/
@@ -555,6 +569,10 @@ public class Folks.BackendStore : Object {
* client application is restarted. This will not remove the backend if it's
* already loaded.
*
+ * This method is safe to call multiple times concurrently (e.g. an
+ * asynchronous call may begin after a previous asynchronous call for the same
+ * backend name has begun and before it has finished).
+ *
* @param name the name of the backend to disable
* @since 0.3.2
*/
@@ -564,6 +582,7 @@ public class Folks.BackendStore : Object {
yield this._save_key_file ();
}
+ /* This method is safe to call multiple times concurrently. */
private async HashMap<string, File>? _get_modules_from_dir (File dir)
{
debug ("Searching for modules in folder '%s' ..", dir.get_path ());
@@ -697,6 +716,7 @@ public class Folks.BackendStore : Object {
debug ("Loaded module source: '%s'", module.name ());
}
+ /* This method is safe to call multiple times concurrently. */
private async static void _get_file_info (File file,
out bool is_file,
out bool is_dir)
@@ -734,6 +754,7 @@ public class Folks.BackendStore : Object {
is_dir = (file_info.get_file_type () == FileType.DIRECTORY);
}
+ /* This method is safe to call multiple times concurrently. */
private async void _load_disabled_backend_names ()
{
File file;
@@ -760,7 +781,7 @@ public class Folks.BackendStore : Object {
this._config_file = file;
/* Load the disabled backends file */
- this._backends_key_file = new GLib.KeyFile ();
+ var key_file = new GLib.KeyFile ();
try
{
uint8[] contents;
@@ -770,7 +791,7 @@ public class Folks.BackendStore : Object {
if (contents_s.length > 0)
{
- this._backends_key_file.load_from_data (contents_s,
+ key_file.load_from_data (contents_s,
contents_s.length, KeyFileFlags.KEEP_COMMENTS);
}
}
@@ -783,8 +804,15 @@ public class Folks.BackendStore : Object {
return;
}
}
+ finally
+ {
+ /* Update the key file in memory, whether the new one is empty or
+ * full. */
+ this._backends_key_file = (owned) key_file;
+ }
}
+ /* This method is safe to call multiple times concurrently. */
private async void _save_key_file ()
{
var key_file_data = this._backends_key_file.to_data ();
diff --git a/folks/individual-aggregator.vala b/folks/individual-aggregator.vala
index 18865f1..6202b3f 100644
--- a/folks/individual-aggregator.vala
+++ b/folks/individual-aggregator.vala
@@ -1817,6 +1817,9 @@ public class Folks.IndividualAggregator : Object
* Completely remove the individual and all of its personas from their
* backing stores.
*
+ * This method is safe to call multiple times concurrently (for the same
+ * individual or different individuals).
+ *
* @param individual the { link Individual} to remove
* @throws GLib.Error if removing the persona failed â this will be passed
* through from { link PersonaStore.remove_persona}
@@ -1844,6 +1847,9 @@ public class Folks.IndividualAggregator : Object
*
* This will leave other personas in the same individual alone.
*
+ * This method is safe to call multiple times concurrently (for the same
+ * persona or different personas).
+ *
* @param persona the { link Persona} to remove
* @throws GLib.Error if removing the persona failed â this will be passed
* through from { link PersonaStore.remove_persona}
@@ -1866,6 +1872,8 @@ public class Folks.IndividualAggregator : Object
* before is signalled by { link IndividualAggregator.individuals_changed} and
* { link Individual.removed}.
*
+ * This method is safe to call multiple times concurrently.
+ *
* @param personas the { link Persona}s to be linked
* @throws IndividualAggregatorError.NO_PRIMARY_STORE if no primary store has
* been configured for the individual aggregator
@@ -2039,6 +2047,10 @@ public class Folks.IndividualAggregator : Object
* new { link Individual}s will be signalled by
* { link IndividualAggregator.individuals_changed}.
*
+ * This method is safe to call multiple times concurrently, although
+ * concurrent calls for the same individual may result in duplicate personas
+ * being created.
+ *
* @param individual the { link Individual} to unlink
* @throws GLib.Error if removing the linking persona failed â this will be
* passed through from { link PersonaStore.remove_persona}
@@ -2120,6 +2132,10 @@ public class Folks.IndividualAggregator : Object
* { link IndividualAggregatorError.PROPERTY_NOT_WRITEABLE} error will be
* thrown.
*
+ * This method is safe to call multiple times concurrently, although
+ * concurrent calls for the same individual may result in duplicate personas
+ * being created.
+ *
* @param individual the individual for which ``property_name`` should be
* writeable
* @param property_name the name of the property which needs to be writeable
@@ -2148,6 +2164,9 @@ public class Folks.IndividualAggregator : Object
return p;
}
+ /* This is safe to call multiple times concurrently, *but* if the set of
+ * personas doesn't change, multiple duplicate personas may be created in the
+ * writeable store. */
private async Persona _ensure_personas_property_writeable (
Set<Persona> personas, string property_name)
throws IndividualAggregatorError
@@ -2249,6 +2268,8 @@ public class Folks.IndividualAggregator : Object
* been called, and will call { link IndividualAggregator.prepare} itself in
* that case.
*
+ * This method is safe to call multiple times concurrently.
+ *
* @param id ID of the individual to look up
* @return individual with ``id``, or ``null`` if no such individual was found
* @throws GLib.Error from { link IndividualAggregator.prepare}
diff --git a/folks/object-cache.vala b/folks/object-cache.vala
index b33a4fe..69a5881 100644
--- a/folks/object-cache.vala
+++ b/folks/object-cache.vala
@@ -180,6 +180,8 @@ public abstract class Folks.ObjectCache<T> : Object
* If any errors are encountered while loading the objects, warnings will be
* logged as appropriate and ``null`` will be returned.
*
+ * This method is safe to call multiple times concurrently.
+ *
* @param cancellable A { link GLib.Cancellable} for the operation, or
* ``null``.
* @return A set of objects from the cache, or ``null``.
@@ -324,6 +326,8 @@ public abstract class Folks.ObjectCache<T> : Object
* cache will be left in a consistent state, but may be storing the old set
* of objects or the new set.
*
+ * This method is safe to call multiple times concurrently.
+ *
* @param objects A set of objects to store. This may be empty, but may not
* be ``null``.
* @param cancellable A { link GLib.Cancellable} for the operation, or
@@ -374,6 +378,7 @@ public abstract class Folks.ObjectCache<T> : Object
{
try
{
+ /* We assume that replace_contents_async() is atomic. */
yield this._cache_file.replace_contents_async (
data, null, false,
FileCreateFlags.PRIVATE, cancellable, null);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]