charmbracelet/bubbletea

Data race in bubbletea.NewProgram.WithColorCache.func1()

3v1n0 opened this issue · 4 comments

Describe the bug
Data race when runnig test code with -race

Trace

WARNING: DATA RACE
Write at 0x00c00013358a by goroutine 342:
  github.com/charmbracelet/bubbletea.NewProgram.WithColorCache.func1()
      /tmp/go-path/pkg/mod/github.com/muesli/termenv@v0.15.2/output.go:107 +0x37
  github.com/charmbracelet/bubbletea.NewProgram()
      /tmp/go-path/pkg/mod/github.com/charmbracelet/bubbletea@v0.25.0/tea.go:209 +0x353
  github.com/ubuntu/authd/pam/internal/adapter.TestGdmModel.func23()
      /home/marco/Dev/authd/pam/internal/adapter/gdmmodel_test.go:1279 +0xb44
  testing.tRunner()
      /snap/go/10489/src/testing/testing.go:1595 +0x261
  testing.(*T).Run.func1()
      /snap/go/10489/src/testing/testing.go:1648 +0x44

Previous read at 0x00c000133588 by goroutine 334:
  github.com/charmbracelet/bubbletea.(*Program).checkResize()
      /tmp/go-path/pkg/mod/github.com/charmbracelet/bubbletea@v0.25.0/tty.go:99 +0x67
  github.com/charmbracelet/bubbletea.(*Program).handleResize.func1()
      /tmp/go-path/pkg/mod/github.com/charmbracelet/bubbletea@v0.25.0/tea.go:259 +0x33

Goroutine 342 (running) created at:
  testing.(*T).Run()
      /snap/go/10489/src/testing/testing.go:1648 +0x845
  github.com/ubuntu/authd/pam/internal/adapter.TestGdmModel()
      /home/marco/Dev/authd/pam/internal/adapter/gdmmodel_test.go:1235 +0x5c08
  testing.tRunner()
      /snap/go/10489/src/testing/testing.go:1595 +0x261
  testing.(*T).Run.func1()
      /snap/go/10489/src/testing/testing.go:1648 +0x44

Code here isn't doing anything just calling into bubbletea code, this can be workarounded by using tea.WithOutput(os.Stdout) so that termenv.WithColorCache is not called.

Same for me

can someone take a look at this
@muesli @meowgorithm @caarlos0

Click here for the race condition log
WARNING: DATA RACE
Write at 0x00c00013214a by goroutine 13:
  github.com/charmbracelet/bubbletea.NewProgram.WithColorCache.func1()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:107 +0x38
  github.com/charmbracelet/bubbletea.NewProgram()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/charmbracelet/bubbletea/tea.go:209 +0x304
  github.com/gofiber/cli/cmd/internal.NewPrompt()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt.go:30 +0x1dc
  github.com/gofiber/cli/cmd/internal.Test_Prompt_View()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt_test.go:84 +0x64
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.(*T).Run.gowrap1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x40

Previous write at 0x00c00013214a by goroutine 11:
  github.com/charmbracelet/bubbletea.NewProgram.WithColorCache.func1()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:107 +0x38
  github.com/charmbracelet/bubbletea.NewProgram()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/charmbracelet/bubbletea/tea.go:209 +0x304
  github.com/gofiber/cli/cmd/internal.NewPrompt()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt.go:30 +0x1dc
  github.com/gofiber/cli/cmd/internal.Test_Prompt_Initialize()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt_test.go:52 +0x64
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.(*T).Run.gowrap1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x40

Goroutine 13 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x5e4
  testing.runTests.func1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2161 +0x80
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.runTests()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2159 +0x6e0
  testing.(*M).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2027 +0xb74
  main.main()
      _testmain.go:59 +0x294

Goroutine 11 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x5e4
  testing.runTests.func1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2161 +0x80
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.runTests()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2159 +0x6e0
  testing.(*M).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2027 +0xb74
  main.main()
      _testmain.go:59 +0x294
