]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Fix potential deadlock in Translations
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 20 Mar 2024 10:48:06 +0000 (11:48 +0100)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 20 Mar 2024 11:33:05 +0000 (12:33 +0100)
Fixes #12129

hugolib/content_map_page.go
hugolib/page.go

index f4ff2a9ee2828d855d02ee038a6763304e668c3c..2f0a6408bd4f0bae7d1032dde4fe4125c2175d78 100644 (file)
@@ -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}),
index 5d083fd5b3db9699823f3a1d5b1df8a5a235dde1..028d3fa959f81cdcf0fe083e612b3f21f5f273f9 100644 (file)
@@ -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) {