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

html/template: data race with concurrent ExecuteTemplate calls #47040

Closed
josharian opened this issue Jul 2, 2021 · 5 comments
Closed

html/template: data race with concurrent ExecuteTemplate calls #47040

josharian opened this issue Jul 2, 2021 · 5 comments
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Milestone

Comments

@josharian
Copy link
Contributor

Extracted from flaky production tests.

ExecuteTemplate is documented to allow concurrent calls:

A template may be executed safely in parallel, although if parallel executions share a Writer the output may be interleaved.

https://play.golang.org/p/MHNbSuG9gOj

package main

import (
	"html/template"
	"io"
	"strings"
	"sync"
)

func main() {
	tmpl, err := template.New("a").Parse(`{{template "t"}}`)
	if err != nil {
		panic(err)
	}
	tmpl.New("b").Parse(`{{template "h"}}{{template "t"}}`)
	tmpl.New("h").Parse(`<style>{{template "s"}}</style>`)
	tmpl.New("t").Parse("")
	tmpl.New("s").Parse(strings.Repeat("🏎", 10000))
	var wg sync.WaitGroup
	wg.Add(100)
	for i := 0; i < 100; i++ {
		name := "a"
		if i&1 == 0 {
			name = "b"
		}
		go func(name string) {
			defer wg.Done()
			tmpl.ExecuteTemplate(io.Discard, name, nil)
		}(name)
	}
	wg.Wait()
}

Result:

==================
WARNING: DATA RACE
Write at 0x00c00011c540 by goroutine 35:
  runtime.mapaccess2_faststr()
      /Users/josh/go/1.16/src/runtime/map_faststr.go:107 +0x48c
  text/template.(*Template).associate()
      /Users/josh/go/1.16/src/text/template/template.go:227 +0x160
  text/template.(*Template).AddParseTree()
      /Users/josh/go/1.16/src/text/template/template.go:133 +0x218
  html/template.(*escaper).commit()
      /Users/josh/go/1.16/src/html/template/escape.go:812 +0x1fc
  html/template.escapeTemplate()
      /Users/josh/go/1.16/src/html/template/escape.go:38 +0x264
  html/template.(*Template).lookupAndEscapeTemplate()
      /Users/josh/go/1.16/src/html/template/template.go:163 +0x2a8
  html/template.(*Template).ExecuteTemplate()
      /Users/josh/go/1.16/src/html/template/template.go:135 +0x38
  main.main.func1()
      /Users/josh/grrr/x.go:28 +0x94

Previous read at 0x00c00011c540 by goroutine 34:
  runtime.evacuate_fast64()
      /Users/josh/go/1.16/src/runtime/map_fast64.go:375 +0x3dc
  text/template.(*state).walkTemplate()
      /Users/josh/go/1.16/src/text/template/exec.go:404 +0xf0
  text/template.(*state).walk()
      /Users/josh/go/1.16/src/text/template/exec.go:269 +0x214
  text/template.(*state).walk()
      /Users/josh/go/1.16/src/text/template/exec.go:264 +0x140
  text/template.(*Template).execute()
      /Users/josh/go/1.16/src/text/template/exec.go:220 +0x214
  text/template.(*Template).Execute()
      /Users/josh/go/1.16/src/text/template/exec.go:203 +0xb8
  html/template.(*Template).ExecuteTemplate()
      /Users/josh/go/1.16/src/html/template/template.go:139 +0x7c
  main.main.func1()
      /Users/josh/grrr/x.go:28 +0x94

Goroutine 35 (running) created at:
  main.main()
      /Users/josh/grrr/x.go:26 +0x21c

Goroutine 34 (finished) created at:
  main.main()
      /Users/josh/grrr/x.go:26 +0x21c
==================
==================
WARNING: DATA RACE
Write at 0x00c00011f020 by goroutine 35:
  text/template.(*Template).associate()
      /Users/josh/go/1.16/src/text/template/template.go:227 +0x174
  text/template.(*Template).AddParseTree()
      /Users/josh/go/1.16/src/text/template/template.go:133 +0x218
  html/template.(*escaper).commit()
      /Users/josh/go/1.16/src/html/template/escape.go:812 +0x1fc
  html/template.escapeTemplate()
      /Users/josh/go/1.16/src/html/template/escape.go:38 +0x264
  html/template.(*Template).lookupAndEscapeTemplate()
      /Users/josh/go/1.16/src/html/template/template.go:163 +0x2a8
  html/template.(*Template).ExecuteTemplate()
      /Users/josh/go/1.16/src/html/template/template.go:135 +0x38
  main.main.func1()
      /Users/josh/grrr/x.go:28 +0x94

Previous read at 0x00c00011f020 by goroutine 12:
  text/template.(*state).walkTemplate()
      /Users/josh/go/1.16/src/text/template/exec.go:404 +0x100
  text/template.(*state).walk()
      /Users/josh/go/1.16/src/text/template/exec.go:269 +0x214
  text/template.(*state).walk()
      /Users/josh/go/1.16/src/text/template/exec.go:264 +0x140
  text/template.(*state).walkTemplate()
      /Users/josh/go/1.16/src/text/template/exec.go:418 +0x2f4
  text/template.(*state).walk()
      /Users/josh/go/1.16/src/text/template/exec.go:269 +0x214
  text/template.(*state).walk()
      /Users/josh/go/1.16/src/text/template/exec.go:264 +0x140
  text/template.(*Template).execute()
      /Users/josh/go/1.16/src/text/template/exec.go:220 +0x214
  text/template.(*Template).Execute()
      /Users/josh/go/1.16/src/text/template/exec.go:203 +0xb8
  html/template.(*Template).ExecuteTemplate()
      /Users/josh/go/1.16/src/html/template/template.go:139 +0x7c
  main.main.func1()
      /Users/josh/grrr/x.go:28 +0x94

Goroutine 35 (running) created at:
  main.main()
      /Users/josh/grrr/x.go:26 +0x21c

Goroutine 12 (finished) created at:
  main.main()
      /Users/josh/grrr/x.go:26 +0x21c
==================
Found 2 data race(s)
exit status 66
@bradfitz
Copy link
Contributor

bradfitz commented Jul 2, 2021

This is from 1.16. Did you also try 1.17beta1?

@josharian
Copy link
Contributor Author

Yep. Doesn't reproduce on 1.17beta.

@bradfitz
Copy link
Contributor

bradfitz commented Jul 2, 2021

Still might be worth tracking for a cherry pick to 1.16.

@josharian
Copy link
Contributor Author

496d7c6 is the fix.

@gopherbot, please open backport issues.

@gopherbot
Copy link

Backport issue(s) opened: #47041 (for 1.15), #47042 (for 1.16).

Remember to create the cherry-pick CL(s) as soon as the patch is submitted to master, according to https://golang.org/wiki/MinorReleases.

@dmitshur dmitshur added the NeedsFix The path to resolution is known, but the work has not been done. label Jul 9, 2021
@dmitshur dmitshur added this to the Go1.17 milestone Jul 9, 2021
@golang golang locked and limited conversation to collaborators Jul 9, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done.
Projects
None yet
Development

No branches or pull requests

4 participants