mirror of
https://github.com/golang/go
synced 2024-09-15 22:20:06 +00:00
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 <joedian@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Daniel Martí <mvdan@mvdan.cc> Reviewed-by: Michael Pratt <mpratt@google.com>
This commit is contained in:
parent
67e6542467
commit
bd5595d7fa
|
@ -1264,10 +1264,7 @@ func SetIntArgRegs(a int) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func FinalizerGAsleep() bool {
|
func FinalizerGAsleep() bool {
|
||||||
lock(&finlock)
|
return fingStatus.Load()&fingWait != 0
|
||||||
result := fingwait
|
|
||||||
unlock(&finlock)
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For GCTestMoveStackOnNextCall, it's important not to introduce an
|
// For GCTestMoveStackOnNextCall, it's important not to introduce an
|
||||||
|
|
|
@ -29,13 +29,23 @@ type finblock struct {
|
||||||
fin [(_FinBlockSize - 2*goarch.PtrSize - 2*4) / unsafe.Sizeof(finalizer{})]finalizer
|
fin [(_FinBlockSize - 2*goarch.PtrSize - 2*4) / unsafe.Sizeof(finalizer{})]finalizer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fingStatus atomic.Uint32
|
||||||
|
|
||||||
|
// finalizer goroutine status.
|
||||||
|
const (
|
||||||
|
fingUninitialized uint32 = iota
|
||||||
|
fingCreated uint32 = 1 << (iota - 1)
|
||||||
|
fingRunningFinalizer
|
||||||
|
fingWait
|
||||||
|
fingWake
|
||||||
|
)
|
||||||
|
|
||||||
var finlock mutex // protects the following variables
|
var finlock mutex // protects the following variables
|
||||||
var fing *g // goroutine that runs finalizers
|
var fing *g // goroutine that runs finalizers
|
||||||
var finq *finblock // list of finalizers that are to be executed
|
var finq *finblock // list of finalizers that are to be executed
|
||||||
var finc *finblock // cache of free blocks
|
var finc *finblock // cache of free blocks
|
||||||
var finptrmask [_FinBlockSize / goarch.PtrSize / 8]byte
|
var finptrmask [_FinBlockSize / goarch.PtrSize / 8]byte
|
||||||
var fingwait bool
|
|
||||||
var fingwake bool
|
|
||||||
var allfin *finblock // list of all blocks
|
var allfin *finblock // list of all blocks
|
||||||
|
|
||||||
// NOTE: Layout known to queuefinalizer.
|
// NOTE: Layout known to queuefinalizer.
|
||||||
|
@ -126,8 +136,8 @@ func queuefinalizer(p unsafe.Pointer, fn *funcval, nret uintptr, fint *_type, ot
|
||||||
f.fint = fint
|
f.fint = fint
|
||||||
f.ot = ot
|
f.ot = ot
|
||||||
f.arg = p
|
f.arg = p
|
||||||
fingwake = true
|
|
||||||
unlock(&finlock)
|
unlock(&finlock)
|
||||||
|
fingStatus.Or(fingWake)
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:nowritebarrier
|
//go:nowritebarrier
|
||||||
|
@ -141,29 +151,27 @@ func iterate_finq(callback func(*funcval, unsafe.Pointer, uintptr, *_type, *ptrt
|
||||||
}
|
}
|
||||||
|
|
||||||
func wakefing() *g {
|
func wakefing() *g {
|
||||||
var res *g
|
if ok := fingStatus.CompareAndSwap(fingCreated|fingWait|fingWake, fingCreated); ok {
|
||||||
lock(&finlock)
|
return fing
|
||||||
if fingwait && fingwake {
|
|
||||||
fingwait = false
|
|
||||||
fingwake = false
|
|
||||||
res = fing
|
|
||||||
}
|
}
|
||||||
unlock(&finlock)
|
return nil
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
fingCreate uint32
|
|
||||||
fingRunning bool
|
|
||||||
)
|
|
||||||
|
|
||||||
func createfing() {
|
func createfing() {
|
||||||
// start the finalizer goroutine exactly once
|
// start the finalizer goroutine exactly once
|
||||||
if fingCreate == 0 && atomic.Cas(&fingCreate, 0, 1) {
|
if fingStatus.Load() == fingUninitialized && fingStatus.CompareAndSwap(fingUninitialized, fingCreated) {
|
||||||
go runfinq()
|
go runfinq()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func finalizercommit(gp *g, lock unsafe.Pointer) bool {
|
||||||
|
unlock((*mutex)(lock))
|
||||||
|
// fingStatus should be modified after fing is put into a waiting state
|
||||||
|
// to avoid waking fing in running state, even if it is about to be parked.
|
||||||
|
fingStatus.Or(fingWait)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// This is the goroutine that runs all of the finalizers
|
// This is the goroutine that runs all of the finalizers
|
||||||
func runfinq() {
|
func runfinq() {
|
||||||
var (
|
var (
|
||||||
|
@ -182,8 +190,7 @@ func runfinq() {
|
||||||
fb := finq
|
fb := finq
|
||||||
finq = nil
|
finq = nil
|
||||||
if fb == nil {
|
if fb == nil {
|
||||||
fingwait = true
|
gopark(finalizercommit, unsafe.Pointer(&finlock), waitReasonFinalizerWait, traceEvGoBlock, 1)
|
||||||
goparkunlock(&finlock, waitReasonFinalizerWait, traceEvGoBlock, 1)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
argRegs = intArgRegs
|
argRegs = intArgRegs
|
||||||
|
@ -244,9 +251,9 @@ func runfinq() {
|
||||||
default:
|
default:
|
||||||
throw("bad kind in runfinq")
|
throw("bad kind in runfinq")
|
||||||
}
|
}
|
||||||
fingRunning = true
|
fingStatus.Or(fingRunningFinalizer)
|
||||||
reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz), uint32(framesz), ®s)
|
reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz), uint32(framesz), ®s)
|
||||||
fingRunning = false
|
fingStatus.And(^fingRunningFinalizer)
|
||||||
|
|
||||||
// Drop finalizer queue heap references
|
// Drop finalizer queue heap references
|
||||||
// before hiding them from markroot.
|
// before hiding them from markroot.
|
||||||
|
|
|
@ -917,7 +917,7 @@ func goroutineProfileWithLabelsConcurrent(p []StackRecord, labels []unsafe.Point
|
||||||
// doesn't change during the collection. So, check the finalizer goroutine
|
// doesn't change during the collection. So, check the finalizer goroutine
|
||||||
// in particular.
|
// in particular.
|
||||||
n = int(gcount())
|
n = int(gcount())
|
||||||
if fingRunning {
|
if fingStatus.Load()&fingRunningFinalizer != 0 {
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2628,7 +2628,7 @@ top:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake up the finalizer G.
|
// Wake up the finalizer G.
|
||||||
if fingwait && fingwake {
|
if fingStatus.Load()&(fingWait|fingWake) == fingWait|fingWake {
|
||||||
if gp := wakefing(); gp != nil {
|
if gp := wakefing(); gp != nil {
|
||||||
ready(gp, 0, true)
|
ready(gp, 0, true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1051,7 +1051,7 @@ func isSystemGoroutine(gp *g, fixed bool) bool {
|
||||||
// always consider it a user goroutine.
|
// always consider it a user goroutine.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return !fingRunning
|
return fingStatus.Load()&fingRunningFinalizer == 0
|
||||||
}
|
}
|
||||||
return hasPrefix(funcname(f), "runtime.")
|
return hasPrefix(funcname(f), "runtime.")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue