Skip to content

Commit bd5595d

Browse files
WangLeonardmvdan
authored andcommitted
runtime: refactor finalizer goroutine status
Use an atomic.Uint32 to represent the state of finalizer goroutine. fingStatus will only be changed to fingWake in non fingWait state, so it is safe to set fingRunningFinalizer status in runfinq. name old time/op new time/op delta Finalizer-8 592µs ± 4% 561µs ± 1% -5.22% (p=0.000 n=10+10) FinalizerRun-8 694ns ± 6% 675ns ± 7% ~ (p=0.059 n=9+8) Change-Id: I7e4da30cec98ce99f7d8cf4c97f933a8a2d1cae1 Reviewed-on: https://go-review.googlesource.com/c/go/+/400134 Reviewed-by: Joedian Reid <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Daniel Martí <[email protected]> Reviewed-by: Michael Pratt <[email protected]>
1 parent 67e6542 commit bd5595d

File tree

5 files changed

+32
-28
lines changed

5 files changed

+32
-28
lines changed

src/runtime/export_test.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,10 +1264,7 @@ func SetIntArgRegs(a int) int {
12641264
}
12651265

12661266
func FinalizerGAsleep() bool {
1267-
lock(&finlock)
1268-
result := fingwait
1269-
unlock(&finlock)
1270-
return result
1267+
return fingStatus.Load()&fingWait != 0
12711268
}
12721269

12731270
// For GCTestMoveStackOnNextCall, it's important not to introduce an

src/runtime/mfinal.go

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,23 @@ type finblock struct {
2929
fin [(_FinBlockSize - 2*goarch.PtrSize - 2*4) / unsafe.Sizeof(finalizer{})]finalizer
3030
}
3131

32+
var fingStatus atomic.Uint32
33+
34+
// finalizer goroutine status.
35+
const (
36+
fingUninitialized uint32 = iota
37+
fingCreated uint32 = 1 << (iota - 1)
38+
fingRunningFinalizer
39+
fingWait
40+
fingWake
41+
)
42+
3243
var finlock mutex // protects the following variables
3344
var fing *g // goroutine that runs finalizers
3445
var finq *finblock // list of finalizers that are to be executed
3546
var finc *finblock // cache of free blocks
3647
var finptrmask [_FinBlockSize / goarch.PtrSize / 8]byte
37-
var fingwait bool
38-
var fingwake bool
48+
3949
var allfin *finblock // list of all blocks
4050

4151
// NOTE: Layout known to queuefinalizer.
@@ -126,8 +136,8 @@ func queuefinalizer(p unsafe.Pointer, fn *funcval, nret uintptr, fint *_type, ot
126136
f.fint = fint
127137
f.ot = ot
128138
f.arg = p
129-
fingwake = true
130139
unlock(&finlock)
140+
fingStatus.Or(fingWake)
131141
}
132142

133143
//go:nowritebarrier
@@ -141,29 +151,27 @@ func iterate_finq(callback func(*funcval, unsafe.Pointer, uintptr, *_type, *ptrt
141151
}
142152

143153
func wakefing() *g {
144-
var res *g
145-
lock(&finlock)
146-
if fingwait && fingwake {
147-
fingwait = false
148-
fingwake = false
149-
res = fing
154+
if ok := fingStatus.CompareAndSwap(fingCreated|fingWait|fingWake, fingCreated); ok {
155+
return fing
150156
}
151-
unlock(&finlock)
152-
return res
157+
return nil
153158
}
154159

155-
var (
156-
fingCreate uint32
157-
fingRunning bool
158-
)
159-
160160
func createfing() {
161161
// start the finalizer goroutine exactly once
162-
if fingCreate == 0 && atomic.Cas(&fingCreate, 0, 1) {
162+
if fingStatus.Load() == fingUninitialized && fingStatus.CompareAndSwap(fingUninitialized, fingCreated) {
163163
go runfinq()
164164
}
165165
}
166166

167+
func finalizercommit(gp *g, lock unsafe.Pointer) bool {
168+
unlock((*mutex)(lock))
169+
// fingStatus should be modified after fing is put into a waiting state
170+
// to avoid waking fing in running state, even if it is about to be parked.
171+
fingStatus.Or(fingWait)
172+
return true
173+
}
174+
167175
// This is the goroutine that runs all of the finalizers
168176
func runfinq() {
169177
var (
@@ -182,8 +190,7 @@ func runfinq() {
182190
fb := finq
183191
finq = nil
184192
if fb == nil {
185-
fingwait = true
186-
goparkunlock(&finlock, waitReasonFinalizerWait, traceEvGoBlock, 1)
193+
gopark(finalizercommit, unsafe.Pointer(&finlock), waitReasonFinalizerWait, traceEvGoBlock, 1)
187194
continue
188195
}
189196
argRegs = intArgRegs
@@ -244,9 +251,9 @@ func runfinq() {
244251
default:
245252
throw("bad kind in runfinq")
246253
}
247-
fingRunning = true
254+
fingStatus.Or(fingRunningFinalizer)
248255
reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz), uint32(framesz), &regs)
249-
fingRunning = false
256+
fingStatus.And(^fingRunningFinalizer)
250257

251258
// Drop finalizer queue heap references
252259
// before hiding them from markroot.

src/runtime/mprof.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -917,7 +917,7 @@ func goroutineProfileWithLabelsConcurrent(p []StackRecord, labels []unsafe.Point
917917
// doesn't change during the collection. So, check the finalizer goroutine
918918
// in particular.
919919
n = int(gcount())
920-
if fingRunning {
920+
if fingStatus.Load()&fingRunningFinalizer != 0 {
921921
n++
922922
}
923923

src/runtime/proc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2628,7 +2628,7 @@ top:
26282628
}
26292629

26302630
// Wake up the finalizer G.
2631-
if fingwait && fingwake {
2631+
if fingStatus.Load()&(fingWait|fingWake) == fingWait|fingWake {
26322632
if gp := wakefing(); gp != nil {
26332633
ready(gp, 0, true)
26342634
}

src/runtime/traceback.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,7 @@ func isSystemGoroutine(gp *g, fixed bool) bool {
10511051
// always consider it a user goroutine.
10521052
return false
10531053
}
1054-
return !fingRunning
1054+
return fingStatus.Load()&fingRunningFinalizer == 0
10551055
}
10561056
return hasPrefix(funcname(f), "runtime.")
10571057
}

0 commit comments

Comments
 (0)