[geary/wip/721828-undo-2] Further work on undo, using timer and folder/app close to commit
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/721828-undo-2] Further work on undo, using timer and folder/app close to commit
- Date: Tue, 3 Feb 2015 01:26:50 +0000 (UTC)
commit aa0a383dc7330fb62a8b91c8c5e7eefa39042609
Author: Jim Nelson <jim yorba org>
Date: Mon Feb 2 17:26:28 2015 -0800
Further work on undo, using timer and folder/app close to commit
src/CMakeLists.txt | 1 +
src/client/application/geary-application.vala | 19 ++++++-
src/client/application/geary-controller.vala | 55 ++++++++++++++++++--
src/engine/api/geary-abstract-local-folder.vala | 13 ++++-
src/engine/api/geary-engine.vala | 14 +++--
src/engine/api/geary-folder.vala | 8 +++
src/engine/api/geary-revokable.vala | 39 ++++++++++++--
src/engine/app/app-conversation-monitor.vala | 7 ++-
.../imap-engine/imap-engine-minimal-folder.vala | 36 +++++++++++--
.../imap-engine/imap-engine-revokable-move.vala | 13 ++++-
.../replay-ops/imap-engine-move-email-commit.vala | 4 ++
.../replay-ops/imap-engine-move-email-prepare.vala | 7 ++-
.../replay-ops/imap-engine-move-email-revoke.vala | 13 ++++-
.../replay-ops/imap-engine-user-close.vala | 45 ++++++++++++++++
.../transport/imap-client-session-manager.vala | 13 ++++-
15 files changed, 251 insertions(+), 36 deletions(-)
---
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6a991b3..f1d7928 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -221,6 +221,7 @@ engine/imap-engine/replay-ops/imap-engine-replay-append.vala
engine/imap-engine/replay-ops/imap-engine-replay-disconnect.vala
engine/imap-engine/replay-ops/imap-engine-replay-removal.vala
engine/imap-engine/replay-ops/imap-engine-server-search-email.vala
+engine/imap-engine/replay-ops/imap-engine-user-close.vala
engine/imap-engine/yahoo/imap-engine-yahoo-account.vala
engine/imap-engine/yahoo/imap-engine-yahoo-folder.vala
diff --git a/src/client/application/geary-application.vala b/src/client/application/geary-application.vala
index f533254..5000ae6 100644
--- a/src/client/application/geary-application.vala
+++ b/src/client/application/geary-application.vala
@@ -59,7 +59,6 @@ public class GearyApplication : Gtk.Application {
* an exit, a callback should return true.
*/
public virtual signal bool exiting(bool panicked) {
- controller.close();
Date.terminate();
return true;
@@ -89,9 +88,9 @@ public class GearyApplication : Gtk.Application {
private string bin;
private File exec_dir;
-
private bool exiting_fired = false;
private int exitcode = 0;
+ private bool is_destroyed = false;
public GearyApplication() {
Object(application_id: APP_ID);
@@ -199,6 +198,17 @@ public class GearyApplication : Gtk.Application {
release();
}
+ private async void destroy_async() {
+ // see create_async() for reasoning hold/release is used
+ hold();
+
+ yield controller.close_async();
+
+ release();
+
+ is_destroyed = true;
+ }
+
public bool compose(string mailto) {
if (controller == null)
return false;
@@ -327,6 +337,11 @@ public class GearyApplication : Gtk.Application {
return;
}
+ // Give asynchronous destroy_async() a chance to complete
+ destroy_async.begin();
+ while (!is_destroyed || Gtk.events_pending())
+ Gtk.main_iteration();
+
if (Gtk.main_level() > 0)
Gtk.main_quit();
else
diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala
index 76f5fcf..fdbc192 100644
--- a/src/client/application/geary-controller.vala
+++ b/src/client/application/geary-controller.vala
@@ -256,13 +256,55 @@ public class GearyController : Geary.BaseObject {
}
/**
- * Stops the controller and shuts down Geary.
+ * At the moment, this is non-reversible, i.e. once closed a GearyController cannot be
+ * re-opened.
*/
- public void close() {
+ public async void close_async() {
+ // hide window while shutting down, as this can take a few seconds under certain conditions
+ main_window.hide();
+
+ // drop the Revokable, which will commit it if necessary
+ save_revokable(null, null);
+
+ try {
+ if (current_conversations != null) {
+ yield current_conversations.stop_monitoring_async(null);
+
+ // If not an Inbox, wait for it to close so all pending operations are flushed
+ if (!inboxes.values.contains(current_conversations.folder))
+ yield current_conversations.folder.wait_for_close_async(null);
+ }
+ } catch (Error err) {
+ message("Error closing conversation at shutdown: %s", err.message);
+ } finally {
+ current_conversations = null;
+ }
+
+ foreach (Geary.Folder inbox in inboxes.values) {
+ try {
+ // close and wait for all pending operations to be flushed
+ yield inbox.close_async(null);
+ yield inbox.wait_for_close_async(null);
+ } catch (Error err) {
+ message("Error closing Inbox %s at shutdown: %s", inbox.to_string(), err.message);
+ }
+ }
+
+ foreach (Geary.Account account in email_stores.keys) {
+ try {
+ yield account.close_async(null);
+ } catch (Error err) {
+ message("Error closing account %s at shutdown: %s", account.to_string(), err.message);
+ }
+ }
+
main_window.destroy();
- main_window = null;
- current_account = null;
- account_selected(null);
+
+ try {
+ yield Geary.Engine.instance.close_async(null);
+ } catch (Error err) {
+ message("Error closing Geary Engine instance: %s", err.message);
+ }
}
private void add_accelerator(string accelerator, string action) {
@@ -1283,6 +1325,9 @@ public class GearyController : Geary.BaseObject {
Cancellable? conversation_cancellable = (current_is_inbox ?
inbox_cancellables.get(folder.account) : cancellable_folder);
+ // clear Revokable, as Undo is only available while a folder is selected
+ save_revokable(null, null);
+
// stop monitoring for conversations and close the folder
if (current_conversations != null) {
yield current_conversations.stop_monitoring_async(null);
diff --git a/src/engine/api/geary-abstract-local-folder.vala b/src/engine/api/geary-abstract-local-folder.vala
index f78dcab..3b32a92 100644
--- a/src/engine/api/geary-abstract-local-folder.vala
+++ b/src/engine/api/geary-abstract-local-folder.vala
@@ -8,11 +8,12 @@
* Handles open/close for local folders.
*/
public abstract class Geary.AbstractLocalFolder : Geary.Folder {
- private int open_count = 0;
-
private ProgressMonitor _opening_monitor = new
Geary.ReentrantProgressMonitor(Geary.ProgressType.ACTIVITY);
public override Geary.ProgressMonitor opening_monitor { get { return _opening_monitor; } }
+ private int open_count = 0;
+ private Nonblocking.Semaphore closed_semaphore = new Nonblocking.Semaphore();
+
protected AbstractLocalFolder() {
}
@@ -39,6 +40,8 @@ public abstract class Geary.AbstractLocalFolder : Geary.Folder {
if (open_count++ > 0)
return false;
+ closed_semaphore.reset();
+
notify_opened(Geary.Folder.OpenState.LOCAL, properties.email_total);
return true;
@@ -48,8 +51,14 @@ public abstract class Geary.AbstractLocalFolder : Geary.Folder {
if (open_count == 0 || --open_count > 0)
return;
+ closed_semaphore.blind_notify();
+
notify_closed(Geary.Folder.CloseReason.LOCAL_CLOSE);
notify_closed(Geary.Folder.CloseReason.FOLDER_CLOSED);
}
+
+ public override async void wait_for_close_async(Cancellable? cancellable = null) throws Error {
+ yield closed_semaphore.wait_async(cancellable);
+ }
}
diff --git a/src/engine/api/geary-engine.vala b/src/engine/api/geary-engine.vala
index ae807e9..3d2c8b8 100644
--- a/src/engine/api/geary-engine.vala
+++ b/src/engine/api/geary-engine.vala
@@ -136,8 +136,7 @@ public class Geary.Engine : BaseObject {
* when necessary.
*/
public async void open_async(File user_data_dir, File resource_dir,
- Geary.CredentialsMediator? authentication_mediator,
- Cancellable? cancellable = null) throws Error {
+ Geary.CredentialsMediator? authentication_mediator, Cancellable? cancellable = null) throws Error {
// initialize *before* opening the Engine ... all initialize code should assume the Engine
// is closed
initialize_library();
@@ -202,16 +201,19 @@ public class Geary.Engine : BaseObject {
public async void close_async(Cancellable? cancellable = null) throws Error {
if (!is_open)
return;
-
- foreach(AccountInformation account in accounts.values)
+
+ Gee.Collection<AccountInformation> unavailable_accounts = accounts.values;
+ accounts.clear();
+
+ foreach(AccountInformation account in unavailable_accounts)
account_unavailable(account);
-
+
user_data_dir = null;
resource_dir = null;
authentication_mediator = null;
accounts = null;
account_instances = null;
-
+
is_open = false;
closed();
}
diff --git a/src/engine/api/geary-folder.vala b/src/engine/api/geary-folder.vala
index 8f76fb3..cba29de 100644
--- a/src/engine/api/geary-folder.vala
+++ b/src/engine/api/geary-folder.vala
@@ -457,6 +457,14 @@ public abstract class Geary.Folder : BaseObject {
public abstract async void close_async(Cancellable? cancellable = null) throws Error;
/**
+ * Wait for the Folder to fully close.
+ *
+ * Unlike { link wait_for_open_async}, this will ''always'' block until a { link Folder} is
+ * closed, even if it's not open.
+ */
+ public abstract async void wait_for_close_async(Cancellable? cancellable = null) throws Error;
+
+ /**
* Find the lowest- and highest-ordered { link EmailIdentifier}s in the
* folder, among the given set of EmailIdentifiers that may or may not be
* in the folder. If none of the given set are in the folder, return null.
diff --git a/src/engine/api/geary-revokable.vala b/src/engine/api/geary-revokable.vala
index dbf938d..68707d3 100644
--- a/src/engine/api/geary-revokable.vala
+++ b/src/engine/api/geary-revokable.vala
@@ -32,7 +32,22 @@ public abstract class Geary.Revokable : BaseObject {
*/
public bool in_process { get; protected set; default = false; }
- protected Revokable() {
+ private uint commit_timeout_id = 0;
+
+ /**
+ * Create a { link Revokable} with optional parameters.
+ *
+ * If commit_timeout_sec is nonzero, Revokable will automatically call { link commit_async}
+ * after the timeout expires if it is still { link valid}.
+ */
+ protected Revokable(int commit_timeout_sec = 0) {
+ if (commit_timeout_sec > 0)
+ commit_timeout_id = Timeout.add_seconds(commit_timeout_sec, on_commit);
+ }
+
+ ~Revokable() {
+ if (commit_timeout_id > 0)
+ Source.remove(commit_timeout_id);
}
/**
@@ -41,12 +56,16 @@ public abstract class Geary.Revokable : BaseObject {
* If the call throws an Error that does not necessarily mean the { link Revokable} is
* invalid. Check { link valid}.
*
- * @throws EngineError.ALREADY_OPEN if { link in_process} is true.
+ * @throws EngineError.ALREADY_OPEN if { link in_process} is true. EngineError.ALREADY_CLOSED
+ * if { link valid} is false.
*/
public virtual async void revoke_async(Cancellable? cancellable = null) throws Error {
if (in_process)
throw new EngineError.ALREADY_OPEN("Already revoking or committing operation");
+ if (!valid)
+ throw new EngineError.ALREADY_CLOSED("Revokable not valid");
+
in_process = true;
try {
yield internal_revoke_async(cancellable);
@@ -76,13 +95,16 @@ public abstract class Geary.Revokable : BaseObject {
* Even if the operation "actually" commits and is not delayed, calling commit_async() will
* make this Revokable invalid.
*
- * @throws EngineError.ALREADY_OPEN if { link is_revoking} or { link is_committing} is true
- * when called.
+ * @throws EngineError.ALREADY_OPEN if { link in_process} is true. EngineError.ALREADY_CLOSED
+ * if { link valid} is false.
*/
public virtual async void commit_async(Cancellable? cancellable = null) throws Error {
if (in_process)
throw new EngineError.ALREADY_OPEN("Already revoking or committing operation");
+ if (!valid)
+ throw new EngineError.ALREADY_CLOSED("Revokable not valid");
+
in_process = true;
try {
yield internal_commit_async(cancellable);
@@ -101,5 +123,14 @@ public abstract class Geary.Revokable : BaseObject {
* This call *must* set { link valid} before exiting.
*/
protected abstract async void internal_commit_async(Cancellable? cancellable) throws Error;
+
+ private bool on_commit() {
+ commit_timeout_id = 0;
+
+ if (valid && !in_process)
+ commit_async.begin();
+
+ return false;
+ }
}
diff --git a/src/engine/app/app-conversation-monitor.vala b/src/engine/app/app-conversation-monitor.vala
index db4373a..48943c1 100644
--- a/src/engine/app/app-conversation-monitor.vala
+++ b/src/engine/app/app-conversation-monitor.vala
@@ -631,7 +631,7 @@ public class Geary.App.ConversationMonitor : BaseObject {
}
internal async void remove_emails_async(Gee.Collection<Geary.EmailIdentifier> removed_ids) {
- debug("%d messages(s) removed to %s, trimming/removing conversations...", removed_ids.size,
+ debug("%d messages(s) removed from %s, trimming/removing conversations...", removed_ids.size,
folder.to_string());
Gee.Collection<Geary.App.Conversation> removed;
@@ -732,7 +732,10 @@ public class Geary.App.ConversationMonitor : BaseObject {
* Attempts to load enough conversations to fill min_window_count.
*/
internal async void fill_window_async(bool is_insert) {
- if (!is_monitoring || min_window_count <= conversations.size)
+ if (!is_monitoring)
+ return;
+
+ if (!is_insert && min_window_count <= conversations.size)
return;
int initial_message_count = conversations.get_email_count();
diff --git a/src/engine/imap-engine/imap-engine-minimal-folder.vala
b/src/engine/imap-engine/imap-engine-minimal-folder.vala
index 21d9c59..1e2985a 100644
--- a/src/engine/imap-engine/imap-engine-minimal-folder.vala
+++ b/src/engine/imap-engine/imap-engine-minimal-folder.vala
@@ -45,6 +45,7 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
private bool remote_opened = false;
private Nonblocking.ReportingSemaphore<bool> remote_semaphore =
new Nonblocking.ReportingSemaphore<bool>(false);
+ private Nonblocking.Semaphore closed_semaphore = new Nonblocking.Semaphore();
private ReplayQueue replay_queue;
private int remote_count = -1;
private uint open_remote_timer_id = 0;
@@ -537,6 +538,9 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
// reset to force waiting in wait_for_open_async()
remote_semaphore.reset();
+ // reset to force waiting in wait_for_close_async()
+ closed_semaphore.reset();
+
// Unless NO_DELAY is set, do NOT open the remote side here; wait for the ReplayQueue to
// require a remote connection or wait_for_open_async() to be called ... this allows for
// fast local-only operations to occur, local-only either because (a) the folder has all
@@ -758,6 +762,22 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
}
public override async void close_async(Cancellable? cancellable = null) throws Error {
+ // Check open_count but only decrement inside of replay queue
+ if (open_count <= 0)
+ return;
+
+ UserClose user_close = new UserClose(this, cancellable);
+ replay_queue.schedule(user_close);
+
+ yield user_close.wait_for_ready_async(cancellable);
+ }
+
+ public override async void wait_for_close_async(Cancellable? cancellable = null) throws Error {
+ yield closed_semaphore.wait_async(cancellable);
+ }
+
+ internal async void user_close_async(Cancellable? cancellable) {
+ // decrement open_count and, if zero, continue closing Folder
if (open_count == 0 || --open_count > 0)
return;
@@ -767,11 +787,9 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
// block anyone from wait_until_open_async(), as this is no longer open
remote_semaphore.reset();
- ReplayDisconnect disconnect_op = new ReplayDisconnect(this,
- Imap.ClientSession.DisconnectReason.REMOTE_CLOSE, true, cancellable);
- replay_queue.schedule(disconnect_op);
-
- yield disconnect_op.wait_for_ready_async(cancellable);
+ // don't yield here, close_internal_async() needs to be called outside of the replay queue
+ // the open_count protects against this path scheduling it more than once
+ close_internal_async.begin(CloseReason.LOCAL_CLOSE, CloseReason.REMOTE_CLOSE, true, cancellable);
}
// Close the remote connection and, if open_count is zero, the Folder itself. A Mutex is used
@@ -901,6 +919,10 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
notify_closed(CloseReason.FOLDER_CLOSED);
+ // If not closing in the background, do it here
+ if (closing_remote_folder == null)
+ closed_semaphore.blind_notify();
+
debug("Folder %s closed", to_string());
}
@@ -949,6 +971,10 @@ private class Geary.ImapEngine.MinimalFolder : Geary.Folder, Geary.FolderSupport
if (folder.open_count <= 0) {
debug("Not reestablishing connection to %s: closed", folder.to_string());
+ // need to do it here if not done in close_internal_locked_async()
+ if (remote_folder != null)
+ folder.closed_semaphore.blind_notify();
+
return;
}
diff --git a/src/engine/imap-engine/imap-engine-revokable-move.vala
b/src/engine/imap-engine/imap-engine-revokable-move.vala
index 3c5ce8d..b7c8add 100644
--- a/src/engine/imap-engine/imap-engine-revokable-move.vala
+++ b/src/engine/imap-engine/imap-engine-revokable-move.vala
@@ -5,6 +5,8 @@
*/
private class Geary.ImapEngine.RevokableMove : Revokable {
+ private const int REVOKE_TIMEOUT_SEC = 60;
+
private GenericAccount account;
private ImapEngine.MinimalFolder source;
private FolderPath destination;
@@ -12,6 +14,8 @@ private class Geary.ImapEngine.RevokableMove : Revokable {
public RevokableMove(GenericAccount account, ImapEngine.MinimalFolder source, FolderPath destination,
Gee.Set<ImapDB.EmailIdentifier> move_ids) {
+ base (REVOKE_TIMEOUT_SEC);
+
this.account = account;
this.source = source;
this.destination = destination;
@@ -73,7 +77,7 @@ private class Geary.ImapEngine.RevokableMove : Revokable {
}
private void on_source_email_removed(Gee.Collection<EmailIdentifier> ids) {
- // one-way switch, and only interested in destination folder activity
+ // one-way switch
if (!valid)
return;
@@ -84,8 +88,11 @@ private class Geary.ImapEngine.RevokableMove : Revokable {
}
private void on_source_closing(Gee.List<ReplayOperation> final_ops) {
- if (valid)
- final_ops.add(new MoveEmailCommit(source, move_ids, destination, null));
+ if (!valid)
+ return;
+
+ final_ops.add(new MoveEmailCommit(source, move_ids, destination, null));
+ valid = false;
}
}
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
b/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
index 2d46ae0..fb71550 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-move-email-commit.vala
@@ -4,6 +4,10 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
+/**
+ * Stage two of a { link RevokableMove}: move messages from folder to destination.
+ */
+
private class Geary.ImapEngine.MoveEmailCommit : Geary.ImapEngine.SendReplayOperation {
private MinimalFolder engine;
private Gee.List<ImapDB.EmailIdentifier> to_move = new Gee.ArrayList<ImapDB.EmailIdentifier>();
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-move-email-prepare.vala
b/src/engine/imap-engine/replay-ops/imap-engine-move-email-prepare.vala
index 7e0db69..58b4e4a 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-move-email-prepare.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-move-email-prepare.vala
@@ -4,6 +4,11 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
+/**
+ * Stage one of a { link RevokableMove}: collect valid { link ImapDB.EmailIdentifiers}, mark
+ * messages as removed, and update counts.
+ */
+
private class Geary.ImapEngine.MoveEmailPrepare : Geary.ImapEngine.SendReplayOperation {
public Gee.Set<ImapDB.EmailIdentifier>? prepared_for_move = null;
@@ -59,7 +64,7 @@ private class Geary.ImapEngine.MoveEmailPrepare : Geary.ImapEngine.SendReplayOpe
}
public override string describe_state() {
- return "%d email IDs".printf(prepared_for_move.size);
+ return "%d email IDs".printf(prepared_for_move != null ? prepared_for_move.size : 0);
}
}
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-move-email-revoke.vala
b/src/engine/imap-engine/replay-ops/imap-engine-move-email-revoke.vala
index 62df914..15de189 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-move-email-revoke.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-move-email-revoke.vala
@@ -4,6 +4,10 @@
* (version 2.1 or later). See the COPYING file in this distribution.
*/
+/**
+ * Revoked { link RevokableMove}: Unmark emails as removed and update counts.
+ */
+
private class Geary.ImapEngine.MoveEmailRevoke : Geary.ImapEngine.SendReplayOperation {
private MinimalFolder engine;
private Gee.List<ImapDB.EmailIdentifier> to_revoke = new Gee.ArrayList<ImapDB.EmailIdentifier>();
@@ -27,12 +31,15 @@ private class Geary.ImapEngine.MoveEmailRevoke : Geary.ImapEngine.SendReplayOper
if (to_revoke.size == 0)
return ReplayOperation.Status.COMPLETED;
- yield engine.local_folder.mark_removed_async(to_revoke, false, cancellable);
+ Gee.Set<ImapDB.EmailIdentifier>? revoked = yield engine.local_folder.mark_removed_async(
+ to_revoke, false, cancellable);
+ if (revoked == null || revoked.size == 0)
+ return ReplayOperation.Status.COMPLETED;
int count = engine.get_remote_counts(null, null);
- engine.replay_notify_email_inserted(to_revoke);
- engine.replay_notify_email_count_changed(count + to_revoke.size,
+ engine.replay_notify_email_inserted(revoked);
+ engine.replay_notify_email_count_changed(count + revoked.size,
Geary.Folder.CountChangeReason.INSERTED);
return ReplayOperation.Status.COMPLETED;
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-user-close.vala
b/src/engine/imap-engine/replay-ops/imap-engine-user-close.vala
new file mode 100644
index 0000000..14e279f
--- /dev/null
+++ b/src/engine/imap-engine/replay-ops/imap-engine-user-close.vala
@@ -0,0 +1,45 @@
+/* Copyright 2012-2014 Yorba Foundation
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+private class Geary.ImapEngine.UserClose : Geary.ImapEngine.ReplayOperation {
+ private MinimalFolder owner;
+ private Cancellable? cancellable;
+
+ public UserClose(MinimalFolder owner, Cancellable? cancellable) {
+ base ("UserClose", Scope.LOCAL_ONLY);
+
+ this.owner = owner;
+ this.cancellable = cancellable;
+ }
+
+ public override void notify_remote_removed_position(Imap.SequenceNumber removed) {
+ }
+
+ public override void notify_remote_removed_ids(Gee.Collection<ImapDB.EmailIdentifier> ids) {
+ }
+
+ public override void get_ids_to_be_remote_removed(Gee.Collection<ImapDB.EmailIdentifier> ids) {
+ }
+
+ public override async ReplayOperation.Status replay_local_async() throws Error {
+ yield owner.user_close_async(cancellable);
+
+ return ReplayOperation.Status.COMPLETED;
+ }
+
+ public override async void backout_local_async() throws Error {
+ }
+
+ public override async ReplayOperation.Status replay_remote_async() throws Error {
+ // should not be called
+ return ReplayOperation.Status.COMPLETED;
+ }
+
+ public override string describe_state() {
+ return "";
+ }
+}
+
diff --git a/src/engine/imap/transport/imap-client-session-manager.vala
b/src/engine/imap/transport/imap-client-session-manager.vala
index c84ccce..2f9f1de 100644
--- a/src/engine/imap/transport/imap-client-session-manager.vala
+++ b/src/engine/imap/transport/imap-client-session-manager.vala
@@ -341,7 +341,8 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
public async void release_session_async(ClientSession session, Cancellable? cancellable)
throws Error {
- check_open();
+ // Don't check_open(), it's valid for this to be called when is_open is false, that happens
+ // during mop-up
MailboxSpecifier? mailbox;
ClientSession.Context context = session.get_context(out mailbox);
@@ -383,9 +384,15 @@ public class Geary.Imap.ClientSessionManager : BaseObject {
assert_not_reached();
}
- if (unreserve) {
+ if (!unreserve)
+ return;
+
+ // if not open, disconnect, which will remove from the reserved pool anyway
+ if (!is_open) {
+ yield force_disconnect_async(session, true);
+ } else {
try {
- // don't respect Cancellable because this *must* happen; don't want this lingering
+ // don't respect Cancellable because this *must* happen; don't want this lingering
// on the reserved list forever
int token = yield sessions_mutex.claim_async();
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]