aboutsummaryrefslogtreecommitdiffstats
path: root/worker/imap
diff options
context:
space:
mode:
authorKoni Marti <koni.marti@gmail.com>2024-02-25 12:43:55 +0100
committerRobin Jarry <robin@jarry.cc>2024-02-26 09:26:45 +0100
commitd8d5fc8d31f358c6425dfa18a9f8d2767bc6df40 (patch)
treefad404f8451c9d44cb8e9ac61fae2b70817bd2fc /worker/imap
parent83c3a4051b146651c9e56924bf8e4a9c8b01c5a8 (diff)
downloadaerc-d8d5fc8d31f358c6425dfa18a9f8d2767bc6df40.tar.gz
imap: drain updates channel when deleting messages
Drain the buffered updates channel when deleting messages to prevent a backend freeze. Unilateral update messages from the IMAP server can fill up the buffered channel if not handled during a large operation (more than ~50 messages). Link: https://lists.sr.ht/~rjarry/aerc-discuss/%3CCZCPBTWI3PIW.T8MWNCBG7FGL%40disroot.org%3E Changelog-fixed: Prevent a freeze for large-scale deletions with IMAP. Signed-off-by: Koni Marti <koni.marti@gmail.com> Tested-by: Jeremy Baxter <jtbx@disroot.org> Acked-by: Robin Jarry <robin@jarry.cc>
Diffstat (limited to 'worker/imap')
-rw-r--r--worker/imap/flags.go40
1 files changed, 40 insertions, 0 deletions
diff --git a/worker/imap/flags.go b/worker/imap/flags.go
index 06680818..60137bd3 100644
--- a/worker/imap/flags.go
+++ b/worker/imap/flags.go
@@ -2,13 +2,53 @@ package imap
import (
"github.com/emersion/go-imap"
+ "github.com/emersion/go-imap/client"
"git.sr.ht/~rjarry/aerc/lib/log"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/types"
)
+// drainUpdates will drain the updates channel. For some operations, the imap
+// server will send unilateral messages. If they arrive while another operation
+// is in progress, the buffered updates channel can fill up and cause a freeze
+// of the entire backend. Avoid this by draining the updates channel and only
+// process the Message and Expunge updates.
+//
+// To stop the draining, close the returned struct.
+func (imapw *IMAPWorker) drainUpdates() *drainCloser {
+ done := make(chan struct{})
+ go func() {
+ defer log.PanicHandler()
+ for {
+ select {
+ case update := <-imapw.updates:
+ switch update.(type) {
+ case *client.MessageUpdate,
+ *client.ExpungeUpdate:
+ imapw.handleImapUpdate(update)
+ }
+ case <-done:
+ return
+ }
+ }
+ }()
+ return &drainCloser{done}
+}
+
+type drainCloser struct {
+ done chan struct{}
+}
+
+func (d *drainCloser) Close() error {
+ close(d.done)
+ return nil
+}
+
func (imapw *IMAPWorker) handleDeleteMessages(msg *types.DeleteMessages) {
+ drain := imapw.drainUpdates()
+ defer drain.Close()
+
item := imap.FormatFlagsOp(imap.AddFlags, true)
flags := []interface{}{imap.DeletedFlag}
uids := toSeqSet(msg.Uids)