evolution-data-server r9910 - in branches/gnome-2-24/camel: . providers/imap
- From: msuman svn gnome org
- To: svn-commits-list gnome org
- Subject: evolution-data-server r9910 - in branches/gnome-2-24/camel: . providers/imap
- Date: Mon, 12 Jan 2009 03:46:33 +0000 (UTC)
Author: msuman
Date: Mon Jan 12 03:46:33 2009
New Revision: 9910
URL: http://svn.gnome.org/viewvc/evolution-data-server?rev=9910&view=rev
Log:
Patch from Robert Collins <robertc robertcollins net>: Fix for bug #564339 (Reduce the IO done during sync by not reading into memory locally cached files).
Modified:
branches/gnome-2-24/camel/ChangeLog
branches/gnome-2-24/camel/camel-folder.c
branches/gnome-2-24/camel/camel-folder.h
branches/gnome-2-24/camel/camel-offline-folder.c
branches/gnome-2-24/camel/providers/imap/ChangeLog
branches/gnome-2-24/camel/providers/imap/camel-imap-folder.c
branches/gnome-2-24/camel/providers/imap/camel-imap-message-cache.c
branches/gnome-2-24/camel/providers/imap/camel-imap-message-cache.h
Modified: branches/gnome-2-24/camel/camel-folder.c
==============================================================================
--- branches/gnome-2-24/camel/camel-folder.c (original)
+++ branches/gnome-2-24/camel/camel-folder.c Mon Jan 12 03:46:33 2009
@@ -88,6 +88,7 @@
CamelException *ex);
static GPtrArray *get_uids (CamelFolder *folder);
+static GPtrArray *get_uncached_uids (CamelFolder *, GPtrArray * uids, CamelException *);
static void free_uids (CamelFolder *folder,
GPtrArray *array);
static void sort_uids (CamelFolder *folder,
@@ -148,6 +149,7 @@
camel_folder_class->set_message_user_tag = set_message_user_tag;
camel_folder_class->get_message = get_message;
camel_folder_class->get_uids = get_uids;
+ camel_folder_class->get_uncached_uids = get_uncached_uids;
camel_folder_class->free_uids = free_uids;
camel_folder_class->sort_uids = sort_uids;
camel_folder_class->get_summary = get_summary;
@@ -163,6 +165,7 @@
camel_folder_class->delete = delete;
camel_folder_class->rename = folder_rename;
camel_folder_class->freeze = freeze;
+ camel_folder_class->sync_message = NULL;
camel_folder_class->thaw = thaw;
camel_folder_class->is_frozen = is_frozen;
camel_folder_class->get_quota_info = get_quota_info;
@@ -1133,6 +1136,35 @@
return ret;
}
+/**
+ * camel_folder_sync_message:
+ * @folder: a #CamelFolder object
+ * @uid: the UID
+ * @ex: a #CamelException
+ *
+ * Ensure that a message identified by UID has been synced in the folder (so
+ * that camel_folder_get_message on it later will work in offline mode).
+ *
+ * Returns: void.
+ **/
+void
+camel_folder_sync_message (CamelFolder *folder, const char *uid, CamelException *ex)
+{
+ g_return_if_fail (CAMEL_IS_FOLDER (folder));
+ CAMEL_FOLDER_REC_LOCK(folder, lock);
+
+ /* Use the sync_message method if the class implements it. */
+ if (CF_CLASS (folder)->sync_message)
+ CF_CLASS (folder)->sync_message (folder, uid, ex);
+ else {
+ CamelMimeMessage *message;
+ message = CF_CLASS (folder)->get_message (folder, uid, ex);
+ if (message)
+ camel_object_unref(message);
+ }
+ CAMEL_FOLDER_REC_UNLOCK(folder, lock);
+}
+
static GPtrArray *
get_uids(CamelFolder *folder)
{
@@ -1196,6 +1228,41 @@
}
+/**
+ * Default: return the uids we are given.
+ */
+static GPtrArray *
+get_uncached_uids (CamelFolder *folder, GPtrArray * uids, CamelException *ex)
+{
+ GPtrArray *result;
+ int i;
+
+ result = g_ptr_array_new();
+
+ g_ptr_array_set_size(result, uids->len);
+ for (i = 0; i < uids->len; i++)
+ result->pdata[i] = (char *)camel_pstring_strdup(uids->pdata[i]);
+ return result;
+}
+
+/**
+ * camel_folder_get_uncached_uids:
+ * @folder: a #CamelFolder object
+ * @uids: the array of uids to filter down to uncached ones.
+ *
+ * Returns the known-uncached uids from a list of uids. It may return uids
+ * which are locally cached but should never filter out a uid which is not
+ * locally cached. Free the result by called #camel_folder_free_uids.
+ * Frees the array of UIDs returned by #camel_folder_get_uids.
+ **/
+GPtrArray *
+camel_folder_get_uncached_uids (CamelFolder *folder, GPtrArray * uids, CamelException *ex)
+{
+ g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
+ return CF_CLASS (folder)->get_uncached_uids(folder, uids, ex);
+}
+
+
static int
uidcmp (const void *v0, const void *v1)
{
Modified: branches/gnome-2-24/camel/camel-folder.h
==============================================================================
--- branches/gnome-2-24/camel/camel-folder.h (original)
+++ branches/gnome-2-24/camel/camel-folder.h Mon Jan 12 03:46:33 2009
@@ -211,6 +211,10 @@
CamelFolderQuotaInfo * (*get_quota_info) (CamelFolder *folder);
guint32 (*count_by_expression) (CamelFolder *, const char *, CamelException *);
+ void (*sync_message) (CamelFolder *folder,
+ const char *uid,
+ CamelException *ex);
+ GPtrArray * (*get_uncached_uids)(CamelFolder *, GPtrArray * uids, CamelException *);
} CamelFolderClass;
/* Standard Camel function */
@@ -303,12 +307,18 @@
CamelMimeMessage * camel_folder_get_message (CamelFolder *folder,
const char *uid,
CamelException *ex);
+void camel_folder_sync_message (CamelFolder *folder,
+ const char *uid,
+ CamelException *ex);
#define camel_folder_delete_message(folder, uid) \
camel_folder_set_message_flags (folder, uid, CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_DELETED|CAMEL_MESSAGE_SEEN)
GPtrArray * camel_folder_get_uids (CamelFolder *folder);
void camel_folder_free_uids (CamelFolder *folder,
GPtrArray *array);
+GPtrArray * camel_folder_get_uncached_uids (CamelFolder *,
+ GPtrArray * uids,
+ CamelException *);
void camel_folder_sort_uids (CamelFolder *folder,
GPtrArray *uids);
Modified: branches/gnome-2-24/camel/camel-offline-folder.c
==============================================================================
--- branches/gnome-2-24/camel/camel-offline-folder.c (original)
+++ branches/gnome-2-24/camel/camel-offline-folder.c Mon Jan 12 03:46:33 2009
@@ -247,8 +247,7 @@
offline_folder_downsync (CamelOfflineFolder *offline, const char *expression, CamelException *ex)
{
CamelFolder *folder = (CamelFolder *) offline;
- CamelMimeMessage *message;
- GPtrArray *uids;
+ GPtrArray *uids, *uncached_uids = NULL;
int i;
camel_operation_start (NULL, _("Syncing messages in folder '%s' to disk"), folder->full_name);
@@ -257,27 +256,29 @@
uids = camel_folder_search_by_expression (folder, expression, ex);
else
uids = camel_folder_get_uids (folder);
-
- if (!uids) {
- camel_operation_end (NULL);
- return;
+
+ if (!uids)
+ goto done;
+ uncached_uids = camel_folder_get_uncached_uids(folder, uids, ex);
+ if (uids) {
+ if (expression)
+ camel_folder_search_free (folder, uids);
+ else
+ camel_folder_free_uids (folder, uids);
}
- for (i = 0; i < uids->len; i++) {
- int pc = i * 100 / uids->len;
-
- message = camel_folder_get_message (folder, uids->pdata[i], ex);
+ if (!uncached_uids)
+ goto done;
+
+ for (i = 0; i < uncached_uids->len; i++) {
+ int pc = i * 100 / uncached_uids->len;
+ camel_folder_sync_message (folder, uncached_uids->pdata[i], ex);
camel_operation_progress (NULL, pc);
- if (message == NULL)
- break;
-
- camel_object_unref (message);
}
-
- if (expression)
- camel_folder_search_free (folder, uids);
- else
- camel_folder_free_uids (folder, uids);
+
+done:
+ if (uncached_uids)
+ camel_folder_free_uids(folder, uncached_uids);
camel_operation_end (NULL);
}
Modified: branches/gnome-2-24/camel/providers/imap/camel-imap-folder.c
==============================================================================
--- branches/gnome-2-24/camel/providers/imap/camel-imap-folder.c (original)
+++ branches/gnome-2-24/camel/providers/imap/camel-imap-folder.c Mon Jan 12 03:46:33 2009
@@ -102,10 +102,13 @@
static void imap_expunge (CamelFolder *folder, CamelException *ex);
//static void imap_cache_message (CamelDiscoFolder *disco_folder, const char *uid, CamelException *ex);
static void imap_rename (CamelFolder *folder, const char *new);
+static GPtrArray * imap_get_uncached_uids (CamelFolder *folder, GPtrArray * uids, CamelException *ex);
/* message manipulation */
static CamelMimeMessage *imap_get_message (CamelFolder *folder, const gchar *uid,
CamelException *ex);
+static void imap_sync_message (CamelFolder *folder, const gchar *uid,
+ CamelException *ex);
static void imap_append_online (CamelFolder *folder, CamelMimeMessage *message,
const CamelMessageInfo *info, char **appended_uid,
CamelException *ex);
@@ -135,6 +138,12 @@
static GData *parse_fetch_response (CamelImapFolder *imap_folder, char *msg_att);
+/* internal helpers */
+static CamelImapMessageInfo * imap_folder_summary_uid_or_error(
+ CamelFolderSummary *summary,
+ const char * uid,
+ CamelException *ex);
+
#ifdef G_OS_WIN32
/* The strtok() in Microsoft's C library is MT-safe (but still uses
* only one buffer pointer per thread, but for the use of strtok_r()
@@ -166,7 +175,9 @@
camel_folder_class->expunge = imap_expunge;
camel_folder_class->sync= imap_sync;
camel_folder_class->append_message = imap_append_online;
+ camel_folder_class->sync_message = imap_sync_message;
camel_folder_class->transfer_messages_to = imap_transfer_online;
+ camel_folder_class->get_uncached_uids = imap_get_uncached_uids;
}
static void
@@ -2816,6 +2827,20 @@
return FALSE;
}
+static CamelImapMessageInfo *
+imap_folder_summary_uid_or_error(CamelFolderSummary *summary, const char * uid, CamelException *ex)
+{
+ CamelImapMessageInfo *mi;
+ mi = (CamelImapMessageInfo *)camel_folder_summary_uid (summary, uid);
+ if (mi == NULL) {
+ camel_exception_setv (
+ ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
+ _("Cannot get message with message ID %s: %s"),
+ uid, _("No such message available."));
+ }
+ return mi;
+}
+
static CamelMimeMessage *
imap_get_message (CamelFolder *folder, const char *uid, CamelException *ex)
{
@@ -2826,14 +2851,9 @@
CamelStream *stream = NULL;
int retry;
- mi = (CamelImapMessageInfo *)camel_folder_summary_uid (folder->summary, uid);
- if (mi == NULL) {
- camel_exception_setv (
- ex, CAMEL_EXCEPTION_FOLDER_INVALID_UID,
- _("Cannot get message with message ID %s: %s"),
- uid, _("No such message available."));
- return NULL;
- }
+ mi = imap_folder_summary_uid_or_error(folder->summary, uid, ex);
+ if (!mi)
+ return NULL;
/* If its cached in full, just get it as is, this is only a shortcut,
since we get stuff from the cache anyway. It affects a busted connection though. */
@@ -2954,6 +2974,44 @@
return msg;
}
+/**
+ * imap_sync_message
+ *
+ * Ensure that a message is cached locally, but don't retrieve the content if
+ * it is already local.
+ */
+static void
+imap_sync_message (CamelFolder *folder, const char *uid, CamelException *ex)
+{
+ CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
+ CamelImapMessageInfo *mi;
+ CamelMimeMessage *msg = NULL;
+ CamelStream *stream = NULL;
+
+ mi = imap_folder_summary_uid_or_error(folder->summary, uid, ex);
+ if (!mi)
+ /* No such UID - is this duplicate work? The sync process selects
+ * UIDs to start with.
+ */
+ return;
+ camel_message_info_free(&mi->info);
+
+ /* If we can get a stream, assume its fully cached. This may be false
+ * if partial streams are saved elsewhere in the code - but that seems
+ * best solved by knowning more about whether a given message is fully
+ * available locally or not,
+ */
+ /* If its cached in full, just get it as is, this is only a shortcut,
+ since we get stuff from the cache anyway. It affects a busted connection though. */
+ if ((stream = camel_imap_folder_fetch_data(imap_folder, uid, "", TRUE, NULL))) {
+ camel_object_unref (stream);
+ return;
+ }
+ msg = imap_get_message(folder, uid, ex);
+ if (msg)
+ camel_object_unref(msg);
+}
+
/* FIXME Remove it after confirming
static void
imap_cache_message (CamelDiscoFolder *disco_folder, const char *uid,
@@ -3928,3 +3986,19 @@
CAMEL_SERVICE_REC_UNLOCK (imap_store, connect_lock);
return res;
}
+
+/**
+ * Scan for messages that are local and return the rest.
+ */
+static GPtrArray *
+imap_get_uncached_uids (CamelFolder *folder, GPtrArray * uids, CamelException *ex)
+{
+ GPtrArray *result;
+ CamelImapFolder *imap_folder = CAMEL_IMAP_FOLDER (folder);
+
+ CAMEL_IMAP_FOLDER_REC_LOCK (imap_folder, cache_lock);
+ result = camel_imap_message_cache_filter_cached (imap_folder->cache, uids, ex);
+ CAMEL_IMAP_FOLDER_REC_UNLOCK (imap_folder, cache_lock);
+ return result;
+}
+
Modified: branches/gnome-2-24/camel/providers/imap/camel-imap-message-cache.c
==============================================================================
--- branches/gnome-2-24/camel/providers/imap/camel-imap-message-cache.c (original)
+++ branches/gnome-2-24/camel/providers/imap/camel-imap-message-cache.c Mon Jan 12 03:46:33 2009
@@ -35,6 +35,7 @@
#include "camel-data-wrapper.h"
#include "camel-exception.h"
#include "camel-stream-fs.h"
+#include "camel-string-utils.h"
#include "camel-imap-message-cache.h"
@@ -42,9 +43,27 @@
#define O_BINARY 0
#endif
+/* Common define to start reducing duplication of base-part handling on win32.
+ */
+#ifdef G_OS_WIN32
+/* See comment in insert_setup() */
+#define BASE_PART_SUFFIX ".~"
+#else
+#define BASE_PART_SUFFIX "."
+#endif
+
static void finalize (CamelImapMessageCache *cache);
static void stream_finalize (CamelObject *stream, gpointer event_data, gpointer user_data);
+struct _part_find {
+ /* UID name on disk - e.g. "0." or "0.HEADERS". On windows "0." is
+ * stored as "0.~"
+ */
+ char *disk_part_name;
+ /* Was the part found? */
+ int found;
+};
+
CamelType
camel_imap_message_cache_get_type (void)
@@ -142,6 +161,8 @@
* Return value: a new CamelImapMessageCache object using @path for
* storage. If cache files already exist in @path, then any that do not
* correspond to messages in @summary will be deleted.
+ * @path is scanned for its contents, which means creating a cache object can be
+ * expensive, but the parts hash is immediately usable.
**/
CamelImapMessageCache *
camel_imap_message_cache_new (const char *path, CamelFolderSummary *summary,
@@ -591,3 +612,67 @@
}
}
}
+
+
+static void
+_match_part(gpointer part_name, gpointer user_data)
+{
+ struct _part_find *part_find = (struct _part_find *) user_data;
+ if (g_str_equal(part_name, part_find->disk_part_name))
+ part_find->found = 1;
+}
+
+/**
+ * Filter uids by the uids cached in cache.
+ * The intent is that only uids fully cached are returned, but that may not be
+ * what is achieved. An additional constraint is that this check should be
+ * cheap, so that going offline is not an expensive operation. Filtering all
+ * uids is inefficient in the first place; significant processing per uid
+ * makes synchronisation very expensive. At the suggestion of Srinivasa Ragavan
+ * (see http://bugzilla.gnome.org/show_bug.cgi?id=564339) the cache->parts hash
+ * table is consulted. If there is a parts-list in the hash table containing
+ * the part "", then we assume the message has been completely downloaded. This
+ * is incorrect (see http://bugzilla.gnome.org/show_bug.cgi?id=561211 for the
+ * symptoms). The code this replaces, a loop over all uids asking for the ""
+ * part of the message has the same flaw: it is no /less/ accurate to assess
+ * 'cached' in the manner this method does (assuming no concurrent process is
+ * removing messages from the cache).
+ *
+ * In the future, fixing bug 561211 needs a check for *all* the parts of a
+ * given uid. If the complete list of parts is available in the folder summary
+ * information then it can be done cheaply, otherwise some redesign will be
+ * needed.
+ */
+GPtrArray *
+camel_imap_message_cache_filter_cached(CamelImapMessageCache *cache, GPtrArray *uids, CamelException *ex)
+{
+ GPtrArray *result, *parts_list;
+ int i;
+ struct _part_find part_find;
+ /* Look for a part "" for each uid. */
+ result = g_ptr_array_sized_new(uids->len);
+ for (i = 0; i < uids->len; i++) {
+ if ((parts_list = g_hash_table_lookup(cache->parts, uids->pdata[i]))) {
+ /* At least one part locally present; look for "" (the
+ * HEADERS part can be present without anything else,
+ * and that part is not useful for users wanting to
+ * read the message).
+ */
+ part_find.found = 0;
+ part_find.disk_part_name = g_strdup_printf("%s" BASE_PART_SUFFIX,
+ (char *)uids->pdata[i]);
+ g_ptr_array_foreach(parts_list, _match_part, &part_find);
+ g_free(part_find.disk_part_name);
+ if (part_find.found)
+ /* The message is cached locally, do not
+ * include it in the result.
+ */
+ continue;
+ }
+ /* No message parts, or message part "" not found: include the
+ * uid in the result.
+ */
+ g_ptr_array_add(result, (char *)camel_pstring_strdup(uids->pdata[i]));
+ }
+ return result;
+}
Modified: branches/gnome-2-24/camel/providers/imap/camel-imap-message-cache.h
==============================================================================
--- branches/gnome-2-24/camel/providers/imap/camel-imap-message-cache.h (original)
+++ branches/gnome-2-24/camel/providers/imap/camel-imap-message-cache.h Mon Jan 12 03:46:33 2009
@@ -41,6 +41,13 @@
CamelObject parent_object;
char *path;
+ /* parts contains two sorts of objects.
+ * If the key contains '.' then it is a stream (also reverse-indexed in
+ * cached).
+ * Otherwise it is a g_ptr_array containing the subparts the message
+ * has. (e.g. 0., or 0.MIME.1).
+ */
+ /* cached contains streams for recently accessed messages */
GHashTable *parts, *cached;
guint32 max_uid;
};
@@ -98,6 +105,9 @@
CamelException *ex);
gboolean camel_imap_message_cache_delete (const char *path,
CamelException *ex);
+GPtrArray * camel_imap_message_cache_filter_cached(CamelImapMessageCache *,
+ GPtrArray *uids,
+ CamelException *ex);
/* Standard Camel function */
CamelType camel_imap_message_cache_get_type (void);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]