@@ -381,16 +381,18 @@ type workType struct {
381
381
382
382
// The following fields monitor the GC phase of the current cycle during
383
383
// goroutine leak detection.
384
- //
385
- // - pendingGoleakDetection: The GC has been instructed to perform goroutine leak
386
- // detection during the next GC cycle; it is set by DetectGoroutineLeaks()
387
- // and unset during gcStart().
388
- // - detectingGoleaks: The GC is running in goroutine leak detection mode; it is set
389
- // during gcStart() and unset during gcMarkTermination().
390
- // - detectedGoleaks: The GC has performed goroutine leak detection during the current
391
- // GC cycle; it is set during gcMarkDone(), right after goroutine leak detection has concluded,
392
- // and unset during gcStart().
393
- pendingGoleakDetection , detectingGoleaks , detectedGoleaks bool
384
+ goroutineLeakFinder struct {
385
+ // The GC has been instructed to perform goroutine leak detection during the next GC cycle;
386
+ // it is set by DetectGoroutineLeaks() and unset during gcStart().
387
+ pending atomic.Bool
388
+ // The GC is running in goroutine leak detection mode; it is set during gcStart()
389
+ // and unset during gcMarkTermination(). Is protected by STW.
390
+ enabled bool
391
+ // The GC has performed goroutine leak detection during the current GC cycle; it is set
392
+ // during gcMarkDone(), right after goroutine leak detection has concluded, and unset during
393
+ // gcStart(). Is protected by STW.
394
+ done bool
395
+ }
394
396
395
397
// Base indexes of each root type. Set by gcPrepareMarkRoots.
396
398
baseData , baseBSS , baseSpans , baseStacks , baseEnd uint32
@@ -571,22 +573,17 @@ func GC() {
571
573
// FindGoleaks instructs the Go garbage collector to attempt
572
574
// goroutine leak detection during the next GC cycle.
573
575
//
574
- // Only operates if golfgc is enabled in GOEXPERIMENT.
576
+ // Only operates if goroutineleakfindergc is enabled in GOEXPERIMENT.
575
577
// Otherwise, it just runs runtime.GC().
576
578
func FindGoLeaks () {
577
- if ! goexperiment .GolfGC {
579
+ if ! goexperiment .GoroutineLeakFinderGC {
578
580
GC ()
579
581
return
580
582
}
581
583
582
- // This write should be thread-safe, as the overwritten value is true.
583
- // pendingGoleakDetection is only set to false under STW at the start
584
- // of the GC cycle that picks it up.
585
- work .pendingGoleakDetection = true
584
+ work .goroutineLeakFinder .pending .Store (true )
586
585
587
- // This read should be thread-safe for the same reason as the write above above.
588
- // At most, we trigger the GC an additional time.
589
- for work .pendingGoleakDetection {
586
+ for work .goroutineLeakFinder .pending .Load () {
590
587
GC ()
591
588
}
592
589
}
@@ -736,8 +733,8 @@ func gcStart(trigger gcTrigger) {
736
733
mode = gcForceMode
737
734
} else if debug .gcstoptheworld == 2 {
738
735
mode = gcForceBlockMode
739
- } else if goexperiment .GolfGC {
740
- if work .pendingGoleakDetection {
736
+ } else if goexperiment .GoroutineLeakFinderGC {
737
+ if work .goroutineLeakFinder . pending . Load () {
741
738
// Fully stop the world if running deadlock detection.
742
739
mode = gcForceBlockMode
743
740
}
@@ -803,7 +800,7 @@ func gcStart(trigger gcTrigger) {
803
800
clearpools ()
804
801
805
802
work .cycles .Add (1 )
806
- work .detectedGoleaks = false
803
+ work .goroutineLeakFinder . done = false
807
804
808
805
// Assists and workers can start the moment we start
809
806
// the world.
@@ -835,12 +832,9 @@ func gcStart(trigger gcTrigger) {
835
832
// possible.
836
833
setGCPhase (_GCmark )
837
834
838
- if goexperiment .GolfGC {
839
- if work .pendingGoleakDetection {
840
- // Write is thread-safe because the world is stopped
841
- work .detectingGoleaks = true
842
- work .pendingGoleakDetection = false
843
- }
835
+ if work .goroutineLeakFinder .pending .Load () {
836
+ work .goroutineLeakFinder .enabled = true
837
+ work .goroutineLeakFinder .pending .Store (false )
844
838
}
845
839
846
840
gcBgMarkPrepare () // Must happen before assists are enabled.
@@ -943,10 +937,8 @@ func gcMarkDone() {
943
937
// Ensure only one thread is running the ragged barrier at a
944
938
// time.
945
939
semacquire (& work .markDoneSema )
946
- if goexperiment .GolfGC {
947
- if work .detectingGoleaks {
948
- gcDiscoverMoreStackRoots ()
949
- }
940
+ if work .goroutineLeakFinder .enabled {
941
+ gcDiscoverMoreStackRoots ()
950
942
}
951
943
952
944
top:
@@ -1007,7 +999,8 @@ top:
1007
999
// communicated work since we took markDoneSema. Therefore
1008
1000
// there are no grey objects and no more objects can be
1009
1001
// shaded. Transition to mark termination.
1010
- var now int64
1002
+ now := nanotime ()
1003
+ work .tMarkTerm = now
1011
1004
getg ().m .preemptoff = "gcing"
1012
1005
var stw worldStop
1013
1006
systemstack (func () {
@@ -1053,44 +1046,13 @@ top:
1053
1046
})
1054
1047
semrelease (& worldsema )
1055
1048
goto top
1056
- } else if goexperiment .GolfGC {
1049
+ } else if goexperiment .GoroutineLeakFinderGC {
1057
1050
// If we are detecting goroutine leaks, do so now.
1058
- if work .detectingGoleaks && ! work .detectedGoleaks {
1051
+ if work .goroutineLeakFinder . enabled && ! work .goroutineLeakFinder . done {
1059
1052
// Detect goroutine leaks. If the returned value is true, then
1060
1053
// detection was performed during this cycle. Otherwise, more mark work is needed,
1061
1054
// or live goroutines were found.
1062
- work .detectedGoleaks = findGoleaks ()
1063
-
1064
- getg ().m .preemptoff = ""
1065
- systemstack (func () {
1066
- // Accumulate the time we were stopped before we had to start again.
1067
- work .cpuStats .accumulateGCPauseTime (nanotime ()- stw .finishedStopping , work .maxprocs )
1068
-
1069
- now := startTheWorldWithSema (0 , stw )
1070
- work .pauseNS += now - stw .startedStopping
1071
- })
1072
- semrelease (& worldsema )
1073
- goto top
1074
- }
1075
-
1076
- now = nanotime ()
1077
- work .tMarkTerm = now
1078
- // Check again whether any P needs to flush its write barrier
1079
- // to the GC work queue.
1080
- systemstack (func () {
1081
- for _ , p := range allp {
1082
- wbBufFlush1 (p )
1083
- if ! p .gcw .empty () {
1084
- restart = true
1085
- break
1086
- }
1087
- }
1088
- })
1089
-
1090
- // If that is the case, restart again. Once restarts are no longer needed,
1091
- // run this without deadlock detection.
1092
- if restart {
1093
- gcDebugMarkDone .restartedDueTo27993 = true
1055
+ work .goroutineLeakFinder .done = findGoleaks ()
1094
1056
1095
1057
getg ().m .preemptoff = ""
1096
1058
systemstack (func () {
@@ -1141,24 +1103,11 @@ top:
1141
1103
gcMarkTermination (stw )
1142
1104
}
1143
1105
1144
- // Check if an object is marked in the heap.
1145
- func checkIfMarked (p unsafe.Pointer ) bool {
1146
- obj , span , objIndex := findObject (uintptr (p ), 0 , 0 )
1147
- if obj != 0 {
1148
- mbits := span .markBitsForIndex (objIndex )
1149
- return mbits .isMarked ()
1150
- }
1151
- // if we fall through to get here, we are within the stack ranges of reachable goroutines
1152
- return true
1153
- }
1154
-
1155
- // maybeLive checks whether a goroutine may still be semantically runnable.
1156
- // This returns true if the goroutine is waiting on at least one concurrency primitive
1157
- // which is reachable in memory, i.e., has been by the GC.
1158
- //
1106
+ // checkIfMaybeRunnable checks whether a goroutine may still be semantically runnable.
1159
1107
// For goroutines which are semantically runnable, this will eventually return true
1160
- // as the GC marking phase progresses.
1161
- func (gp * g ) maybeLive () bool {
1108
+ // as the GC marking phase progresses. It returns false for leaked goroutines, or for
1109
+ // goroutines which are not yet computed as possibly runnable by the GC.
1110
+ func (gp * g ) checkIfMaybeRunnable () bool {
1162
1111
// Unmask the goroutine address to ensure we are not
1163
1112
// dereferencing a masked address.
1164
1113
gp = gp .unmask ()
@@ -1176,7 +1125,7 @@ func (gp *g) maybeLive() bool {
1176
1125
// Cycle all through all *sudog to check whether
1177
1126
// the goroutine is waiting on a marked channel.
1178
1127
for sg := gp .waiting ; sg != nil ; sg = sg .waitlink {
1179
- if checkIfMarked (unsafe .Pointer (sg .c )) {
1128
+ if isMarkedOrNotInHeap (unsafe .Pointer (sg .c )) {
1180
1129
return true
1181
1130
}
1182
1131
}
@@ -1190,7 +1139,7 @@ func (gp *g) maybeLive() bool {
1190
1139
// check if the synchronization primitive attached to the sudog is marked.
1191
1140
if gp .waiting != nil {
1192
1141
// Unmask the sema address and check if it's marked.
1193
- return checkIfMarked (gcUnmask (gp .waiting .elem ))
1142
+ return isMarkedOrNotInHeap (gcUnmask (gp .waiting .elem ))
1194
1143
}
1195
1144
}
1196
1145
return true
@@ -1223,13 +1172,13 @@ func gcDiscoverMoreStackRoots() {
1223
1172
// Reorder goroutine list
1224
1173
for vIndex < ivIndex {
1225
1174
gp := work .stackRoots [vIndex ]
1226
- if gp .maybeLive () {
1175
+ if gp .checkIfMaybeRunnable () {
1227
1176
work .stackRoots [vIndex ] = gp
1228
1177
vIndex = vIndex + 1
1229
1178
continue
1230
1179
}
1231
1180
for ivIndex = ivIndex - 1 ; ivIndex != vIndex ; ivIndex = ivIndex - 1 {
1232
- if swapGp := work .stackRoots [ivIndex ]; swapGp .maybeLive () {
1181
+ if swapGp := work .stackRoots [ivIndex ]; swapGp .checkIfMaybeRunnable () {
1233
1182
work .stackRoots [ivIndex ] = gp
1234
1183
work .stackRoots [vIndex ] = swapGp .unmask ()
1235
1184
vIndex = vIndex + 1
@@ -1268,7 +1217,7 @@ func findGoleaks() bool {
1268
1217
var foundMoreWork bool
1269
1218
for i := work .nLiveStackRoots ; i < work .nStackRoots ; i ++ {
1270
1219
gp := work .stackRoots [i ].unmask ()
1271
- if readgstatus (gp ) == _Gwaiting && ! gp .maybeLive () {
1220
+ if readgstatus (gp ) == _Gwaiting && ! gp .checkIfMaybeRunnable () {
1272
1221
// Blocking unrunnable goroutines will be skipped.
1273
1222
continue
1274
1223
}
@@ -1459,12 +1408,8 @@ func gcMarkTermination(stw worldStop) {
1459
1408
}
1460
1409
1461
1410
systemstack (func () {
1462
- if goexperiment .GolfGC {
1463
- // Pull the GC out of goroutine leak detection mode.
1464
- // Write is thread-safe because the world is stopped, and only one
1465
- // GC cycle can run at a time.
1466
- work .detectingGoleaks = false
1467
- }
1411
+ // Pull the GC out of goroutine leak detection mode.
1412
+ work .goroutineLeakFinder .enabled = false
1468
1413
1469
1414
// The memstats updated above must be updated with the world
1470
1415
// stopped to ensure consistency of some values, such as
@@ -1893,12 +1838,12 @@ func gcMarkWorkAvailable(p *p) bool {
1893
1838
if ! work .full .empty () || ! work .spanq .empty () {
1894
1839
return true // global work available
1895
1840
}
1896
- if ! work .detectingGoleaks {
1897
- return work .markrootNext < work .markrootJobs
1898
- }
1899
1841
rootNext := atomic .Load (& work .markrootNext )
1900
1842
rootJobs := atomic .Load (& work .markrootJobs )
1901
- return rootNext < rootJobs
1843
+ if rootNext < rootJobs {
1844
+ return true // root scan work available
1845
+ }
1846
+ return false
1902
1847
}
1903
1848
1904
1849
// gcMark runs the mark (or, for concurrent GC, mark termination)
0 commit comments