aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Barnes <mbarnes@redhat.com>2013-07-18 23:25:26 +0800
committerMatthew Barnes <mbarnes@redhat.com>2013-07-19 20:36:56 +0800
commit7383843653a4aecb0bffb589e57ff6bad51547fd (patch)
tree63bd66661d6d7d394d1adc03e20478ffd29b70e6
parentdd4d570b628a19e74546fbbbc9ee2c0e6783c9e7 (diff)
downloadgsoc2013-evolution-7383843653a4aecb0bffb589e57ff6bad51547fd.tar.gz
gsoc2013-evolution-7383843653a4aecb0bffb589e57ff6bad51547fd.tar.zst
gsoc2013-evolution-7383843653a4aecb0bffb589e57ff6bad51547fd.zip
Reimplement mail_folder_cache_note_store().
* Use GIO-style async parameters. * Add mail_folder_cache_note_store_finish(). * Do the bulk of the work in a thread so the logic is more readable. * Queue multiple calls for the same CamelStore and share the results.
-rw-r--r--libemail-engine/mail-folder-cache.c417
-rw-r--r--libemail-engine/mail-folder-cache.h20
-rw-r--r--mail/mail-send-recv.c45
3 files changed, 266 insertions, 216 deletions
diff --git a/libemail-engine/mail-folder-cache.c b/libemail-engine/mail-folder-cache.c
index d536f6dabb..6919b4fc1e 100644
--- a/libemail-engine/mail-folder-cache.c
+++ b/libemail-engine/mail-folder-cache.c
@@ -57,6 +57,7 @@
typedef struct _StoreInfo StoreInfo;
typedef struct _FolderInfo FolderInfo;
+typedef struct _AsyncContext AsyncContext;
typedef struct _UpdateClosure UpdateClosure;
struct _MailFolderCachePrivate {
@@ -131,6 +132,11 @@ struct _FolderInfo {
gulong folder_changed_handler_id;
};
+struct _AsyncContext {
+ StoreInfo *store_info;
+ CamelFolderInfo *info;
+};
+
struct _UpdateClosure {
GWeakRef cache;
@@ -153,13 +159,6 @@ struct _UpdateClosure {
gchar *msg_subject;
};
-struct _update_data {
- NoteDoneFunc done;
- gpointer data;
- MailFolderCache *cache;
- GCancellable *cancellable;
-};
-
/* Forward Declarations */
static void store_folder_created_cb (CamelStore *store,
CamelFolderInfo *info,
@@ -303,12 +302,10 @@ store_info_unref (StoreInfo *store_info)
g_return_if_fail (store_info->ref_count > 0);
if (g_atomic_int_dec_and_test (&store_info->ref_count)) {
- struct _update_data *ud;
- while (!g_queue_is_empty (&store_info->folderinfo_updates)) {
- ud = g_queue_pop_head (&store_info->folderinfo_updates);
- g_cancellable_cancel (ud->cancellable);
- }
+ g_warn_if_fail (
+ g_queue_is_empty (
+ &store_info->folderinfo_updates));
if (store_info->folder_opened_handler_id > 0) {
g_signal_handler_disconnect (
@@ -439,6 +436,19 @@ store_info_steal_folder_info (StoreInfo *store_info,
return folder_info;
}
+static void
+async_context_free (AsyncContext *async_context)
+{
+ if (async_context->info != NULL)
+ camel_store_free_folder_info (
+ async_context->store_info->store,
+ async_context->info);
+
+ store_info_unref (async_context->store_info);
+
+ g_slice_free (AsyncContext, async_context);
+}
+
static UpdateClosure *
update_closure_new (MailFolderCache *cache,
CamelStore *store)
@@ -1215,93 +1225,6 @@ store_folder_renamed_cb (CamelStore *store,
}
}
-static void
-mail_folder_cache_first_update (MailFolderCache *cache,
- StoreInfo *store_info)
-{
- CamelService *service;
- CamelSession *session;
- const gchar *uid;
-
- service = CAMEL_SERVICE (store_info->store);
- session = camel_service_ref_session (service);
- uid = camel_service_get_uid (service);
-
- if (store_info->vjunk != NULL)
- mail_folder_cache_note_folder (cache, store_info->vjunk);
-
- if (store_info->vtrash != NULL)
- mail_folder_cache_note_folder (cache, store_info->vtrash);
-
- /* Some extra work for the "On This Computer" store. */
- if (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0) {
- CamelFolder *folder;
- gint ii;
-
- for (ii = 0; ii < E_MAIL_NUM_LOCAL_FOLDERS; ii++) {
- folder = e_mail_session_get_local_folder (
- E_MAIL_SESSION (session), ii);
- mail_folder_cache_note_folder (cache, folder);
- }
- }
-
- g_object_unref (session);
-}
-
-static void
-update_folders (CamelStore *store,
- GAsyncResult *result,
- struct _update_data *ud)
-{
- CamelFolderInfo *fi;
- StoreInfo *store_info;
- GError *error = NULL;
- gboolean free_fi = TRUE;
-
- fi = camel_store_get_folder_info_finish (store, result, &error);
-
- /* Silently ignore cancellation errors. */
- if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- g_error_free (error);
-
- } else if (error != NULL) {
- g_warning ("%s", error->message);
- g_error_free (error);
- }
-
- store_info = mail_folder_cache_ref_store_info (ud->cache, store);
-
- if (store_info != NULL) {
- /* The store info is still there, so we can remove ourselves
- * from its list. Or else its not, and we're on our own and
- * free anyway. */
- g_mutex_lock (&store_info->lock);
- g_queue_remove (&store_info->folderinfo_updates, ud);
- g_mutex_unlock (&store_info->lock);
-
- if (fi != NULL && !g_cancellable_is_cancelled (ud->cancellable))
- create_folders (ud->cache, fi, store_info);
-
- /* Do some extra work for the first update. */
- if (store_info->first_update) {
- mail_folder_cache_first_update (ud->cache, store_info);
- store_info->first_update = FALSE;
- }
-
- store_info_unref (store_info);
- }
-
- if (ud->done != NULL)
- free_fi = ud->done (ud->cache, store, fi, ud->data);
- if (fi && free_fi)
- camel_store_free_folder_info (store, fi);
-
- if (ud->cancellable != NULL)
- g_object_unref (ud->cancellable);
-
- g_free (ud);
-}
-
struct _ping_store_msg {
MailMsg base;
CamelStore *store;
@@ -1408,46 +1331,6 @@ store_has_folder_hierarchy (CamelStore *store)
return FALSE;
}
-static void
-store_go_online_cb (CamelStore *store,
- GAsyncResult *result,
- struct _update_data *ud)
-{
- StoreInfo *store_info;
-
- /* FIXME Not checking the GAsyncResult for error. */
-
- store_info = mail_folder_cache_ref_store_info (ud->cache, store);
-
- if (store_info != NULL &&
- !g_cancellable_is_cancelled (ud->cancellable)) {
- /* We're already in the store update list. */
- if (store_has_folder_hierarchy (store))
- camel_store_get_folder_info (
- store, NULL,
- CAMEL_STORE_FOLDER_INFO_FAST |
- CAMEL_STORE_FOLDER_INFO_RECURSIVE |
- CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
- G_PRIORITY_DEFAULT, ud->cancellable,
- (GAsyncReadyCallback) update_folders, ud);
- } else {
- if (store_info != NULL) {
- g_mutex_lock (&store_info->lock);
- g_queue_remove (&store_info->folderinfo_updates, ud);
- g_mutex_unlock (&store_info->lock);
- }
-
- /* The store vanished, that means we were probably cancelled,
- * or at any rate, need to clean ourselves up. */
- if (ud->cancellable != NULL)
- g_object_unref (ud->cancellable);
- g_free (ud);
- }
-
- if (store_info != NULL)
- store_info_unref (store_info);
-}
-
static GList *
find_folder_uri (GQueue *queue,
CamelSession *session,
@@ -1881,6 +1764,151 @@ mail_folder_cache_ref_main_context (MailFolderCache *cache)
return g_main_context_ref (cache->priv->main_context);
}
+/* Helper for mail_folder_cache_note_store() */
+static void
+mail_folder_cache_first_update (MailFolderCache *cache,
+ StoreInfo *store_info)
+{
+ CamelService *service;
+ CamelSession *session;
+ const gchar *uid;
+
+ service = CAMEL_SERVICE (store_info->store);
+ session = camel_service_ref_session (service);
+ uid = camel_service_get_uid (service);
+
+ if (store_info->vjunk != NULL)
+ mail_folder_cache_note_folder (cache, store_info->vjunk);
+
+ if (store_info->vtrash != NULL)
+ mail_folder_cache_note_folder (cache, store_info->vtrash);
+
+ /* Some extra work for the "On This Computer" store. */
+ if (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0) {
+ CamelFolder *folder;
+ gint ii;
+
+ for (ii = 0; ii < E_MAIL_NUM_LOCAL_FOLDERS; ii++) {
+ folder = e_mail_session_get_local_folder (
+ E_MAIL_SESSION (session), ii);
+ mail_folder_cache_note_folder (cache, folder);
+ }
+ }
+
+ g_object_unref (session);
+}
+
+/* Helper for mail_folder_cache_note_store() */
+static void
+mail_folder_cache_note_store_thread (GSimpleAsyncResult *simple,
+ GObject *source_object,
+ GCancellable *cancellable)
+{
+ MailFolderCache *cache;
+ CamelService *service;
+ CamelSession *session;
+ StoreInfo *store_info;
+ GQueue result_queue = G_QUEUE_INIT;
+ AsyncContext *async_context;
+ GError *local_error = NULL;
+
+ cache = MAIL_FOLDER_CACHE (source_object);
+ async_context = g_simple_async_result_get_op_res_gpointer (simple);
+ store_info = async_context->store_info;
+
+ service = CAMEL_SERVICE (store_info->store);
+ session = camel_service_ref_session (service);
+
+ /* We might get a race when setting up a store, such that it is
+ * still left in offline mode, after we've gone online. This
+ * catches and fixes it up when the shell opens us.
+ *
+ * XXX This is a Bonobo-era artifact. Do we really still need
+ * to do this? Also, CamelDiscoStore needs to die already!
+ */
+ if (camel_session_get_online (session)) {
+ gboolean store_online = TRUE;
+
+ if (CAMEL_IS_DISCO_STORE (service)) {
+ CamelDiscoStore *disco_store;
+ CamelDiscoStoreStatus status;
+
+ disco_store = CAMEL_DISCO_STORE (service);
+ status = camel_disco_store_status (disco_store);
+ store_online = (status != CAMEL_DISCO_STORE_OFFLINE);
+ }
+
+ if (CAMEL_IS_OFFLINE_STORE (service)) {
+ store_online = camel_offline_store_get_online (
+ CAMEL_OFFLINE_STORE (service));
+ }
+
+ if (!store_online) {
+ e_mail_store_go_online_sync (
+ CAMEL_STORE (service),
+ cancellable, &local_error);
+
+ if (local_error != NULL) {
+ g_simple_async_result_take_error (
+ simple, local_error);
+ goto exit;
+ }
+ }
+ }
+
+ /* No folder hierarchy means we're done. */
+ if (!store_has_folder_hierarchy (store_info->store))
+ goto exit;
+
+ async_context->info = camel_store_get_folder_info_sync (
+ store_info->store, NULL,
+ CAMEL_STORE_FOLDER_INFO_FAST |
+ CAMEL_STORE_FOLDER_INFO_RECURSIVE |
+ CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
+ cancellable, &local_error);
+
+ /* Sanity check. */
+ g_return_if_fail (
+ ((async_context->info != NULL) && (local_error == NULL)) ||
+ ((async_context->info == NULL) && (local_error != NULL)));
+
+ if (local_error != NULL) {
+ g_simple_async_result_take_error (simple, local_error);
+ goto exit;
+ }
+
+ create_folders (cache, async_context->info, store_info);
+
+ /* Do some extra work for the first update. */
+ if (store_info->first_update) {
+ mail_folder_cache_first_update (cache, store_info);
+ store_info->first_update = FALSE;
+ }
+
+exit:
+ /* We don't want finish() functions being invoked while holding a
+ * locked mutex, so flush the StoreInfo's queue to a local queue. */
+ g_mutex_lock (&store_info->lock);
+ e_queue_transfer (&store_info->folderinfo_updates, &result_queue);
+ g_mutex_unlock (&store_info->lock);
+
+ while (!g_queue_is_empty (&result_queue)) {
+ GSimpleAsyncResult *queued_result;
+
+ queued_result = g_queue_pop_head (&result_queue);
+
+ /* Skip the GSimpleAsyncResult passed into this function.
+ * g_simple_async_result_run_in_thread() will complete it
+ * for us, and we don't want to complete it twice. */
+ if (queued_result != simple)
+ g_simple_async_result_complete_in_idle (queued_result);
+
+ g_clear_object (&queued_result);
+ }
+
+ g_object_unref (session);
+}
+
/**
* mail_folder_cache_note_store:
*
@@ -1892,72 +1920,83 @@ void
mail_folder_cache_note_store (MailFolderCache *cache,
CamelStore *store,
GCancellable *cancellable,
- NoteDoneFunc done,
- gpointer data)
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- CamelSession *session;
StoreInfo *store_info;
- struct _update_data *ud;
+ GSimpleAsyncResult *simple;
+ AsyncContext *async_context;
g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
g_return_if_fail (CAMEL_IS_STORE (store));
- session = camel_service_ref_session (CAMEL_SERVICE (store));
-
store_info = mail_folder_cache_ref_store_info (cache, store);
if (store_info == NULL)
store_info = mail_folder_cache_new_store_info (cache, store);
- ud = g_malloc0 (sizeof (*ud));
- ud->done = done;
- ud->data = data;
- ud->cache = cache;
+ async_context = g_slice_new0 (AsyncContext);
+ async_context->store_info = store_info_ref (store_info);
- if (G_IS_CANCELLABLE (cancellable))
- ud->cancellable = g_object_ref (cancellable);
+ simple = g_simple_async_result_new (
+ G_OBJECT (cache), callback, user_data,
+ mail_folder_cache_note_store);
- /* We might get a race when setting up a store, such that it is
- * still left in offline mode, after we've gone online. This
- * catches and fixes it up when the shell opens us. */
- if (CAMEL_IS_DISCO_STORE (store)) {
- if (camel_session_get_online (session) &&
- camel_disco_store_status (CAMEL_DISCO_STORE (store)) ==
- CAMEL_DISCO_STORE_OFFLINE) {
- e_mail_store_go_online (
- store, G_PRIORITY_DEFAULT, cancellable,
- (GAsyncReadyCallback) store_go_online_cb, ud);
- } else {
- goto normal_setup;
- }
- } else if (CAMEL_IS_OFFLINE_STORE (store)) {
- if (camel_session_get_online (session) &&
- !camel_offline_store_get_online (
- CAMEL_OFFLINE_STORE (store))) {
- e_mail_store_go_online (
- store, G_PRIORITY_DEFAULT, cancellable,
- (GAsyncReadyCallback) store_go_online_cb, ud);
- } else {
- goto normal_setup;
- }
- } else {
- normal_setup:
- if (store_has_folder_hierarchy (store))
- camel_store_get_folder_info (
- store, NULL,
- CAMEL_STORE_FOLDER_INFO_FAST |
- CAMEL_STORE_FOLDER_INFO_RECURSIVE |
- CAMEL_STORE_FOLDER_INFO_SUBSCRIBED,
- G_PRIORITY_DEFAULT, cancellable,
- (GAsyncReadyCallback) update_folders, ud);
- }
+ g_simple_async_result_set_check_cancellable (simple, cancellable);
+
+ g_simple_async_result_set_op_res_gpointer (
+ simple, async_context, (GDestroyNotify) async_context_free);
g_mutex_lock (&store_info->lock);
- g_queue_push_tail (&store_info->folderinfo_updates, ud);
+
+ g_queue_push_tail (
+ &store_info->folderinfo_updates,
+ g_object_ref (simple));
+
+ /* Queue length > 1 means there's already an operation for
+ * this store in progress so we'll just pick up the result
+ * when it finishes. */
+ if (g_queue_get_length (&store_info->folderinfo_updates) == 1)
+ g_simple_async_result_run_in_thread (
+ simple,
+ mail_folder_cache_note_store_thread,
+ G_PRIORITY_DEFAULT, cancellable);
+
g_mutex_unlock (&store_info->lock);
+ g_object_unref (simple);
+
store_info_unref (store_info);
+}
- g_object_unref (session);
+gboolean
+mail_folder_cache_note_store_finish (MailFolderCache *cache,
+ GAsyncResult *result,
+ CamelFolderInfo **out_info,
+ GError **error)
+{
+ GSimpleAsyncResult *simple;
+ AsyncContext *async_context;
+
+ g_return_val_if_fail (
+ g_simple_async_result_is_valid (
+ result, G_OBJECT (cache),
+ mail_folder_cache_note_store), FALSE);
+
+ simple = G_SIMPLE_ASYNC_RESULT (result);
+ async_context = g_simple_async_result_get_op_res_gpointer (simple);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+
+ if (out_info != NULL) {
+ if (async_context->info != NULL)
+ *out_info = camel_folder_info_clone (
+ async_context->info);
+ else
+ *out_info = NULL;
+ }
+
+ return TRUE;
}
/**
@@ -2205,6 +2244,10 @@ mail_folder_cache_service_enabled (MailFolderCache *cache,
g_return_if_fail (MAIL_IS_FOLDER_CACHE (cache));
g_return_if_fail (CAMEL_IS_SERVICE (service));
+ /* XXX This has no callback and it swallows errors. Maybe
+ * we don't want a service_enabled() function after all?
+ * Call mail_folder_cache_note_store() directly instead
+ * and handle errors appropriately. */
mail_folder_cache_note_store (
cache, CAMEL_STORE (service), NULL, NULL, NULL);
}
diff --git a/libemail-engine/mail-folder-cache.h b/libemail-engine/mail-folder-cache.h
index 1c8a5cc4ee..8a91ca56a4 100644
--- a/libemail-engine/mail-folder-cache.h
+++ b/libemail-engine/mail-folder-cache.h
@@ -54,17 +54,6 @@ typedef struct _MailFolderCacheClass MailFolderCacheClass;
typedef struct _MailFolderCachePrivate MailFolderCachePrivate;
/**
- * NoteDoneFunc:
- *
- * The signature of a function to be registered as a callback for
- * mail_folder_cache_note_store()
- */
-typedef gboolean (*NoteDoneFunc) (MailFolderCache *cache,
- CamelStore *store,
- CamelFolderInfo *info,
- gpointer data);
-
-/**
* MailFolderCache:
*
* Contains only private data that should be read and manipulated using the
@@ -113,8 +102,13 @@ GMainContext * mail_folder_cache_ref_main_context
void mail_folder_cache_note_store (MailFolderCache *cache,
CamelStore *store,
GCancellable *cancellable,
- NoteDoneFunc done,
- gpointer data);
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mail_folder_cache_note_store_finish
+ (MailFolderCache *cache,
+ GAsyncResult *result,
+ CamelFolderInfo **out_info,
+ GError **error);
void mail_folder_cache_note_folder (MailFolderCache *cache,
CamelFolder *folder);
gboolean mail_folder_cache_has_folder_info
diff --git a/mail/mail-send-recv.c b/mail/mail-send-recv.c
index 6de97a26b9..cd8b7b457e 100644
--- a/mail/mail-send-recv.c
+++ b/mail/mail-send-recv.c
@@ -1122,33 +1122,46 @@ static MailMsgInfo refresh_folders_info = {
(MailMsgFreeFunc) refresh_folders_free
};
-static gboolean
-receive_update_got_folderinfo (MailFolderCache *folder_cache,
- CamelStore *store,
- CamelFolderInfo *info,
- gpointer data)
+static void
+receive_update_got_folderinfo (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
{
- if (info) {
+ CamelFolderInfo *info = NULL;
+ struct _send_info *send_info = user_data;
+ GError *local_error = NULL;
+
+ mail_folder_cache_note_store_finish (
+ MAIL_FOLDER_CACHE (source_object),
+ result, &info, &local_error);
+
+ /* Ignore cancellations. */
+ if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warn_if_fail (info != NULL);
+ g_error_free (local_error);
+
+ /* XXX Need to hand this off to an EAlertSink. */
+ } else if (local_error != NULL) {
+ g_warn_if_fail (info != NULL);
+ g_warning ("%s: %s", G_STRFUNC, local_error->message);
+ g_error_free (local_error);
+
+ /* CamelFolderInfo may be NULL even if no error occurred. */
+ } else if (info != NULL) {
GPtrArray *folders = g_ptr_array_new ();
struct _refresh_folders_msg *m;
- struct _send_info *sinfo = data;
m = mail_msg_new (&refresh_folders_info);
- m->store = store;
- g_object_ref (store);
+ m->store = g_object_ref (send_info->service);
m->folders = folders;
- m->info = sinfo;
- m->finfo = info;
+ m->info = send_info;
+ m->finfo = info; /* takes ownership */
mail_msg_unordered_push (m);
- /* do not free folder info, we will free it later */
- return FALSE;
} else {
- receive_done (-1, data);
+ receive_done (-1, send_info);
}
-
- return TRUE;
}
static void