Skip to content

Commit 48849e0

Browse files
ianlancetaylorgopherbot
authored andcommitted
runtime: don't frob isSending for tickers
The Ticker Stop and Reset methods don't report a value, so we don't need to track whether they are interrupting a send. This includes a test that used to fail about 2% of the time on my laptop when run under x/tools/cmd/stress. Change-Id: Ic6d14b344594149dd3c24b37bbe4e42e83f9a9ad Reviewed-on: https://go-review.googlesource.com/c/go/+/620136 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Auto-Submit: Michael Knyszek <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Michael Knyszek <[email protected]>
1 parent 1f51b82 commit 48849e0

File tree

2 files changed

+36
-6
lines changed

2 files changed

+36
-6
lines changed

src/runtime/time.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type timer struct {
3333
// isSending is used to handle races between running a
3434
// channel timer and stopping or resetting the timer.
3535
// It is used only for channel timers (t.isChan == true).
36+
// It is not used for tickers.
3637
// The lowest zero bit is set when about to send a value on the channel,
3738
// and cleared after sending the value.
3839
// The stop/reset code uses this to detect whether it
@@ -467,7 +468,7 @@ func (t *timer) stop() bool {
467468
// send from actually happening. That means
468469
// that we should return true: the timer was
469470
// stopped, even though t.when may be zero.
470-
if t.isSending.Load() > 0 {
471+
if t.period == 0 && t.isSending.Load() > 0 {
471472
pending = true
472473
}
473474
}
@@ -529,6 +530,7 @@ func (t *timer) modify(when, period int64, f func(arg any, seq uintptr, delay in
529530
t.maybeRunAsync()
530531
}
531532
t.trace("modify")
533+
oldPeriod := t.period
532534
t.period = period
533535
if f != nil {
534536
t.f = f
@@ -570,7 +572,7 @@ func (t *timer) modify(when, period int64, f func(arg any, seq uintptr, delay in
570572
// send from actually happening. That means
571573
// that we should return true: the timer was
572574
// stopped, even though t.when may be zero.
573-
if t.isSending.Load() > 0 {
575+
if oldPeriod == 0 && t.isSending.Load() > 0 {
574576
pending = true
575577
}
576578
}
@@ -1064,7 +1066,7 @@ func (t *timer) unlockAndRun(now int64) {
10641066

10651067
async := debug.asynctimerchan.Load() != 0
10661068
var isSendingClear uint8
1067-
if !async && t.isChan {
1069+
if !async && t.isChan && t.period == 0 {
10681070
// Tell Stop/Reset that we are sending a value.
10691071
// Set the lowest zero bit.
10701072
// We do this awkward step because atomic.Uint8
@@ -1115,9 +1117,12 @@ func (t *timer) unlockAndRun(now int64) {
11151117
// true meaning that no value was sent.
11161118
lock(&t.sendLock)
11171119

1118-
// We are committed to possibly sending a value based on seq,
1119-
// so no need to keep telling stop/modify that we are sending.
1120-
t.isSending.And(^isSendingClear)
1120+
if t.period == 0 {
1121+
// We are committed to possibly sending a value
1122+
// based on seq, so no need to keep telling
1123+
// stop/modify that we are sending.
1124+
t.isSending.And(^isSendingClear)
1125+
}
11211126

11221127
if t.seq != seq {
11231128
f = func(any, uintptr, int64) {}

src/time/sleep_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,31 @@ func testStopResetResultGODEBUG(t *testing.T, testStop bool, godebug string) {
847847
wg.Wait()
848848
}
849849

850+
// Test having a large number of goroutines wake up a timer simultaneously.
851+
// This used to trigger a crash when run under x/tools/cmd/stress.
852+
func TestMultiWakeup(t *testing.T) {
853+
if testing.Short() {
854+
t.Skip("-short")
855+
}
856+
857+
goroutines := runtime.GOMAXPROCS(0)
858+
timer := NewTicker(Microsecond)
859+
var wg sync.WaitGroup
860+
wg.Add(goroutines)
861+
for range goroutines {
862+
go func() {
863+
defer wg.Done()
864+
for range 100000 {
865+
select {
866+
case <-timer.C:
867+
case <-After(Millisecond):
868+
}
869+
}
870+
}()
871+
}
872+
wg.Wait()
873+
}
874+
850875
// Benchmark timer latency when the thread that creates the timer is busy with
851876
// other work and the timers must be serviced by other threads.
852877
// https://golang.org/issue/38860

0 commit comments

Comments
 (0)