Skip to content

Commit d90ce58

Browse files
committed
internal/synctest: new package for testing concurrent code
Add an internal (for now) implementation of testing/synctest. The synctest.Run function executes a tree of goroutines in an isolated environment using a fake clock. The synctest.Wait function allows a test to wait for all other goroutines within the test to reach a blocking point. For #67434 For #69687 Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc Reviewed-on: https://go-review.googlesource.com/c/go/+/591997 Reviewed-by: Michael Pratt <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 944df9a commit d90ce58

26 files changed

+1154
-35
lines changed

src/go/build/deps_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,9 @@ var depsRules = `
643643
FMT, DEBUG, flag, runtime/trace, internal/sysinfo, math/rand
644644
< testing;
645645
646+
RUNTIME
647+
< internal/synctest;
648+
646649
log/slog, testing
647650
< testing/slogtest;
648651

src/internal/synctest/synctest.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright 2024 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// Package synctest provides support for testing concurrent code.
6+
package synctest
7+
8+
import (
9+
_ "unsafe" // for go:linkname
10+
)
11+
12+
// Run executes f in a new goroutine.
13+
//
14+
// The new goroutine and any goroutines transitively started by it form
15+
// an isolated "bubble".
16+
// Run waits for all goroutines in the bubble to exit before returning.
17+
//
18+
// Goroutines in the bubble use a synthetic time implementation.
19+
// The initial time is midnight UTC 2000-01-01.
20+
//
21+
// Time advances when every goroutine in the bubble is blocked.
22+
// For example, a call to time.Sleep will block until all other
23+
// goroutines are blocked and return after the bubble's clock has
24+
// advanced. See [Wait] for the specific definition of blocked.
25+
//
26+
// If every goroutine is blocked and there are no timers scheduled,
27+
// Run panics.
28+
//
29+
// Channels, time.Timers, and time.Tickers created within the bubble
30+
// are associated with it. Operating on a bubbled channel, timer, or ticker
31+
// from outside the bubble panics.
32+
//
33+
//go:linkname Run
34+
func Run(f func())
35+
36+
// Wait blocks until every goroutine within the current bubble,
37+
// other than the current goroutine, is durably blocked.
38+
// It panics if called from a non-bubbled goroutine,
39+
// or if two goroutines in the same bubble call Wait at the same time.
40+
//
41+
// A goroutine is durably blocked if can only be unblocked by another
42+
// goroutine in its bubble. The following operations durably block
43+
// a goroutine:
44+
// - a send or receive on a channel from within the bubble
45+
// - a select statement where every case is a channel within the bubble
46+
// - sync.Cond.Wait
47+
// - time.Sleep
48+
//
49+
// A goroutine executing a system call or waiting for an external event
50+
// such as a network operation is not durably blocked.
51+
// For example, a goroutine blocked reading from an network connection
52+
// is not durably blocked even if no data is currently available on the
53+
// connection, because it may be unblocked by data written from outside
54+
// the bubble or may be in the process of receiving data from a kernel
55+
// network buffer.
56+
//
57+
// A goroutine is not durably blocked when blocked on a send or receive
58+
// on a channel that was not created within its bubble, because it may
59+
// be unblocked by a channel receive or send from outside its bubble.
60+
//
61+
//go:linkname Wait
62+
func Wait()

0 commit comments

Comments
 (0)