Skip to content
Snippets Groups Projects
Commit 4ce116a8 authored by Damien Neil's avatar Damien Neil Committed by Gopher Robot
Browse files

runtime: avoid panic in expired synctest timer chan read

When reading from time.Timer.C for an expired timer using
a fake clock (in a synctest bubble), the timer will not
be in a heap. Avoid a spurious panic claiming the timer
moved between synctest bubbles.

Drop the panic when a bubbled goroutine reads from a
non-bubbled timer channel: We allow bubbled goroutines
to access non-bubbled channels in general.

Fixes #70741

Change-Id: I27005e46f4d0067cc6846d234d22766d2e05d163
Reviewed-on: https://go-review.googlesource.com/c/go/+/634955


Auto-Submit: Damien Neil <dneil@google.com>
Reviewed-by: default avatarMichael Pratt <mpratt@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
parent e6de1b2d
No related branches found
No related tags found
No related merge requests found
......@@ -105,7 +105,7 @@ func TestMallocs(t *testing.T) {
}
}
func TestTimer(t *testing.T) {
func TestTimerReadBeforeDeadline(t *testing.T) {
synctest.Run(func() {
start := time.Now()
tm := time.NewTimer(5 * time.Second)
......@@ -116,6 +116,41 @@ func TestTimer(t *testing.T) {
})
}
func TestTimerReadAfterDeadline(t *testing.T) {
synctest.Run(func() {
delay := 1 * time.Second
want := time.Now().Add(delay)
tm := time.NewTimer(delay)
time.Sleep(2 * delay)
got := <-tm.C
if got != want {
t.Errorf("<-tm.C = %v, want %v", got, want)
}
})
}
func TestTimerReset(t *testing.T) {
synctest.Run(func() {
start := time.Now()
tm := time.NewTimer(1 * time.Second)
if got, want := <-tm.C, start.Add(1*time.Second); got != want {
t.Errorf("first sleep: <-tm.C = %v, want %v", got, want)
}
tm.Reset(2 * time.Second)
if got, want := <-tm.C, start.Add((1+2)*time.Second); got != want {
t.Errorf("second sleep: <-tm.C = %v, want %v", got, want)
}
tm.Reset(3 * time.Second)
time.Sleep(1 * time.Second)
tm.Reset(3 * time.Second)
if got, want := <-tm.C, start.Add((1+2+4)*time.Second); got != want {
t.Errorf("third sleep: <-tm.C = %v, want %v", got, want)
}
})
}
func TestTimeAfter(t *testing.T) {
synctest.Run(func() {
i := 0
......@@ -138,9 +173,11 @@ func TestTimeAfter(t *testing.T) {
func TestTimerFromOutsideBubble(t *testing.T) {
tm := time.NewTimer(10 * time.Millisecond)
synctest.Run(func() {
defer wantPanic(t, "timer moved between synctest groups")
<-tm.C
})
if tm.Stop() {
t.Errorf("synctest.Run unexpectedly returned before timer fired")
}
}
func TestChannelFromOutsideBubble(t *testing.T) {
......
......@@ -1370,15 +1370,19 @@ func badTimer() {
// to send a value to its associated channel. If so, it does.
// The timer must not be locked.
func (t *timer) maybeRunChan() {
if sg := getg().syncGroup; sg != nil || t.isFake {
if t.isFake {
t.lock()
var timerGroup *synctestGroup
if t.ts != nil {
timerGroup = t.ts.syncGroup
}
t.unlock()
if sg == nil || !t.isFake || sg != timerGroup {
panic(plainError("timer moved between synctest groups"))
sg := getg().syncGroup
if sg == nil {
panic(plainError("synctest timer accessed from outside bubble"))
}
if timerGroup != nil && sg != timerGroup {
panic(plainError("timer moved between synctest bubbles"))
}
// No need to do anything here.
// synctest.Run will run the timer when it advances its fake clock.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment