From d90ce588eac7b9105c0ca556a7c6e975fd5c1eca Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Tue, 11 Jun 2024 11:02:18 -0700 Subject: 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 LUCI-TryBot-Result: Go LUCI --- src/runtime/runtime2.go | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'src/runtime/runtime2.go') diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 03798d5699..e837c28af8 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -489,7 +489,8 @@ type g struct { // current in-progress goroutine profile goroutineProfiled goroutineProfileStateHolder - coroarg *coro // argument during coroutine transfers + coroarg *coro // argument during coroutine transfers + syncGroup *synctestGroup // Per-G tracer state. trace gTraceState @@ -1064,6 +1065,7 @@ const ( waitReasonSyncMutexLock // "sync.Mutex.Lock" waitReasonSyncRWMutexRLock // "sync.RWMutex.RLock" waitReasonSyncRWMutexLock // "sync.RWMutex.Lock" + waitReasonSyncWaitGroupWait // "sync.WaitGroup.Wait" waitReasonTraceReaderBlocked // "trace reader (blocked)" waitReasonWaitForGCCycle // "wait for GC cycle" waitReasonGCWorkerIdle // "GC worker (idle)" @@ -1078,6 +1080,11 @@ const ( waitReasonPageTraceFlush // "page trace flush" waitReasonCoroutine // "coroutine" waitReasonGCWeakToStrongWait // "GC weak to strong wait" + waitReasonSynctestRun // "synctest.Run" + waitReasonSynctestWait // "synctest.Wait" + waitReasonSynctestChanReceive // "chan receive (synctest)" + waitReasonSynctestChanSend // "chan send (synctest)" + waitReasonSynctestSelect // "select (synctest)" ) var waitReasonStrings = [...]string{ @@ -1105,6 +1112,7 @@ var waitReasonStrings = [...]string{ waitReasonSyncMutexLock: "sync.Mutex.Lock", waitReasonSyncRWMutexRLock: "sync.RWMutex.RLock", waitReasonSyncRWMutexLock: "sync.RWMutex.Lock", + waitReasonSyncWaitGroupWait: "sync.WaitGroup.Wait", waitReasonTraceReaderBlocked: "trace reader (blocked)", waitReasonWaitForGCCycle: "wait for GC cycle", waitReasonGCWorkerIdle: "GC worker (idle)", @@ -1119,6 +1127,11 @@ var waitReasonStrings = [...]string{ waitReasonPageTraceFlush: "page trace flush", waitReasonCoroutine: "coroutine", waitReasonGCWeakToStrongWait: "GC weak to strong wait", + waitReasonSynctestRun: "synctest.Run", + waitReasonSynctestWait: "synctest.Wait", + waitReasonSynctestChanReceive: "chan receive (synctest)", + waitReasonSynctestChanSend: "chan send (synctest)", + waitReasonSynctestSelect: "select (synctest)", } func (w waitReason) String() string { @@ -1157,6 +1170,26 @@ var isWaitingForGC = [len(waitReasonStrings)]bool{ waitReasonFlushProcCaches: true, } +func (w waitReason) isIdleInSynctest() bool { + return isIdleInSynctest[w] +} + +// isIdleInSynctest indicates that a goroutine is considered idle by synctest.Wait. +var isIdleInSynctest = [len(waitReasonStrings)]bool{ + waitReasonChanReceiveNilChan: true, + waitReasonChanSendNilChan: true, + waitReasonSelectNoCases: true, + waitReasonSleep: true, + waitReasonSyncCondWait: true, + waitReasonSyncWaitGroupWait: true, + waitReasonCoroutine: true, + waitReasonSynctestRun: true, + waitReasonSynctestWait: true, + waitReasonSynctestChanReceive: true, + waitReasonSynctestChanSend: true, + waitReasonSynctestSelect: true, +} + var ( allm *m gomaxprocs int32 -- cgit v1.3-6-g1900