engine: Cancel any remote folders update before marking a message

When marking a message, a race condition can happen:
- A remote folders update is already running
- We mark the message locally and replay it remotely
- The previous remote update result restores invalid values locally
This commit is contained in:
Cédric Bellegarde 2022-09-22 11:49:05 +02:00
parent f45afac091
commit 1a7a3a987a
5 changed files with 43 additions and 0 deletions

View file

@ -2002,6 +2002,8 @@ private class Application.MarkEmailCommand : TrivialCommand, EmailCommand {
public override async void execute(GLib.Cancellable? cancellable)
throws GLib.Error {
this.location.account.cancel_remote_update();
yield this.store.mark_email_async(
this.email, this.to_add, this.to_remove, cancellable
);

View file

@ -309,6 +309,11 @@ public abstract class Geary.Account : BaseObject, Logging.Source {
*/
public abstract bool is_open();
/**
* Cancel any running/pending remote update for this {@link Account}.
*/
public abstract void cancel_remote_update();
/**
* Rebuild the local data stores for this {@link Account}.
*

View file

@ -78,6 +78,26 @@ internal class Geary.ImapEngine.AccountProcessor :
this.queue.revoke(op);
}
// Revokes all operations with the given type; returns true if at least one
// operation with the given type was found
public bool dequeue_by_type(GLib.Type type) {
bool found = false;
if (this.current_op != null &&
this.current_op.get_type() == type &&
this.op_cancellable != null) {
this.op_cancellable.cancel();
this.op_cancellable = null;
found = true;
}
this.queue.revoke_matching((op) => {
if (op.get_type() == type) {
found = true;
}
return op.get_type() == type;
});
return found;
}
public void stop() {
this.is_running = false;
if (this.op_cancellable != null) {

View file

@ -229,6 +229,15 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
return open;
}
/** {@inheritDoc} */
public override void cancel_remote_update() {
// Cancel and update again
if (this.processor.dequeue_by_type(typeof(UpdateRemoteFolders))) {
debug("Cancelled a remote update! Updating again...\n");
update_remote_folders(false);
}
}
public override async void rebuild_async(GLib.Cancellable? cancellable = null)
throws GLib.Error {
if (this.open) {

View file

@ -58,6 +58,13 @@ public class Mock.Account : Geary.Account,
}
}
public override void cancel_remote_update() {
try {
void_call("cancel_remote_update", {});
} catch (GLib.Error err) {
}
}
public override async void rebuild_async(GLib.Cancellable? cancellable = null)
throws GLib.Error {
void_call("rebuild_async", { cancellable });