Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ensure page translation CPs are initialized #9398

Merged
merged 2 commits into from
Jan 27, 2022

Conversation

ptgott
Copy link
Contributor

@ptgott ptgott commented Jan 16, 2022

PR #9342 introduced a regression in which calling .Translations in a
template and calling RenderString on a translated Page caused a nil
pointer dereference. The issue was that some Pages returned from
.Translations had a nil cp field at the time the calling template was
being executed.

While PR #9342 had attempted to ensure that all ContentProviders were
initialized for translations at build time, it only performed the
initialization for receivers of ContentProvider methods such as
.Summary. However, the ContentProvider's *pageState.pageOutput.cp would
remain uninitialized, causing the nil pointer dereference.

This change edits the .Translations and .AllTranslations methods to
ensure that all of a page's translations have an initialized
content provider in time for a template to be executed.

Since LazyContentProvider is no longer needed with this approach, this
change also reverts the following commits:

Fixes #9383

hugolib/page.go Outdated
if !ok {
continue
}
if tp.pageOutput.cp == nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this.

But I don't think we can solve this like this. Most notably is the data race which, if it's not already failing the build, it will if you move the .Translations into the single template.

I suspect that the cleanest, working solution we will find without doing too much work is to expand on the patch we added a few days ago: Make sure that the lazy content provider is triggered on all access to cp (including RenderString).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. Would you mind explaining the data race a bit more? Which goroutines, processes, I/O, etc. would be racing?

I'll spend some time early this week implementing the approach you've suggested.

Copy link
Member

@bep bep Jan 17, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. We render pages in parallel.
  2. Given the below, AllTranslations may be called from multiple go routines on the same page.
  3. Any of the code lines reading or writing mutable page state below will be a potential data race (e.g. if tp.pageOutput.cp == nil and tp.initContentProvider(cp)). Go's race detector will fail the tests when run with go test -race (which we do on the CI server), but I guess you need to have test cases that trigger it.
func (p *pageState) AllTranslations() page.Pages {
	p.s.h.init.translations.Do()

	// Hugo attempts to reuse content providers while preparing each page for
	// rendering. This approach means that sometimes, content providers for
	// translations are not initialized. If needed, initialize them here.
	for _, tr := range p.allTranslations {
		tp, ok := tr.(*pageState)
		if !ok {
			continue
		}
		if tp.pageOutput.cp == nil {
			cp, err := newPageContentOutput(tp, tp.pageOutput)
			if err != nil {
				// This will be caught by texttemplate.safeCall
				panic(fmt.Errorf("error rendering page translation: %w", err))
			}
			tp.initContentProvider(cp)
		}
	}

	return p.allTranslations
}

@ptgott ptgott force-pushed the 9383-init-translation-cps branch 2 times, most recently from 501015e to 10118f4 Compare January 18, 2022 01:11
@ptgott
Copy link
Contributor Author

ptgott commented Jan 18, 2022

@bep I've suggested initializing a LazyContentProvider inside RenderString. If this approach looks okay, should I go ahead and see if other *pageState methods need a similar treatment?

hugolib/page.go Outdated
var cp *pageContentOutput

// If the current content provider is not yet initialized, do so now.
if lcp, ok := p.pageOutput.ContentProvider.(*page.LazyContentProvider); ok {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, this is conceptually what we need, but I would love if we could do this in one place. I think that if we use the same "interface approach" for RenderString (and potentially others) and make LazyContentProvider implement that interface, that should work.

@bep
Copy link
Member

bep commented Jan 27, 2022

I have some time today and will look closer at this PR -- again, much appreciated.

@bep bep force-pushed the 9383-init-translation-cps branch from 10118f4 to c67e487 Compare January 27, 2022 09:39
@bep
Copy link
Member

bep commented Jan 27, 2022

@ptgott I have added a commit to your PR which is a little bit more elaborate, but it's just moving the RenderString and Render code down to where it belongs, I guess. Note that there may still be cases where .Translations[0].SomeMethod may still return empty (I have always thought about the translation listings as mostly informative/metadata), but at least now it's not clearly specificed by an interface (OutputFormatContentProvider).

@bep bep force-pushed the 9383-init-translation-cps branch from c67e487 to 490deb6 Compare January 27, 2022 10:10
@bep bep merged commit f22c4ab into gohugoio:master Jan 27, 2022
@github-actions
Copy link

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 19, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Investigate .RenderString regression
2 participants