diff --git a/src/engine/imap-engine/imap-engine-generic-folder.vala b/src/engine/imap-engine/imap-engine-generic-folder.vala index 240765c9..d08d19a3 100644 --- a/src/engine/imap-engine/imap-engine-generic-folder.vala +++ b/src/engine/imap-engine/imap-engine-generic-folder.vala @@ -474,8 +474,15 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde debug("Unable to fire semaphore notifying remote folder ready/not ready: %s", notify_err.message); - remote_folder = null; - remote_count = -1; + // do this now rather than wait for close_internal_async() to execute to ensure that + // any replay operations already queued don't attempt to run + try { + clear_remote_folder(); + } catch (Error err) { + debug("Unable to clear and signal remote folder due to failed open: %s", err.message); + + // fall through + } notify_open_failed(Geary.Folder.OpenFailed.REMOTE_FAILED, notify_err); @@ -506,11 +513,8 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde // Notify all callers waiting for the remote folder that it's not coming available Imap.Folder? closing_remote_folder = remote_folder; - remote_folder = null; - remote_count = -1; - try { - remote_semaphore.notify(); + clear_remote_folder(); } catch (Error err) { debug("close_internal_async: Unable to fire remote semaphore: %s", err.message); } @@ -563,6 +567,14 @@ private class Geary.ImapEngine.GenericFolder : Geary.AbstractFolder, Geary.Folde debug("Folder %s closed", to_string()); } + private void clear_remote_folder() throws Error { + remote_folder = null; + remote_count = -1; + + remote_semaphore.reset(); + remote_semaphore.notify_result(false, null); + } + private void on_remote_messages_appended(int total) { debug("on_remote_messages_appended: total=%d", total); replay_queue.schedule(new ReplayAppend(this, total)); diff --git a/src/engine/imap-engine/imap-engine-replay-queue.vala b/src/engine/imap-engine/imap-engine-replay-queue.vala index ee4a1e7b..d7206af8 100644 --- a/src/engine/imap-engine/imap-engine-replay-queue.vala +++ b/src/engine/imap-engine/imap-engine-replay-queue.vala @@ -108,6 +108,13 @@ private class Geary.ImapEngine.ReplayQueue { Logging.debug(Logging.Flag.REPLAY, "[%s] ReplayQueue::closed", to_string()); } + /** + * ReplayQueue accepts a NonblockingReportingSemaphore which, when signaled, returns + * true if the remote folder is ready and open, false if not (closing or closed), and + * throws an Error if the semaphore has failed. ReplayQueue will wait on this semaphore for + * each ReplayOperation waiting to perform a remote operation, cancelling it if the remote + * folder is not ready. + */ public ReplayQueue(string name, NonblockingReportingSemaphore remote_reporting_semaphore) { this.name = name; this.remote_reporting_semaphore = remote_reporting_semaphore; @@ -293,21 +300,9 @@ private class Geary.ImapEngine.ReplayQueue { } private async void do_replay_remote_async() { - try { - if (!yield remote_reporting_semaphore.wait_for_result_async()) { - debug("Folder %s failed to open, remote replay queue closing", to_string()); - - return; - } - } catch (Error remote_err) { - debug("Error for remote queue waiting for remote %s to open, remote queue closing: %s", to_string(), - remote_err.message); - - return; - } - bool queue_running = true; while (queue_running) { + // wait for the next operation ... do this *before* waiting for remote ReplayOperation op; try { op = yield remote_queue.recv_async(); @@ -318,6 +313,20 @@ private class Geary.ImapEngine.ReplayQueue { break; } + // wait until the remote folder is opened (or returns false, in which case closed) + bool folder_opened = false; + try { + if (yield remote_reporting_semaphore.wait_for_result_async()) + folder_opened = true; + else + debug("Folder %s closed or failed to open, remote replay queue closing", to_string()); + } catch (Error remote_err) { + debug("Error for remote queue waiting for remote %s to open, remote queue closing: %s", to_string(), + remote_err.message); + + // fall through + } + if (op is ReplayClose) queue_running = false; @@ -325,13 +334,17 @@ private class Geary.ImapEngine.ReplayQueue { ReplayOperation.Status status = ReplayOperation.Status.FAILED; Error? remote_err = null; - try { - status = yield op.replay_remote_async(); - } catch (Error replay_err) { - debug("Replay remote error for %s on %s: %s", op.to_string(), to_string(), - replay_err.message); - - remote_err = replay_err; + if (folder_opened) { + try { + status = yield op.replay_remote_async(); + } catch (Error replay_err) { + debug("Replay remote error for %s on %s: %s", op.to_string(), to_string(), + replay_err.message); + + remote_err = replay_err; + } + } else { + remote_err = new EngineError.SERVER_UNAVAILABLE("Folder %s not available", to_string()); } bool has_failed = (status == ReplayOperation.Status.FAILED); diff --git a/src/engine/nonblocking/nonblocking-abstract-semaphore.vala b/src/engine/nonblocking/nonblocking-abstract-semaphore.vala index cb01352f..5dd12a80 100644 --- a/src/engine/nonblocking/nonblocking-abstract-semaphore.vala +++ b/src/engine/nonblocking/nonblocking-abstract-semaphore.vala @@ -138,6 +138,9 @@ public abstract class Geary.NonblockingAbstractSemaphore { } public virtual void reset() { + if (!passed) + return; + passed = false; notify_at_reset();