Skip to content

Commit

Permalink
Further improve locking
Browse files Browse the repository at this point in the history
As a second step improving unrolled#90, switch from using a RWMutex to use an
atomic.Value and a reconstruct functions to ensure that the current
templates are referenced in the helper func instead of a global
reference.

Signed-off-by: Andrew Thornton <[email protected]>
  • Loading branch information
zeripath committed May 12, 2021
1 parent 8d2b52a commit 536d3ec
Showing 1 changed file with 19 additions and 26 deletions.
45 changes: 19 additions & 26 deletions render.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"os"
"path/filepath"
"strings"
"sync"
"sync/atomic"
)

const (
Expand Down Expand Up @@ -122,8 +122,7 @@ type HTMLOptions struct {
type Render struct {
// Customize Secure with an Options struct.
opt Options
templates *template.Template
templatesLk sync.RWMutex
templateStore atomic.Value
compiledCharset string
}

Expand Down Expand Up @@ -242,9 +241,7 @@ func (r *Render) compileTemplatesFromDir() {
return nil
})

r.templatesLk.Lock()
r.templates = tmpTemplates
r.templatesLk.Unlock()
r.templateStore.Store(tmpTemplates)
}

func (r *Render) compileTemplatesFromAsset() {
Expand Down Expand Up @@ -289,28 +286,25 @@ func (r *Render) compileTemplatesFromAsset() {
}
}
}

r.templatesLk.Lock()
r.templates = tmpTemplates
r.templatesLk.Unlock()
r.templateStore.Store(tmpTemplates)
}

// TemplateLookup is a wrapper around template.Lookup and returns
// the template with the given name that is associated with t, or nil
// if there is no such template.
func (r *Render) TemplateLookup(t string) *template.Template {
return r.templates.Lookup(t)
return r.templateStore.Load().(*template.Template).Lookup(t)
}

func (r *Render) execute(name string, binding interface{}) (*bytes.Buffer, error) {
func (r *Render) execute(templates *template.Template, name string, binding interface{}) (*bytes.Buffer, error) {
buf := new(bytes.Buffer)
return buf, r.templates.ExecuteTemplate(buf, name, binding)
return buf, templates.ExecuteTemplate(buf, name, binding)
}

func (r *Render) layoutFuncs(name string, binding interface{}) template.FuncMap {
func (r *Render) layoutFuncs(templates *template.Template, name string, binding interface{}) template.FuncMap {
return template.FuncMap{
"yield": func() (template.HTML, error) {
buf, err := r.execute(name, binding)
buf, err := r.execute(templates, name, binding)
// Return safe HTML here since we are rendering our own template.
return template.HTML(buf.String()), err
},
Expand All @@ -320,23 +314,23 @@ func (r *Render) layoutFuncs(name string, binding interface{}) template.FuncMap
"block": func(partialName string) (template.HTML, error) {
log.Print("Render's `block` implementation is now depericated. Use `partial` as a drop in replacement.")
fullPartialName := fmt.Sprintf("%s-%s", partialName, name)
if r.TemplateLookup(fullPartialName) == nil && r.opt.RenderPartialsWithoutPrefix {
if templates.Lookup(fullPartialName) == nil && r.opt.RenderPartialsWithoutPrefix {
fullPartialName = partialName
}
if r.opt.RequireBlocks || r.TemplateLookup(fullPartialName) != nil {
buf, err := r.execute(fullPartialName, binding)
if r.opt.RequireBlocks || templates.Lookup(fullPartialName) != nil {
buf, err := r.execute(templates, fullPartialName, binding)
// Return safe HTML here since we are rendering our own template.
return template.HTML(buf.String()), err
}
return "", nil
},
"partial": func(partialName string) (template.HTML, error) {
fullPartialName := fmt.Sprintf("%s-%s", partialName, name)
if r.TemplateLookup(fullPartialName) == nil && r.opt.RenderPartialsWithoutPrefix {
if templates.Lookup(fullPartialName) == nil && r.opt.RenderPartialsWithoutPrefix {
fullPartialName = partialName
}
if r.opt.RequirePartials || r.TemplateLookup(fullPartialName) != nil {
buf, err := r.execute(fullPartialName, binding)
if r.opt.RequirePartials || templates.Lookup(fullPartialName) != nil {
buf, err := r.execute(templates, fullPartialName, binding)
// Return safe HTML here since we are rendering our own template.
return template.HTML(buf.String()), err
}
Expand Down Expand Up @@ -403,13 +397,12 @@ func (r *Render) HTML(w io.Writer, status int, name string, binding interface{},
r.compileTemplates()
}

r.templatesLk.RLock()
defer r.templatesLk.RUnlock()
templates := r.templateStore.Load().(*template.Template)

opt := r.prepareHTMLOptions(htmlOpt)
if tpl := r.templates.Lookup(name); tpl != nil {
if tpl := templates.Lookup(name); tpl != nil {
if len(opt.Layout) > 0 {
tpl.Funcs(r.layoutFuncs(name, binding))
tpl.Funcs(r.layoutFuncs(templates, name, binding))
name = opt.Layout
}

Expand All @@ -426,7 +419,7 @@ func (r *Render) HTML(w io.Writer, status int, name string, binding interface{},
h := HTML{
Head: head,
Name: name,
Templates: r.templates,
Templates: templates,
bp: r.opt.BufferPool,
}

Expand Down

0 comments on commit 536d3ec

Please sign in to comment.