From 6911562cf4214531343b7509afe77e38b28a0801 Mon Sep 17 00:00:00 2001 From: René 'Necoro' Neumann Date: Thu, 23 Apr 2020 23:47:05 +0200 Subject: Fix concurrent access to the same folder --- internal/imap/connection.go | 7 +++++++ internal/imap/mailboxes.go | 41 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 5 deletions(-) (limited to 'internal/imap') diff --git a/internal/imap/connection.go b/internal/imap/connection.go index 88b1496..358445b 100644 --- a/internal/imap/connection.go +++ b/internal/imap/connection.go @@ -99,6 +99,13 @@ func (conn *connection) ensureFolder(folder Folder) error { return nil } + if conn.mailboxes.locking(folder) { + // someone else tried to create the MB -- try again, now that he's done + return conn.ensureFolder(folder) + } else { + defer conn.mailboxes.unlocking(folder) + } + log.Printf("Checking for folder '%s'", folder) mbox, found, err := conn.list(folder.str) diff --git a/internal/imap/mailboxes.go b/internal/imap/mailboxes.go index d0fdede..f1dc6c6 100644 --- a/internal/imap/mailboxes.go +++ b/internal/imap/mailboxes.go @@ -7,8 +7,38 @@ import ( ) type mailboxes struct { - mb map[string]*imap.MailboxInfo - mu sync.RWMutex + mb map[string]*imap.MailboxInfo + mu sync.RWMutex + changeLocks map[string]chan struct{} +} + +func (mbs *mailboxes) unlocking(elem Folder) { + mbs.mu.Lock() + defer mbs.mu.Unlock() + + ch, ok := mbs.changeLocks[elem.str] + if !ok { + panic("Unlocking where nothing is locked") + } + close(ch) + delete(mbs.changeLocks, elem.str) +} + +func (mbs *mailboxes) locking(elem Folder) bool { + mbs.mu.Lock() + ch, ok := mbs.changeLocks[elem.str] + if !ok { + ch = make(chan struct{}) + mbs.changeLocks[elem.str] = ch + mbs.mu.Unlock() + // we created the lock, we are in charge and done here + return false + } else { + // someone else is working, we wait till he's done + mbs.mu.Unlock() // we are not doing anything... + <-ch + return true + } } func (mbs *mailboxes) contains(elem Folder) bool { @@ -28,7 +58,8 @@ func (mbs *mailboxes) add(elem *imap.MailboxInfo) { func NewMailboxes() *mailboxes { return &mailboxes{ - mb: map[string]*imap.MailboxInfo{}, - mu: sync.RWMutex{}, + mb: map[string]*imap.MailboxInfo{}, + changeLocks: map[string]chan struct{}{}, + mu: sync.RWMutex{}, } -} \ No newline at end of file +} -- cgit v1.2.3-54-g00ecf