Skip to content

Commit

Permalink
Further improve locking (RWMutex version)
Browse files Browse the repository at this point in the history
As a second step improving unrolled#90, only lock when absolutely necessary
and reconstruct functions to ensure that the current templates are
referenced in the helper func instead of a global reference.

Closes unrolled#91

Signed-off-by: Andrew Thornton <[email protected]>
  • Loading branch information
zeripath committed May 12, 2021
1 parent 8d2b52a commit d086b3e
Showing 1 changed file with 24 additions and 22 deletions.
46 changes: 24 additions & 22 deletions render.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,11 @@ type HTMLOptions struct {
// Render is a service that provides functions for easily writing JSON, XML,
// binary data, and HTML templates out to a HTTP Response.
type Render struct {
lock sync.RWMutex

// Customize Secure with an Options struct.
opt Options
templates *template.Template
templatesLk sync.RWMutex
compiledCharset string
}

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

r.templatesLk.Lock()
r.lock.Lock()
defer r.lock.Unlock()
r.templates = tmpTemplates
r.templatesLk.Unlock()
}

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

r.templatesLk.Lock()
r.lock.Lock()
defer r.lock.Unlock()
r.templates = tmpTemplates
r.templatesLk.Unlock()
}

// 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 {
r.lock.RLock()
defer r.lock.RUnlock()
return r.templates.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 +322,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 @@ -402,14 +404,14 @@ func (r *Render) HTML(w io.Writer, status int, name string, binding interface{},
if r.opt.IsDevelopment {
r.compileTemplates()
}

r.templatesLk.RLock()
defer r.templatesLk.RUnlock()
r.lock.RLock()
templates := r.templates
r.lock.RUnlock()

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 +428,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 d086b3e

Please sign in to comment.