From a275f65848f2c1fdd4302f56121defc408e7d8b6 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 28 Apr 2019 13:26:38 +0000 Subject: [PATCH] lib/msgstore: protect with a mutex MessageStore has a lot of exported fields that can be read from the outside. Each read must be protected, because a call from Update could happen at any time. --- lib/msgstore.go | 25 ++++++++++++++++++++++--- widgets/msglist.go | 18 ++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/lib/msgstore.go b/lib/msgstore.go index 9d537fc..b39d0bb 100644 --- a/lib/msgstore.go +++ b/lib/msgstore.go @@ -2,6 +2,7 @@ package lib import ( "io" + "sync" "time" "github.com/emersion/go-imap" @@ -9,7 +10,10 @@ import ( "git.sr.ht/~sircmpwn/aerc2/worker/types" ) +// Accesses to fields must be guarded by MessageStore.Lock/Unlock type MessageStore struct { + sync.Mutex + Deleted map[uint32]interface{} DirInfo types.DirectoryInfo Messages map[uint32]*types.MessageInfo @@ -45,6 +49,9 @@ func NewMessageStore(worker *types.Worker, func (store *MessageStore) FetchHeaders(uids []uint32, cb func(*types.MessageInfo)) { + store.Lock() + defer store.Unlock() + // TODO: this could be optimized by pre-allocating toFetch and trimming it // at the end. In practice we expect to get most messages back in one frame. var toFetch imap.SeqSet @@ -67,6 +74,9 @@ func (store *MessageStore) FetchHeaders(uids []uint32, } func (store *MessageStore) FetchFull(uids []uint32, cb func(io.Reader)) { + store.Lock() + defer store.Unlock() + // TODO: this could be optimized by pre-allocating toFetch and trimming it // at the end. In practice we expect to get most messages back in one frame. var toFetch imap.SeqSet @@ -103,8 +113,7 @@ func (store *MessageStore) FetchBodyPart( }) } -func (store *MessageStore) merge( - to *types.MessageInfo, from *types.MessageInfo) { +func merge(to *types.MessageInfo, from *types.MessageInfo) { if from.BodyStructure != nil { to.BodyStructure = from.BodyStructure @@ -125,6 +134,8 @@ func (store *MessageStore) merge( } func (store *MessageStore) Update(msg types.WorkerMessage) { + store.Lock() + update := false switch msg := msg.(type) { case *types.DirectoryInfo: @@ -144,7 +155,7 @@ func (store *MessageStore) Update(msg types.WorkerMessage) { update = true case *types.MessageInfo: if existing, ok := store.Messages[msg.Uid]; ok && existing != nil { - store.merge(existing, msg) + merge(existing, msg) } else { store.Messages[msg.Uid] = msg } @@ -186,6 +197,9 @@ func (store *MessageStore) Update(msg types.WorkerMessage) { store.Uids = uids update = true } + + store.Unlock() + if update { store.update() } @@ -202,11 +216,16 @@ func (store *MessageStore) update() { } func (store *MessageStore) Delete(uids []uint32) { + store.Lock() + var set imap.SeqSet for _, uid := range uids { set.AddNum(uid) store.Deleted[uid] = nil } + + store.Unlock() + store.worker.PostAction(&types.DeleteMessages{Uids: set}, nil) store.update() } diff --git a/widgets/msglist.go b/widgets/msglist.go index 4f853f3..eeadec7 100644 --- a/widgets/msglist.go +++ b/widgets/msglist.go @@ -53,6 +53,8 @@ func (ml *MessageList) Draw(ctx *ui.Context) { return } + store.Lock() + var ( needsHeaders []uint32 row int = 0 @@ -92,6 +94,8 @@ func (ml *MessageList) Draw(ctx *ui.Context) { tcell.StyleDefault, "%s", msg) } + store.Unlock() + if len(needsHeaders) != 0 { store.FetchHeaders(needsHeaders, nil) ml.spinner.Start() @@ -109,11 +113,13 @@ func (ml *MessageList) storeUpdate(store *lib.MessageStore) { return } + store.Lock() if len(store.Uids) > 0 { for ml.selected >= len(store.Uids) { ml.Prev() } } + store.Unlock() ml.Invalidate() } @@ -139,16 +145,25 @@ func (ml *MessageList) Store() *lib.MessageStore { func (ml *MessageList) Empty() bool { store := ml.Store() + store.Lock() + defer store.Unlock() + return store == nil || len(store.Uids) == 0 } func (ml *MessageList) Selected() *types.MessageInfo { store := ml.Store() + store.Lock() + defer store.Unlock() + return store.Messages[store.Uids[len(store.Uids)-ml.selected-1]] } func (ml *MessageList) Select(index int) { store := ml.Store() + store.Lock() + defer store.Unlock() + ml.selected = index for ; ml.selected < 0; ml.selected = len(store.Uids) + ml.selected { } @@ -166,6 +181,9 @@ func (ml *MessageList) Select(index int) { func (ml *MessageList) nextPrev(delta int) { store := ml.Store() + store.Lock() + defer store.Unlock() + if store == nil || len(store.Uids) == 0 { return }