[geary/wip/713432-startup] A grab-bag of changes to speed up startup time
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/713432-startup] A grab-bag of changes to speed up startup time
- Date: Thu, 19 Dec 2013 02:36:43 +0000 (UTC)
commit c9a504c2faec4018321bdf15de2f48100e1e2c3c
Author: Jim Nelson <jim yorba org>
Date: Wed Dec 18 18:36:12 2013 -0800
A grab-bag of changes to speed up startup time
Some of these will improve performance in general, but almost all
would be most notable at application start.
src/client/application/geary-controller.vala | 20 ++++++--
src/engine/api/geary-folder.vala | 6 ++-
src/engine/imap-db/imap-db-account.vala | 6 ++-
.../imap-engine/imap-engine-generic-account.vala | 52 ++++++++++++++++----
.../imap-engine/imap-engine-generic-folder.vala | 16 +++++--
src/engine/imap/api/imap-account.vala | 49 ++++++++++++++-----
src/engine/imap/api/imap-folder.vala | 4 +-
.../imap/response/imap-mailbox-information.vala | 9 +++
.../imap/transport/imap-client-connection.vala | 2 +-
9 files changed, 127 insertions(+), 37 deletions(-)
---
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 1cbed51..3f11261 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -89,6 +89,7 @@ public class GearyController : Geary.BaseObject {
private UnityLauncher? unity_launcher = null;
private Libnotify? libnotify = null;
private uint select_folder_timeout_id = 0;
+ private time_t next_folder_select_allowed_time = 0;
private Geary.Folder? folder_to_select = null;
private Geary.Nonblocking.Mutex select_folder_mutex = new Geary.Nonblocking.Mutex();
private Geary.Account? account_to_select = null;
@@ -932,14 +933,23 @@ public class GearyController : Geary.BaseObject {
return;
}
+ folder_to_select = folder;
+
// To prevent the user from selecting folders too quickly, we actually
// schedule the action to happen after a timeout instead of acting
// directly. If the user selects another folder during the timeout,
// we nix the original timeout and start a new one.
- if (select_folder_timeout_id != 0)
- Source.remove(select_folder_timeout_id);
- folder_to_select = folder;
- select_folder_timeout_id = Timeout.add(SELECT_FOLDER_TIMEOUT_MSEC, on_select_folder_timeout);
+ time_t now = time_t();
+ long diff = now - next_folder_select_allowed_time;
+ if (diff < SELECT_FOLDER_TIMEOUT_MSEC) {
+ if (select_folder_timeout_id == 0)
+ select_folder_timeout_id = Timeout.add((uint) diff, on_select_folder_timeout);
+ } else {
+ do_select_folder.begin(folder_to_select, on_select_folder_completed);
+ folder_to_select = null;
+
+ next_folder_select_allowed_time = now + SELECT_FOLDER_TIMEOUT_MSEC;
+ }
}
private bool on_select_folder_timeout() {
@@ -1013,7 +1023,7 @@ public class GearyController : Geary.BaseObject {
update_ui();
- current_conversations = new Geary.App.ConversationMonitor(current_folder,
Geary.Folder.OpenFlags.NONE,
+ current_conversations = new Geary.App.ConversationMonitor(current_folder,
Geary.Folder.OpenFlags.NO_DELAY,
ConversationListStore.REQUIRED_FIELDS, MIN_CONVERSATION_COUNT);
if (inboxes.values.contains(current_folder)) {
diff --git a/src/engine/api/geary-folder.vala b/src/engine/api/geary-folder.vala
index b7ad434..e733636 100644
--- a/src/engine/api/geary-folder.vala
+++ b/src/engine/api/geary-folder.vala
@@ -67,7 +67,11 @@ public interface Geary.Folder : BaseObject {
* the messages (such as their flags or other metadata) may not be up-to-date
* when the folder opens. Not all folders will support this flag.
*/
- FAST_OPEN;
+ FAST_OPEN,
+ /**
+ * Do not delay opening a connection to the server.
+ */
+ NO_DELAY;
public bool is_any_set(OpenFlags flags) {
return (this & flags) != 0;
diff --git a/src/engine/imap-db/imap-db-account.vala b/src/engine/imap-db/imap-db-account.vala
index 3c0407f..dab11e9 100644
--- a/src/engine/imap-db/imap-db-account.vala
+++ b/src/engine/imap-db/imap-db-account.vala
@@ -133,7 +133,11 @@ private class Geary.ImapDB.Account : BaseObject {
background_cancellable = new Cancellable();
// Kick off a background update of the search table.
- populate_search_table_async.begin(background_cancellable);
+ Timeout.add_seconds(30, () => {
+ populate_search_table_async.begin(background_cancellable);
+
+ return false;
+ });
initialize_contacts(cancellable);
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala
b/src/engine/imap-engine/imap-engine-generic-account.vala
index d26aef2..0e8aaaf 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -301,28 +301,60 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.AbstractAccount {
reschedule_folder_refresh(false);
}
+ private class EnumerateLocalOperation : Nonblocking.BatchOperation {
+ public weak GenericAccount owner;
+ public Gee.HashMap<FolderPath, ImapDB.Folder>? local_folders = null;
+
+ public EnumerateLocalOperation(GenericAccount owner) {
+ this.owner = owner;
+ }
+
+ public override async Object? execute_async(Cancellable? cancellable) throws Error {
+ local_folders = yield owner.enumerate_local_folders_async(null, cancellable);
+
+ return null;
+ }
+ }
+
+ private class EnumerateRemoteOperation : Nonblocking.BatchOperation {
+ public weak GenericAccount owner;
+ public Gee.HashMap<FolderPath, Imap.Folder>? remote_folders = null;
+
+ public EnumerateRemoteOperation(GenericAccount owner) {
+ this.owner = owner;
+ }
+
+ public override async Object? execute_async(Cancellable? cancellable) throws Error {
+ remote_folders = yield owner.enumerate_remote_folders_async(null, cancellable);
+
+ return null;
+ }
+ }
+
private async void enumerate_folders_async(Cancellable? cancellable) throws Error {
check_open();
- // get all local folders
- Gee.HashMap<FolderPath, ImapDB.Folder> local_children = yield enumerate_local_folders_async(null,
- cancellable);
+ EnumerateLocalOperation local_op = new EnumerateLocalOperation(this);
+ EnumerateRemoteOperation remote_op = new EnumerateRemoteOperation(this);
+
+ Nonblocking.Batch batch = new Nonblocking.Batch();
+ batch.add(local_op);
+ batch.add(remote_op);
+
+ yield batch.execute_all_async(cancellable);
+ batch.throw_first_exception();
// convert to a list of Geary.Folder ... build_folder() also reports new folders, so this
// gets the word out quickly
Gee.Collection<Geary.Folder> existing_list = new Gee.ArrayList<Geary.Folder>();
- existing_list.add_all(build_folders(local_children.values));
+ existing_list.add_all(build_folders(local_op.local_folders.values));
existing_list.add_all(local_only.values);
Gee.HashMap<FolderPath, Geary.Folder> existing_folders
= Geary.traverse<Geary.Folder>(existing_list).to_hash_map<FolderPath>(f => f.path);
- // get all remote (server) folder paths
- Gee.HashMap<FolderPath, Imap.Folder> remote_folders = yield enumerate_remote_folders_async(null,
- cancellable);
-
- // combine the two and make sure everything is up-to-date
- yield update_folders_async(existing_folders, remote_folders, cancellable);
+ // pair the local and remote folders and make sure everything is up-to-date
+ yield update_folders_async(existing_folders, remote_op.remote_folders, cancellable);
}
private async Gee.HashMap<FolderPath, ImapDB.Folder> enumerate_local_folders_async(
diff --git a/src/engine/imap-engine/imap-engine-generic-folder.vala
b/src/engine/imap-engine/imap-engine-generic-folder.vala
index 026a1f4..461df5e 100644
--- a/src/engine/imap-engine/imap-engine-generic-folder.vala
+++ b/src/engine/imap-engine/imap-engine-generic-folder.vala
@@ -437,6 +437,11 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
public override async bool open_async(Geary.Folder.OpenFlags open_flags, Cancellable? cancellable = null)
throws Error {
if (open_count++ > 0) {
+ if (open_flags.is_all_set(OpenFlags.NO_DELAY)) {
+ cancel_remote_open_timer();
+ wait_for_open_async.begin();
+ }
+
debug("Not opening %s: already open (open_count=%d)", to_string(), open_count);
return false;
@@ -444,12 +449,12 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
this.open_flags = open_flags;
- open_internal(cancellable);
+ open_internal(open_flags, cancellable);
return true;
}
- private void open_internal(Cancellable? cancellable) {
+ private void open_internal(Folder.OpenFlags open_flags, Cancellable? cancellable) {
remote_semaphore = new Geary.Nonblocking.ReportingSemaphore<bool>(false);
// start the replay queue
@@ -466,7 +471,10 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
// second account Inbox they don't manipulate), no remote connection will ever be made,
// meaning that folder normalization never happens and unsolicited notifications never
// arrive
- start_remote_open_timer();
+ if (open_flags.is_all_set(OpenFlags.NO_DELAY))
+ wait_for_open_async.begin();
+ else
+ start_remote_open_timer();
}
private void start_remote_open_timer() {
@@ -649,7 +657,7 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde
if (remote_reason.is_error()) {
debug("Reestablishing broken connect to %s", to_string());
- open_internal(null);
+ open_internal(OpenFlags.NO_DELAY, null);
return;
}
diff --git a/src/engine/imap/api/imap-account.vala b/src/engine/imap/api/imap-account.vala
index 8d71405..0cd39c4 100644
--- a/src/engine/imap/api/imap-account.vala
+++ b/src/engine/imap/api/imap-account.vala
@@ -12,9 +12,10 @@
* that a Geary.Account implementation would need (in particular, { link Geary.ImapEngine.Account}
* and makes them into simple async calls.
*
- * Geary.Imap.Account does __no__ management of the { link Imap.Folder} objects it returns. Thus,
- * calling a fetch or list operation several times in a row will return separate Folder objects
- * each time. It is up to the higher layers of the stack to manage these objects.
+ * Geary.Imap.Account manages the { link Imap.Folder} objects it returns, but only in the sense
+ * that it will not create new instances repeatedly. Otherwise, it does not refresh or update the
+ * Imap.Folders themselves (such as update their { link Imap.StatusData} periodically).
+ * That's the responsibility of the higher layers of the stack.
*/
private class Geary.Imap.Account : BaseObject {
@@ -28,6 +29,7 @@ private class Geary.Imap.Account : BaseObject {
private Nonblocking.Mutex cmd_mutex = new Nonblocking.Mutex();
private Gee.HashMap<FolderPath, MailboxInformation> path_to_mailbox = new Gee.HashMap<
FolderPath, MailboxInformation>();
+ private Gee.HashMap<FolderPath, Imap.Folder> folders = new Gee.HashMap<FolderPath, Imap.Folder>();
private Gee.List<MailboxInformation>? list_collector = null;
private Gee.List<StatusData>? status_collector = null;
@@ -143,28 +145,38 @@ private class Geary.Imap.Account : BaseObject {
}
public async bool folder_exists_async(FolderPath path, Cancellable? cancellable) throws Error {
- return path_to_mailbox.contains(path);
+ return path_to_mailbox.has_key(path);
}
public async Imap.Folder fetch_folder_async(FolderPath path, Cancellable? cancellable)
throws Error {
check_open();
+ if (folders.has_key(path))
+ return folders.get(path);
+
// if not in map, use list_children_async to add it (if it exists)
- if (!path_to_mailbox.contains(path))
+ if (!path_to_mailbox.has_key(path)) {
+ debug("Listing children to find %s", path.to_string());
yield list_children_async(path.get_parent(), cancellable);
+ }
MailboxInformation? mailbox_info = path_to_mailbox.get(path);
if (mailbox_info == null)
throw_not_found(path);
+ Imap.Folder folder;
if (!mailbox_info.attrs.contains(MailboxAttribute.NO_SELECT)) {
StatusData status = yield fetch_status_async(path, cancellable);
- return new Imap.Folder(session_mgr, status, mailbox_info);
+ folder = new Imap.Folder(session_mgr, status, mailbox_info);
} else {
- return new Imap.Folder.unselectable(session_mgr, mailbox_info);
+ folder = new Imap.Folder.unselectable(session_mgr, mailbox_info);
}
+
+ folders.set(path, folder);
+
+ return folder;
}
private async StatusData fetch_status_async(FolderPath path, Cancellable? cancellable)
@@ -211,8 +223,18 @@ private class Geary.Imap.Account : BaseObject {
Gee.Map<StatusCommand, MailboxSpecifier> cmd_map = new Gee.HashMap<
StatusCommand, MailboxSpecifier>();
foreach (MailboxInformation mailbox_info in child_info) {
+ // if already have an Imap.Folder for this mailbox, use that
+ if (folders.has_key(mailbox_info.path)) {
+ child_folders.add(folders.get(mailbox_info.path));
+
+ continue;
+ }
+
+ // if new mailbox is unselectable, don't bother doing a STATUS command
if (mailbox_info.attrs.contains(MailboxAttribute.NO_SELECT)) {
- child_folders.add(new Imap.Folder.unselectable(session_mgr, mailbox_info));
+ Imap.Folder folder = new Imap.Folder.unselectable(session_mgr, mailbox_info);
+ folders.set(folder.path, folder);
+ child_folders.add(folder);
continue;
}
@@ -255,7 +277,10 @@ private class Geary.Imap.Account : BaseObject {
}
status_results.remove(found_status);
- child_folders.add(new Imap.Folder(session_mgr, found_status, mailbox_info));
+
+ Imap.Folder folder = new Imap.Folder(session_mgr, found_status, mailbox_info);
+ folders.set(folder.path, folder);
+ child_folders.add(folder);
}
if (status_results.size > 0)
@@ -307,10 +332,8 @@ private class Geary.Imap.Account : BaseObject {
// stash all MailboxInformation by path
// TODO: remove any MailboxInformation for this parent that is not found (i.e. has been
// removed on the server)
- foreach (MailboxInformation mailbox_info in list_results) {
- FolderPath path = mailbox_info.mailbox.to_folder_path(mailbox_info.delim);
- path_to_mailbox.set(path, mailbox_info);
- }
+ foreach (MailboxInformation mailbox_info in list_results)
+ path_to_mailbox.set(mailbox_info.path, mailbox_info);
return (list_results.size > 0) ? list_results : null;
}
diff --git a/src/engine/imap/api/imap-folder.vala b/src/engine/imap/api/imap-folder.vala
index 87eae1d..a38dc75 100644
--- a/src/engine/imap/api/imap-folder.vala
+++ b/src/engine/imap/api/imap-folder.vala
@@ -65,7 +65,7 @@ private class Geary.Imap.Folder : BaseObject {
this.session_mgr = session_mgr;
this.info = info;
- path = info.mailbox.to_folder_path(info.delim);
+ path = info.path;
properties = new Imap.FolderProperties.status(status, info.attrs);
}
@@ -73,7 +73,7 @@ private class Geary.Imap.Folder : BaseObject {
internal Folder.unselectable(ClientSessionManager session_mgr, MailboxInformation info) {
this.session_mgr = session_mgr;
this.info = info;
- path = info.mailbox.to_folder_path(info.delim);
+ path = info.path;
properties = new Imap.FolderProperties(0, 0, 0, null, null, info.attrs);
}
diff --git a/src/engine/imap/response/imap-mailbox-information.vala
b/src/engine/imap/response/imap-mailbox-information.vala
index 1e6520b..f29294e 100644
--- a/src/engine/imap/response/imap-mailbox-information.vala
+++ b/src/engine/imap/response/imap-mailbox-information.vala
@@ -30,10 +30,19 @@ public class Geary.Imap.MailboxInformation : Object {
*/
public MailboxAttributes attrs { get; private set; }
+ /**
+ * The { link Geary.FolderPath} for the mailbox.
+ *
+ * This is constructed from the supplied { link mailbox} and { link delim} returned from the
+ * server.
+ */
+ public Geary.FolderPath path { get; private set; }
+
public MailboxInformation(MailboxSpecifier mailbox, string? delim, MailboxAttributes attrs) {
this.mailbox = mailbox;
this.delim = delim;
this.attrs = attrs;
+ path = mailbox.to_folder_path(delim);
}
/**
diff --git a/src/engine/imap/transport/imap-client-connection.vala
b/src/engine/imap/transport/imap-client-connection.vala
index 433406c..6b91369 100644
--- a/src/engine/imap/transport/imap-client-connection.vala
+++ b/src/engine/imap/transport/imap-client-connection.vala
@@ -30,7 +30,7 @@ public class Geary.Imap.ClientConnection : BaseObject {
*/
public const uint DEFAULT_COMMAND_TIMEOUT_SEC = 15;
- private const int FLUSH_TIMEOUT_MSEC = 100;
+ private const int FLUSH_TIMEOUT_MSEC = 10;
private enum State {
UNCONNECTED,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]