From 24fa7544b08f998ed3dd857342dc7787924276a1 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 29 Feb 2024 20:14:32 -0500 Subject: [PATCH] runtime: fix spurious race using Ticker.Reset Ticker.Reset was added in CL 217362 in 2020. It added the runtime helper modTimer, which is analogous to startTimer and resetTimer but for tickers. Unlike those, it does not contain a racerelease, which means that code synchronizing by starting a ticker will be diagnosed with a spurious race. Add racerelease to modTimer and add tests of all three racereleases (in startTimer, resetTimer, and modTimer). Also do not call time.resetTimer from elsewhere in runtime, since that function is only for package time. Use t.reset instead. For #33184. Change-Id: Ie40c1ad24911f21e81b1d3cc608cf086ff2bc83d Reviewed-on: https://go-review.googlesource.com/c/go/+/568340 Auto-Submit: Russ Cox LUCI-TryBot-Result: Go LUCI Reviewed-by: Michael Pratt --- src/runtime/race/testdata/time_test.go | 116 +++++++++++++++++++++++++ src/runtime/time.go | 3 + 2 files changed, 119 insertions(+) create mode 100644 src/runtime/race/testdata/time_test.go diff --git a/src/runtime/race/testdata/time_test.go b/src/runtime/race/testdata/time_test.go new file mode 100644 index 0000000000..820d7c4708 --- /dev/null +++ b/src/runtime/race/testdata/time_test.go @@ -0,0 +1,116 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package race_test + +import ( + "testing" + "time" +) + +func TestNoRaceAfterFunc(_ *testing.T) { + v := 0 + _ = v + c := make(chan int) + f := func() { + v = 1 + c <- 0 + } + v = 2 + time.AfterFunc(1, f) + <-c + v = 3 +} + +func TestNoRaceAfterFuncReset(_ *testing.T) { + v := 0 + _ = v + c := make(chan int) + f := func() { + v = 1 + c <- 0 + } + t := time.AfterFunc(time.Hour, f) + t.Stop() + v = 2 + t.Reset(1) + <-c + v = 3 +} + +func TestNoRaceTimer(_ *testing.T) { + v := 0 + _ = v + c := make(chan int) + f := func() { + v = 1 + c <- 0 + } + v = 2 + t := time.NewTimer(1) + go func() { + <-t.C + f() + }() + <-c + v = 3 +} + +func TestNoRaceTimerReset(_ *testing.T) { + v := 0 + _ = v + c := make(chan int) + f := func() { + v = 1 + c <- 0 + } + t := time.NewTimer(time.Hour) + go func() { + <-t.C + f() + }() + t.Stop() + v = 2 + t.Reset(1) + <-c + v = 3 +} + +func TestNoRaceTicker(_ *testing.T) { + v := 0 + _ = v + c := make(chan int) + f := func() { + v = 1 + c <- 0 + } + v = 2 + t := time.NewTicker(1) + go func() { + <-t.C + f() + }() + <-c + v = 3 +} + +func TestNoRaceTickerReset(_ *testing.T) { + v := 0 + _ = v + c := make(chan int) + f := func() { + v = 1 + c <- 0 + } + t := time.NewTicker(time.Hour) + go func() { + <-t.C + f() + }() + t.Stop() + v = 2 + t.Reset(1) + <-c + v = 3 +} diff --git a/src/runtime/time.go b/src/runtime/time.go index b509d99b80..cee0197907 100644 --- a/src/runtime/time.go +++ b/src/runtime/time.go @@ -295,6 +295,9 @@ func resetTimer(t *timer, when int64) bool { // //go:linkname modTimer time.modTimer func modTimer(t *timer, when, period int64) { + if raceenabled { + racerelease(unsafe.Pointer(t)) + } t.modify(when, period, t.f, t.arg, t.seq) } -- 2.48.1