tpl: Add temporary patch to fix template data race

Keep this as a separate commit so we can reapply it if needed.

Fixes #7293
This commit is contained in:
Bjørn Erik Pedersen 2021-01-29 17:45:38 +01:00
parent e77b2e3aa0
commit 9650e56841
2 changed files with 23 additions and 8 deletions

View file

@ -6,12 +6,13 @@ package template
import ( import (
"fmt" "fmt"
"github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort"
"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
"io" "io"
"reflect" "reflect"
"runtime" "runtime"
"strings" "strings"
"github.com/gohugoio/hugo/tpl/internal/go_templates/fmtsort"
"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
) )
// maxExecDepth specifies the maximum stack depth of templates within // maxExecDepth specifies the maximum stack depth of templates within
@ -179,10 +180,7 @@ func errRecover(errp *error) {
// A template may be executed safely in parallel, although if parallel // A template may be executed safely in parallel, although if parallel
// executions share a Writer the output may be interleaved. // executions share a Writer the output may be interleaved.
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error { func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
var tmpl *Template tmpl := t.Lookup(name)
if t.common != nil {
tmpl = t.tmpl[name]
}
if tmpl == nil { if tmpl == nil {
return fmt.Errorf("template: no template %q associated with template %q", name, t.name) return fmt.Errorf("template: no template %q associated with template %q", name, t.name)
} }
@ -230,6 +228,9 @@ func (t *Template) DefinedTemplates() string {
return "" return ""
} }
var b strings.Builder var b strings.Builder
// temporary Hugo-fix
t.muTmpl.RLock()
defer t.muTmpl.RUnlock()
for name, tmpl := range t.tmpl { for name, tmpl := range t.tmpl {
if tmpl.Tree == nil || tmpl.Root == nil { if tmpl.Tree == nil || tmpl.Root == nil {
continue continue
@ -401,7 +402,7 @@ func (s *state) walkRange(dot reflect.Value, r *parse.RangeNode) {
func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) { func (s *state) walkTemplate(dot reflect.Value, t *parse.TemplateNode) {
s.at(t) s.at(t)
tmpl := s.tmpl.tmpl[t.Name] tmpl := s.tmpl.Lookup(t.Name)
if tmpl == nil { if tmpl == nil {
s.errorf("template %q not defined", t.Name) s.errorf("template %q not defined", t.Name)
} }

View file

@ -5,13 +5,15 @@
package template package template
import ( import (
"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
"reflect" "reflect"
"sync" "sync"
"github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate/parse"
) )
// common holds the information shared by related templates. // common holds the information shared by related templates.
type common struct { type common struct {
muTmpl sync.RWMutex // protects tmpl (temporary Hugo-fix)
tmpl map[string]*Template // Map from name to defined templates. tmpl map[string]*Template // Map from name to defined templates.
option option option option
// We use two maps, one for parsing and one for execution. // We use two maps, one for parsing and one for execution.
@ -88,6 +90,9 @@ func (t *Template) Clone() (*Template, error) {
if t.common == nil { if t.common == nil {
return nt, nil return nt, nil
} }
// temporary Hugo-fix
t.muTmpl.RLock()
defer t.muTmpl.RUnlock()
for k, v := range t.tmpl { for k, v := range t.tmpl {
if k == t.name { if k == t.name {
nt.tmpl[t.name] = nt nt.tmpl[t.name] = nt
@ -124,6 +129,9 @@ func (t *Template) copy(c *common) *Template {
// its definition. If it has been defined and already has that name, the existing // its definition. If it has been defined and already has that name, the existing
// definition is replaced; otherwise a new template is created, defined, and returned. // definition is replaced; otherwise a new template is created, defined, and returned.
func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) { func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
// temporary Hugo-fix
t.muTmpl.Lock()
defer t.muTmpl.Unlock()
t.init() t.init()
nt := t nt := t
if name != t.name { if name != t.name {
@ -142,6 +150,9 @@ func (t *Template) Templates() []*Template {
return nil return nil
} }
// Return a slice so we don't expose the map. // Return a slice so we don't expose the map.
// temporary Hugo-fix
t.muTmpl.RLock()
defer t.muTmpl.RUnlock()
m := make([]*Template, 0, len(t.tmpl)) m := make([]*Template, 0, len(t.tmpl))
for _, v := range t.tmpl { for _, v := range t.tmpl {
m = append(m, v) m = append(m, v)
@ -182,6 +193,9 @@ func (t *Template) Lookup(name string) *Template {
if t.common == nil { if t.common == nil {
return nil return nil
} }
// temporary Hugo-fix
t.muTmpl.RLock()
defer t.muTmpl.RUnlock()
return t.tmpl[name] return t.tmpl[name]
} }