diff options
Diffstat (limited to 'src/runtime/mfinal.go')
| -rw-r--r-- | src/runtime/mfinal.go | 49 |
1 files changed, 28 insertions, 21 deletions
diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go index 9de364c260..ef11b7df96 100644 --- a/src/runtime/mfinal.go +++ b/src/runtime/mfinal.go @@ -29,13 +29,23 @@ type finblock struct { 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 fing *g // goroutine that runs finalizers var finq *finblock // list of finalizers that are to be executed var finc *finblock // cache of free blocks var finptrmask [_FinBlockSize / goarch.PtrSize / 8]byte -var fingwait bool -var fingwake bool + var allfin *finblock // list of all blocks // 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.ot = ot f.arg = p - fingwake = true unlock(&finlock) + fingStatus.Or(fingWake) } //go:nowritebarrier @@ -141,29 +151,27 @@ func iterate_finq(callback func(*funcval, unsafe.Pointer, uintptr, *_type, *ptrt } func wakefing() *g { - var res *g - lock(&finlock) - if fingwait && fingwake { - fingwait = false - fingwake = false - res = fing + if ok := fingStatus.CompareAndSwap(fingCreated|fingWait|fingWake, fingCreated); ok { + return fing } - unlock(&finlock) - return res + return nil } -var ( - fingCreate uint32 - fingRunning bool -) - func createfing() { // start the finalizer goroutine exactly once - if fingCreate == 0 && atomic.Cas(&fingCreate, 0, 1) { + if fingStatus.Load() == fingUninitialized && fingStatus.CompareAndSwap(fingUninitialized, fingCreated) { 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 func runfinq() { var ( @@ -182,8 +190,7 @@ func runfinq() { fb := finq finq = nil if fb == nil { - fingwait = true - goparkunlock(&finlock, waitReasonFinalizerWait, traceEvGoBlock, 1) + gopark(finalizercommit, unsafe.Pointer(&finlock), waitReasonFinalizerWait, traceEvGoBlock, 1) continue } argRegs = intArgRegs @@ -244,9 +251,9 @@ func runfinq() { default: throw("bad kind in runfinq") } - fingRunning = true + fingStatus.Or(fingRunningFinalizer) reflectcall(nil, unsafe.Pointer(f.fn), frame, uint32(framesz), uint32(framesz), uint32(framesz), ®s) - fingRunning = false + fingStatus.And(^fingRunningFinalizer) // Drop finalizer queue heap references // before hiding them from markroot. |
