aboutsummaryrefslogtreecommitdiff
path: root/src/runtime
diff options
context:
space:
mode:
authorDamien Neil <dneil@google.com>2025-04-01 15:43:22 -0700
committerGopher Robot <gobot@golang.org>2025-05-07 13:14:25 -0700
commitd6c8bedc7b3d2de7714dca75bd05912b371538f1 (patch)
tree352d43791d26d75bed756a7b02ef69b872483872 /src/runtime
parent4e63ae46e097062b72424b2ac1da6e7dac33064b (diff)
downloadgo-d6c8bedc7b3d2de7714dca75bd05912b371538f1.tar.xz
runtime, testing/synctest: stop advancing time when main goroutine exits
Once the goroutine started by synctest.Run exits, stop advancing the fake clock in its bubble. This avoids confusing situations where a bubble remains alive indefinitely while a background goroutine reads from a time.Ticker or otherwise advances the clock. For #67434 Change-Id: Id608ffe3c7d7b07747b56a21f365787fb9a057d7 Reviewed-on: https://go-review.googlesource.com/c/go/+/662155 Reviewed-by: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Damien Neil <dneil@google.com>
Diffstat (limited to 'src/runtime')
-rw-r--r--src/runtime/synctest.go34
1 files changed, 26 insertions, 8 deletions
diff --git a/src/runtime/synctest.go b/src/runtime/synctest.go
index b197758ad9..36d6fa67c7 100644
--- a/src/runtime/synctest.go
+++ b/src/runtime/synctest.go
@@ -5,6 +5,7 @@
package runtime
import (
+ "internal/runtime/sys"
"unsafe"
)
@@ -15,7 +16,9 @@ type synctestGroup struct {
now int64 // current fake time
root *g // caller of synctest.Run
waiter *g // caller of synctest.Wait
+ main *g // goroutine started by synctest.Run
waiting bool // true if a goroutine is calling synctest.Wait
+ done bool // true if main has exited
// The group is active (not blocked) so long as running > 0 || active > 0.
//
@@ -60,6 +63,9 @@ func (sg *synctestGroup) changegstatus(gp *g, oldval, newval uint32) {
case _Gdead:
isRunning = false
totalDelta--
+ if gp == sg.main {
+ sg.done = true
+ }
case _Gwaiting:
if gp.waitreason.isIdleInSynctest() {
isRunning = false
@@ -167,24 +173,32 @@ func synctestRun(f func()) {
if gp.syncGroup != nil {
panic("synctest.Run called from within a synctest bubble")
}
- gp.syncGroup = &synctestGroup{
+ sg := &synctestGroup{
total: 1,
running: 1,
root: gp,
}
const synctestBaseTime = 946684800000000000 // midnight UTC 2000-01-01
- gp.syncGroup.now = synctestBaseTime
- gp.syncGroup.timers.syncGroup = gp.syncGroup
- lockInit(&gp.syncGroup.mu, lockRankSynctest)
- lockInit(&gp.syncGroup.timers.mu, lockRankTimers)
+ sg.now = synctestBaseTime
+ sg.timers.syncGroup = sg
+ lockInit(&sg.mu, lockRankSynctest)
+ lockInit(&sg.timers.mu, lockRankTimers)
+
+ gp.syncGroup = sg
defer func() {
gp.syncGroup = nil
}()
- fv := *(**funcval)(unsafe.Pointer(&f))
- newproc(fv)
+ // This is newproc, but also records the new g in sg.main.
+ pc := sys.GetCallerPC()
+ systemstack(func() {
+ fv := *(**funcval)(unsafe.Pointer(&f))
+ sg.main = newproc1(fv, gp, pc, false, waitReasonZero)
+ pp := getg().m.p.ptr()
+ runqput(pp, sg.main, true)
+ wakep()
+ })
- sg := gp.syncGroup
lock(&sg.mu)
sg.active++
for {
@@ -209,6 +223,10 @@ func synctestRun(f func()) {
if next < sg.now {
throw("time went backwards")
}
+ if sg.done {
+ // Time stops once the bubble's main goroutine has exited.
+ break
+ }
sg.now = next
}