==================
==================
WARNING: DATA RACE
Write at 0x00c00013214a by goroutine 12:
  github.com/charmbracelet/bubbletea.NewProgram.WithColorCache.func1()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:107 +0x38
  github.com/charmbracelet/bubbletea.NewProgram()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/charmbracelet/bubbletea/tea.go:209 +0x304
  github.com/gofiber/cli/cmd/internal.NewPrompt()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt.go:30 +0x1dc
  github.com/gofiber/cli/cmd/internal.Test_Prompt_Update()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt_test.go:63 +0x68
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.(*T).Run.gowrap1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x40

Previous read at 0x00c000132148 by goroutine 11:
--- FAIL: Test_Prompt_ParseBool (0.00s)
    testing.go:1398: race detected during execution of test
  github.com/muesli/termenv.(*Output).isTTY()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/termenv.go:38 +0x114
  github.com/muesli/termenv.(*Output).BackgroundColor.func1()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:158 +0x30
  sync.(*Once).doSlow()
      /opt/homebrew/opt/go/libexec/src/sync/once.go:74 +0xb4
  sync.(*Once).Do()
      /opt/homebrew/opt/go/libexec/src/sync/once.go:65 +0x40
  github.com/muesli/termenv.(*Output).BackgroundColor()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:166 +0x78
  github.com/charmbracelet/bubbletea.NewProgram.WithColorCache.func1()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:111 +0x50
  github.com/charmbracelet/bubbletea.NewProgram()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/charmbracelet/bubbletea/tea.go:209 +0x304
  github.com/gofiber/cli/cmd/internal.NewPrompt()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt.go:30 +0x1dc
  github.com/gofiber/cli/cmd/internal.Test_Prompt_Initialize()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt_test.go:52 +0x64
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.(*T).Run.gowrap1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x40

Goroutine 12 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x5e4
  testing.runTests.func1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2161 +0x80
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.runTests()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2159 +0x6e0
  testing.(*M).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2027 +0xb74
  main.main()
      _testmain.go:59 +0x294

Goroutine 11 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x5e4
  testing.runTests.func1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2161 +0x80
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.runTests()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2159 +0x6e0
  testing.(*M).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2027 +0xb74
  main.main()
      _testmain.go:59 +0x294
==================
==================
WARNING: DATA RACE
Read at 0x00c00013214a by goroutine 12:
  github.com/muesli/termenv.(*Output).ForegroundColor()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:146 +0x54
  github.com/charmbracelet/bubbletea.NewProgram.WithColorCache.func1()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:110 +0x48
  github.com/charmbracelet/bubbletea.NewProgram()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/charmbracelet/bubbletea/tea.go:209 +0x304
  github.com/gofiber/cli/cmd/internal.NewPrompt()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt.go:30 +0x1dc
  github.com/gofiber/cli/cmd/internal.Test_Prompt_Update()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt_test.go:63 +0x68
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.(*T).Run.gowrap1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x40

Previous write at 0x00c00013214a by goroutine 9:
  github.com/charmbracelet/bubbletea.NewProgram.WithColorCache.func1()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:107 +0x38
  github.com/charmbracelet/bubbletea.NewProgram()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/charmbracelet/bubbletea/tea.go:209 +0x304
  github.com/gofiber/cli/cmd/internal.NewPrompt()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt.go:30 +0x1dc
  github.com/gofiber/cli/cmd/internal.Test_Prompt_YesOrNo()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt_test.go:35 +0x40
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.(*T).Run.gowrap1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x40

Goroutine 12 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x5e4
  testing.runTests.func1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2161 +0x80
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.runTests()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2159 +0x6e0
  testing.(*M).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2027 +0xb74
  main.main()
      _testmain.go:59 +0x294

Goroutine 9 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x5e4
  testing.runTests.func1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2161 +0x80
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.runTests()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2159 +0x6e0
  testing.(*M).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2027 +0xb74
  main.main()
      _testmain.go:59 +0x294
==================
==================
WARNING: DATA RACE
Write at 0x00c00013214a by goroutine 8:
  github.com/charmbracelet/bubbletea.NewProgram.WithColorCache.func1()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:107 +0x38
  github.com/charmbracelet/bubbletea.NewProgram()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/charmbracelet/bubbletea/tea.go:209 +0x304
  github.com/gofiber/cli/cmd/internal.NewPrompt()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt.go:30 +0x1dc
  github.com/gofiber/cli/cmd/internal.Test_Prompt_Answer()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt_test.go:27 +0x40
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.(*T).Run.gowrap1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x40

