Geary.Imap.Command: Improve cancelled-before send handling

Add `cancelled_before_send` method to support explicitly cancelling the
command before it was sent, so that `wait_until_complete` does not end
up waiting forever when this happens. Call the new method from
`ClientConnection` as required.
This commit is contained in:
Michael Gratton 2020-09-02 11:07:58 +10:00 committed by Michael James Gratton
parent 6c0607d809
commit a1b0547e67
2 changed files with 30 additions and 12 deletions

View file

@ -92,7 +92,7 @@ public abstract class Geary.Imap.Command : BaseObject {
private Geary.Nonblocking.Semaphore complete_lock =
new Geary.Nonblocking.Semaphore();
private ImapError? cancelled_cause = null;
private GLib.Error? cancelled_cause = null;
private Geary.Nonblocking.Spinlock? literal_spinlock = null;
private GLib.Cancellable? literal_cancellable = null;
@ -277,6 +277,16 @@ public abstract class Geary.Imap.Command : BaseObject {
throw this.cancelled_cause;
}
// If everything above is fine, but sending was cancelled, it
// must have been cancelled after being sent. Throw an error
// indicating this specifically.
if (this.should_send != null &&
this.should_send.is_cancelled()) {
throw new GLib.IOError.CANCELLED(
"Command was cancelled after sending: %s", to_brief_string()
);
}
check_has_status();
// Since this is part of the public API, perform a strict
@ -288,15 +298,6 @@ public abstract class Geary.Imap.Command : BaseObject {
this.status.to_string()
);
}
// If everything else looks fine, but sending was cancelled,
// throw an error here so the caller knows that was the case.
if (this.should_send != null &&
this.should_send.is_cancelled()) {
throw new GLib.IOError.CANCELLED(
"Sent command was cancelled: %s", to_brief_string()
);
}
}
public virtual string to_string() {
@ -332,12 +333,27 @@ public abstract class Geary.Imap.Command : BaseObject {
}
/**
* Cancels this command due to a network or server disconnect.
* Marks this command as being cancelled before being sent.
*
* When this method is called, all locks will be released,
* including {@link wait_until_complete}, which will then throw a
* `GLib.IOError.CANCELLED` error.
*/
internal virtual void cancelled_before_send() {
cancel(
new GLib.IOError.CANCELLED(
"Command was cancelled before sending: %s", to_brief_string()
)
);
}
/**
* Cancels this command due to a network or server disconnect.
*
* When this method is called, all locks will be released,
* including {@link wait_until_complete}, which will then throw a
* `ImapError.NOT_CONNECTED` error.
*/
internal virtual void disconnected(string reason) {
cancel(new ImapError.NOT_CONNECTED("%s: %s", to_brief_string(), reason));
}
@ -406,7 +422,7 @@ public abstract class Geary.Imap.Command : BaseObject {
}
}
private void cancel(ImapError cause) {
private void cancel(GLib.Error cause) {
stop_serialisation();
this.cancelled_cause = cause;
this.response_timer.reset();

View file

@ -265,6 +265,7 @@ public class Geary.Imap.ClientConnection : BaseObject, Logging.Source {
check_connection();
if (new_command.should_send != null &&
new_command.should_send.is_cancelled()) {
new_command.cancelled_before_send();
throw new GLib.IOError.CANCELLED(
"Not queuing command, sending is cancelled: %s",
new_command.to_brief_string()
@ -437,6 +438,7 @@ public class Geary.Imap.ClientConnection : BaseObject, Logging.Source {
throws GLib.Error {
if (command.should_send != null &&
command.should_send.is_cancelled()) {
command.cancelled_before_send();
throw new GLib.IOError.CANCELLED(
"Not sending command, sending is cancelled: %s",
command.to_brief_string()