From: Damien Neil Date: Tue, 19 Nov 2024 18:42:11 +0000 (-0800) Subject: testing/synctest: add experimental synctest package X-Git-Tag: go1.24rc1~200 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=76e4efdc77861954969ce192966e1595c268d8c1;p=gostls13.git testing/synctest: add experimental synctest package The testing/synctest package is experimental, subject to change or removal, and only present when GOEXPERIMENT=synctest. Fixes #69687 Change-Id: I6c79e7d1479a54bebcd2de754854c87bfd0a5fa1 Reviewed-on: https://go-review.googlesource.com/c/go/+/629735 Reviewed-by: Michael Pratt LUCI-TryBot-Result: Go LUCI --- diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 365efa7e25..58504ed7da 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -650,7 +650,8 @@ var depsRules = ` < testing; RUNTIME - < internal/synctest; + < internal/synctest + < testing/synctest; log/slog, testing < testing/slogtest; diff --git a/src/internal/goexperiment/flags.go b/src/internal/goexperiment/flags.go index e654471a6a..31b3d0315b 100644 --- a/src/internal/goexperiment/flags.go +++ b/src/internal/goexperiment/flags.go @@ -125,4 +125,7 @@ type Flags struct { // SyncHashTrieMap enables the HashTrieMap sync.Map implementation. SyncHashTrieMap bool + + // Synctest enables the testing/synctest package. + Synctest bool } diff --git a/src/internal/synctest/synctest.go b/src/internal/synctest/synctest.go index eb317fbf82..7714a82bf4 100644 --- a/src/internal/synctest/synctest.go +++ b/src/internal/synctest/synctest.go @@ -3,60 +3,16 @@ // license that can be found in the LICENSE file. // Package synctest provides support for testing concurrent code. +// +// See the testing/synctest package for function documentation. package synctest import ( _ "unsafe" // for go:linkname ) -// Run executes f in a new goroutine. -// -// The new goroutine and any goroutines transitively started by it form -// an isolated "bubble". -// Run waits for all goroutines in the bubble to exit before returning. -// -// Goroutines in the bubble use a synthetic time implementation. -// The initial time is midnight UTC 2000-01-01. -// -// Time advances when every goroutine in the bubble is blocked. -// For example, a call to time.Sleep will block until all other -// goroutines are blocked and return after the bubble's clock has -// advanced. See [Wait] for the specific definition of blocked. -// -// If every goroutine is blocked and there are no timers scheduled, -// Run panics. -// -// Channels, time.Timers, and time.Tickers created within the bubble -// are associated with it. Operating on a bubbled channel, timer, or ticker -// from outside the bubble panics. -// //go:linkname Run func Run(f func()) -// Wait blocks until every goroutine within the current bubble, -// other than the current goroutine, is durably blocked. -// It panics if called from a non-bubbled goroutine, -// or if two goroutines in the same bubble call Wait at the same time. -// -// A goroutine is durably blocked if can only be unblocked by another -// goroutine in its bubble. The following operations durably block -// a goroutine: -// - a send or receive on a channel from within the bubble -// - a select statement where every case is a channel within the bubble -// - sync.Cond.Wait -// - time.Sleep -// -// A goroutine executing a system call or waiting for an external event -// such as a network operation is not durably blocked. -// For example, a goroutine blocked reading from an network connection -// is not durably blocked even if no data is currently available on the -// connection, because it may be unblocked by data written from outside -// the bubble or may be in the process of receiving data from a kernel -// network buffer. -// -// A goroutine is not durably blocked when blocked on a send or receive -// on a channel that was not created within its bubble, because it may -// be unblocked by a channel receive or send from outside its bubble. -// //go:linkname Wait func Wait() diff --git a/src/testing/synctest/synctest.go b/src/testing/synctest/synctest.go new file mode 100644 index 0000000000..90efc789de --- /dev/null +++ b/src/testing/synctest/synctest.go @@ -0,0 +1,67 @@ +// Copyright 2024 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. + +//go:build goexperiment.synctest + +// Package synctest provides support for testing concurrent code. +// +// This package only exists when using Go compiled with GOEXPERIMENT=synctest. +// It is experimental, and not subject to the Go 1 compatibility promise. +package synctest + +import ( + "internal/synctest" +) + +// Run executes f in a new goroutine. +// +// The new goroutine and any goroutines transitively started by it form +// an isolated "bubble". +// Run waits for all goroutines in the bubble to exit before returning. +// +// Goroutines in the bubble use a synthetic time implementation. +// The initial time is midnight UTC 2000-01-01. +// +// Time advances when every goroutine in the bubble is blocked. +// For example, a call to time.Sleep will block until all other +// goroutines are blocked and return after the bubble's clock has +// advanced. See [Wait] for the specific definition of blocked. +// +// If every goroutine is blocked and there are no timers scheduled, +// Run panics. +// +// Channels, time.Timers, and time.Tickers created within the bubble +// are associated with it. Operating on a bubbled channel, timer, or ticker +// from outside the bubble panics. +func Run(f func()) { + synctest.Run(f) +} + +// Wait blocks until every goroutine within the current bubble, +// other than the current goroutine, is durably blocked. +// It panics if called from a non-bubbled goroutine, +// or if two goroutines in the same bubble call Wait at the same time. +// +// A goroutine is durably blocked if can only be unblocked by another +// goroutine in its bubble. The following operations durably block +// a goroutine: +// - a send or receive on a channel from within the bubble +// - a select statement where every case is a channel within the bubble +// - sync.Cond.Wait +// - time.Sleep +// +// A goroutine executing a system call or waiting for an external event +// such as a network operation is not durably blocked. +// For example, a goroutine blocked reading from an network connection +// is not durably blocked even if no data is currently available on the +// connection, because it may be unblocked by data written from outside +// the bubble or may be in the process of receiving data from a kernel +// network buffer. +// +// A goroutine is not durably blocked when blocked on a send or receive +// on a channel that was not created within its bubble, because it may +// be unblocked by a channel receive or send from outside its bubble. +func Wait() { + synctest.Wait() +}