Previous read at 0x00c00013214a by goroutine 13:
  github.com/muesli/termenv.(*Output).ForegroundColor()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:146 +0x54
  github.com/charmbracelet/bubbletea.NewProgram.WithColorCache.func1()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:110 +0x48
  github.com/charmbracelet/bubbletea.NewProgram()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/charmbracelet/bubbletea/tea.go:209 +0x304
  github.com/gofiber/cli/cmd/internal.NewPrompt()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt.go:30 +0x1dc
  github.com/gofiber/cli/cmd/internal.Test_Prompt_View()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt_test.go:84 +0x64
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.(*T).Run.gowrap1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x40

Goroutine 8 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x5e4
  testing.runTests.func1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2161 +0x80
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.runTests()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2159 +0x6e0
  testing.(*M).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2027 +0xb74
  main.main()
      _testmain.go:59 +0x294

Goroutine 13 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x5e4
  testing.runTests.func1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2161 +0x80
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.runTests()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2159 +0x6e0
  testing.(*M).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2027 +0xb74
  main.main()
      _testmain.go:59 +0x294
==================
==================
WARNING: DATA RACE
Read at 0x00c00013214a by goroutine 12:
  github.com/muesli/termenv.(*Output).BackgroundColor()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:165 +0x54
  github.com/charmbracelet/bubbletea.NewProgram.WithColorCache.func1()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:111 +0x50
  github.com/charmbracelet/bubbletea.NewProgram()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/charmbracelet/bubbletea/tea.go:209 +0x304
  github.com/gofiber/cli/cmd/internal.NewPrompt()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt.go:30 +0x1dc
  github.com/gofiber/cli/cmd/internal.Test_Prompt_Update()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt_test.go:63 +0x68
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.(*T).Run.gowrap1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x40

Previous write at 0x00c00013214a by goroutine 7:
  github.com/charmbracelet/bubbletea.NewProgram.WithColorCache.func1()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/muesli/termenv/output.go:107 +0x38
  github.com/charmbracelet/bubbletea.NewProgram()
      /Users/wernerr/projects/fiberProjects/cli/vendor/github.com/charmbracelet/bubbletea/tea.go:209 +0x304
  github.com/gofiber/cli/cmd/internal.NewPrompt()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt.go:30 +0x1dc
  github.com/gofiber/cli/cmd/internal.Test_Prompt_New()
      /Users/wernerr/projects/fiberProjects/cli/cmd/internal/prompt_test.go:16 +0x88
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.(*T).Run.gowrap1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x40

Goroutine 12 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x5e4
  testing.runTests.func1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2161 +0x80
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.runTests()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2159 +0x6e0
  testing.(*M).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2027 +0xb74
  main.main()
      _testmain.go:59 +0x294

Goroutine 7 (running) created at:
  testing.(*T).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1742 +0x5e4
  testing.runTests.func1()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2161 +0x80
  testing.tRunner()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:1689 +0x180
  testing.runTests()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2159 +0x6e0
  testing.(*M).Run()
      /opt/homebrew/opt/go/libexec/src/testing/testing.go:2027 +0xb74
  main.main()
      _testmain.go:59 +0x294
==================

we wanted to use the pack for our fiber cli:
https://github.com/gofiber/cli

to support our project with a cli
https://github.com/gofiber/fiber
image

reproducible with test command:

go test ./... -race

would also say that you should activate race detection when running your

run: go test -v ./...

thank you, great work on the project

It looks like this race condition is being caused by the Output.cache variable in github.com/muesli/termenv/output.go. Specifically, from the logs it seems as if the problematic write is at line 107 and read is at line 146. I think (if I understand this correctly) that the issue arises from termenv rather than bubbletea?

can you share a reproducible? I can then take a look...

thanks!

FWIW, I believe that in tests, you'll need to set a different output/input for each test.

Also might worth a look: https://github.com/charmbracelet/x/tree/main/exp/teatest