From 758a876f901117a9d5f6ce8505b4fe981a8a289a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 20 Mar 2024 11:48:06 +0100 Subject: Fix potential deadlock in Translations Fixes #12129 --- hugolib/content_map_page.go | 18 ++++++++++++------ hugolib/page.go | 6 ++++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/hugolib/content_map_page.go b/hugolib/content_map_page.go index f4ff2a9ee..2f0a6408b 100644 --- a/hugolib/content_map_page.go +++ b/hugolib/content_map_page.go @@ -93,7 +93,8 @@ type pageMap struct { // Used for simple page lookups by name, e.g. "mypage.md" or "mypage". pageReverseIndex *contentTreeReverseIndex - cachePages *dynacache.Partition[string, page.Pages] + cachePages1 *dynacache.Partition[string, page.Pages] + cachePages2 *dynacache.Partition[string, page.Pages] cacheResources *dynacache.Partition[string, resource.Resources] cacheContentRendered *dynacache.Partition[string, *resources.StaleValue[contentSummary]] cacheContentPlain *dynacache.Partition[string, *resources.StaleValue[contentPlainPlainWords]] @@ -324,15 +325,19 @@ func (m *pageMap) forEeachPageIncludingBundledPages(include predicate.P[*pageSta } func (m *pageMap) getOrCreatePagesFromCache( + cache *dynacache.Partition[string, page.Pages], key string, create func(string) (page.Pages, error), ) (page.Pages, error) { - return m.cachePages.GetOrCreate(key, create) + if cache == nil { + cache = m.cachePages1 + } + return cache.GetOrCreate(key, create) } func (m *pageMap) getPagesInSection(q pageMapQueryPagesInSection) page.Pages { cacheKey := q.Key() - pages, err := m.getOrCreatePagesFromCache(cacheKey, func(string) (page.Pages, error) { + pages, err := m.getOrCreatePagesFromCache(nil, cacheKey, func(string) (page.Pages, error) { prefix := paths.AddTrailingSlash(q.Path) var ( @@ -397,7 +402,7 @@ func (m *pageMap) getPagesInSection(q pageMapQueryPagesInSection) page.Pages { func (m *pageMap) getPagesWithTerm(q pageMapQueryPagesBelowPath) page.Pages { key := q.Key() - v, err := m.cachePages.GetOrCreate(key, func(string) (page.Pages, error) { + v, err := m.cachePages1.GetOrCreate(key, func(string) (page.Pages, error) { var pas page.Pages include := q.Include if include == nil { @@ -434,7 +439,7 @@ func (m *pageMap) getPagesWithTerm(q pageMapQueryPagesBelowPath) page.Pages { func (m *pageMap) getTermsForPageInTaxonomy(path, taxonomy string) page.Pages { prefix := paths.AddLeadingSlash(taxonomy) - v, err := m.cachePages.GetOrCreate(prefix+path, func(string) (page.Pages, error) { + v, err := m.cachePages1.GetOrCreate(prefix+path, func(string) (page.Pages, error) { var pas page.Pages err := m.treeTaxonomyEntries.WalkPrefix( @@ -861,7 +866,8 @@ func newPageMap(i int, s *Site, mcache *dynacache.Cache, pageTrees *pageTrees) * m = &pageMap{ pageTrees: pageTrees.Shape(0, i), - cachePages: dynacache.GetOrCreatePartition[string, page.Pages](mcache, fmt.Sprintf("/pags/%d", i), dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild}), + cachePages1: dynacache.GetOrCreatePartition[string, page.Pages](mcache, fmt.Sprintf("/pag1/%d", i), dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild}), + cachePages2: dynacache.GetOrCreatePartition[string, page.Pages](mcache, fmt.Sprintf("/pag2/%d", i), dynacache.OptionsPartition{Weight: 10, ClearWhen: dynacache.ClearOnRebuild}), cacheResources: dynacache.GetOrCreatePartition[string, resource.Resources](mcache, fmt.Sprintf("/ress/%d", i), dynacache.OptionsPartition{Weight: 60, ClearWhen: dynacache.ClearOnRebuild}), cacheContentRendered: dynacache.GetOrCreatePartition[string, *resources.StaleValue[contentSummary]](mcache, fmt.Sprintf("/cont/ren/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}), cacheContentPlain: dynacache.GetOrCreatePartition[string, *resources.StaleValue[contentPlainPlainWords]](mcache, fmt.Sprintf("/cont/pla/%d", i), dynacache.OptionsPartition{Weight: 70, ClearWhen: dynacache.ClearOnChange}), diff --git a/hugolib/page.go b/hugolib/page.go index 5d083fd5b..028d3fa95 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -380,7 +380,9 @@ func (p *pageState) TranslationKey() string { // AllTranslations returns all translations, including the current Page. func (p *pageState) AllTranslations() page.Pages { key := p.Path() + "/" + "translations-all" - pages, err := p.s.pageMap.getOrCreatePagesFromCache(key, func(string) (page.Pages, error) { + // This is called from Translations, so we need to use a different partition, cachePages2, + // to avoid potential deadlocks. + pages, err := p.s.pageMap.getOrCreatePagesFromCache(p.s.pageMap.cachePages2, key, func(string) (page.Pages, error) { if p.m.pageConfig.TranslationKey != "" { // translationKey set by user. pas, _ := p.s.h.translationKeyPages.Get(p.m.pageConfig.TranslationKey) @@ -413,7 +415,7 @@ func (p *pageState) AllTranslations() page.Pages { // Translations returns the translations excluding the current Page. func (p *pageState) Translations() page.Pages { key := p.Path() + "/" + "translations" - pages, err := p.s.pageMap.getOrCreatePagesFromCache(key, func(string) (page.Pages, error) { + pages, err := p.s.pageMap.getOrCreatePagesFromCache(nil, key, func(string) (page.Pages, error) { var pas page.Pages for _, pp := range p.AllTranslations() { if !pp.Eq(p) { -- cgit v1.2.3