From b2dce99714a650707b4c2dcda51df47f07a98976 Mon Sep 17 00:00:00 2001 From: Drew Weymouth Date: Thu, 11 May 2023 16:43:10 -0700 Subject: [PATCH] fix race condition index out of bound crash in gridview --- ui/widgets/gridview.go | 50 +++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/ui/widgets/gridview.go b/ui/widgets/gridview.go index 11704ed4..4d04f0ea 100644 --- a/ui/widgets/gridview.go +++ b/ui/widgets/gridview.go @@ -129,11 +129,11 @@ func (g *GridView) Clear() { func (g *GridView) Reset(iter GridViewIterator) { g.itemsMutex.Lock() g.items = nil - g.itemsMutex.Unlock() g.fetching = false g.done = false g.highestShown = 0 g.iter = iter + g.itemsMutex.Unlock() g.fetchMoreItems(36) } @@ -194,8 +194,13 @@ func (g *GridView) doUpdateItemCard(itemIdx int, card *GridViewItem) { if itemIdx > g.highestShown { g.highestShown = itemIdx } + var item GridViewItemModel g.itemsMutex.RLock() - item := g.items[itemIdx] + // itemIdx can rarely be out of range if the data is being updated + // as the view is requested to refresh + if itemIdx < len(g.items) { + item = g.items[itemIdx] + } g.itemsMutex.RUnlock() if card.PrevID == item.ID { // nothing to do @@ -208,26 +213,31 @@ func (g *GridView) doUpdateItemCard(itemIdx int, card *GridViewItem) { card.ImgLoadCancel() card.ImgLoadCancel = nil } - if img, ok := g.imageFetcher.GetCoverThumbnailFromCache(item.CoverArtID); ok { - card.Cover.SetImage(img) + if item.CoverArtID != "" { + if img, ok := g.imageFetcher.GetCoverThumbnailFromCache(item.CoverArtID); ok { + card.Cover.SetImage(img) + } else { + card.Cover.SetImageResource(res.ResAlbumplaceholderPng) + // asynchronously fetch cover image + ctx, cancel := context.WithCancel(context.Background()) + card.ImgLoadCancel = cancel + go func(ctx context.Context) { + i, err := g.imageFetcher.GetCoverThumbnail(item.CoverArtID) + select { + case <-ctx.Done(): + return + default: + if err == nil { + card.Cover.SetImage(i) + } else { + log.Printf("error fetching image: %s", err.Error()) + } + } + }(ctx) + } } else { + // use the placeholder image for an item that has no cover art ID card.Cover.SetImageResource(res.ResAlbumplaceholderPng) - // asynchronously fetch cover image - ctx, cancel := context.WithCancel(context.Background()) - card.ImgLoadCancel = cancel - go func(ctx context.Context) { - i, err := g.imageFetcher.GetCoverThumbnail(item.CoverArtID) - select { - case <-ctx.Done(): - return - default: - if err == nil { - card.Cover.SetImage(i) - } else { - log.Printf("error fetching image: %s", err.Error()) - } - } - }(ctx) } // if user has scrolled near the bottom, fetch more