)
const (
- _Debugwbufs = false // if true check wbufs consistency
- _WorkbufSize = 2048 // in bytes; larger values result in less contention
+ _WorkbufSize = 2048 // in bytes; larger values result in less contention
)
// Garbage collector work pool abstraction.
}
func (w *gcWork) init() {
- w.wbuf1 = wbufptrOf(getempty(101))
- wbuf2 := trygetfull(102)
+ w.wbuf1 = wbufptrOf(getempty())
+ wbuf2 := trygetfull()
if wbuf2 == nil {
- wbuf2 = getempty(103)
+ wbuf2 = getempty()
}
w.wbuf2 = wbufptrOf(wbuf2)
}
w.wbuf1, w.wbuf2 = w.wbuf2, w.wbuf1
wbuf = w.wbuf1.ptr()
if wbuf.nobj == len(wbuf.obj) {
- putfull(wbuf, 132)
- wbuf = getempty(133)
+ putfull(wbuf)
+ wbuf = getempty()
w.wbuf1 = wbufptrOf(wbuf)
}
}
wbuf = w.wbuf1.ptr()
if wbuf.nobj == 0 {
owbuf := wbuf
- wbuf = trygetfull(167)
+ wbuf = trygetfull()
if wbuf == nil {
return 0
}
- putempty(owbuf, 166)
+ putempty(owbuf)
w.wbuf1 = wbufptrOf(wbuf)
}
}
wbuf = w.wbuf1.ptr()
if wbuf.nobj == 0 {
owbuf := wbuf
- wbuf = getfull(185)
+ wbuf = getfull()
if wbuf == nil {
return 0
}
- putempty(owbuf, 184)
+ putempty(owbuf)
w.wbuf1 = wbufptrOf(wbuf)
}
}
func (w *gcWork) dispose() {
if wbuf := w.wbuf1.ptr(); wbuf != nil {
if wbuf.nobj == 0 {
- putempty(wbuf, 212)
+ putempty(wbuf)
} else {
- putfull(wbuf, 214)
+ putfull(wbuf)
}
w.wbuf1 = 0
wbuf = w.wbuf2.ptr()
if wbuf.nobj == 0 {
- putempty(wbuf, 218)
+ putempty(wbuf)
} else {
- putfull(wbuf, 220)
+ putfull(wbuf)
}
w.wbuf2 = 0
}
return
}
if wbuf := w.wbuf2.ptr(); wbuf.nobj != 0 {
- putfull(wbuf, 246)
- w.wbuf2 = wbufptrOf(getempty(247))
+ putfull(wbuf)
+ w.wbuf2 = wbufptrOf(getempty())
} else if wbuf := w.wbuf1.ptr(); wbuf.nobj > 4 {
w.wbuf1 = wbufptrOf(handoff(wbuf))
}
// avoid contending on the global work buffer lists.
type workbufhdr struct {
- node lfnode // must be first
- nobj int
- inuse bool // This workbuf is in use by some goroutine and is not on the work.empty/full queues.
- log [4]int // line numbers forming a history of ownership changes to workbuf
+ node lfnode // must be first
+ nobj int
}
type workbuf struct {
// workbufs.
// If the GC asks for some work these are the only routines that
// make wbufs available to the GC.
-// Each of the gets and puts also take an distinct integer that is used
-// to record a brief history of changes to ownership of the workbuf.
-// The convention is to use a unique line number but any encoding
-// is permissible. For example if you want to pass in 2 bits of information
-// you could simple add lineno1*100000+lineno2.
-
-// logget records the past few values of entry to aid in debugging.
-// logget checks the buffer b is not currently in use.
-func (b *workbuf) logget(entry int) {
- if !_Debugwbufs {
- return
- }
- if b.inuse {
- println("runtime: logget fails log entry=", entry,
- "b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
- "b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
- throw("logget: get not legal")
- }
- b.inuse = true
- copy(b.log[1:], b.log[:])
- b.log[0] = entry
-}
-
-// logput records the past few values of entry to aid in debugging.
-// logput checks the buffer b is currently in use.
-func (b *workbuf) logput(entry int) {
- if !_Debugwbufs {
- return
- }
- if !b.inuse {
- println("runtime: logput fails log entry=", entry,
- "b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
- "b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
- throw("logput: put not legal")
- }
- b.inuse = false
- copy(b.log[1:], b.log[:])
- b.log[0] = entry
-}
func (b *workbuf) checknonempty() {
if b.nobj == 0 {
- println("runtime: nonempty check fails",
- "b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
- "b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
throw("workbuf is empty")
}
}
func (b *workbuf) checkempty() {
if b.nobj != 0 {
- println("runtime: empty check fails",
- "b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
- "b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
throw("workbuf is not empty")
}
}
// getempty pops an empty work buffer off the work.empty list,
// allocating new buffers if none are available.
-// entry is used to record a brief history of ownership.
//go:nowritebarrier
-func getempty(entry int) *workbuf {
+func getempty() *workbuf {
var b *workbuf
if work.empty != 0 {
b = (*workbuf)(lfstackpop(&work.empty))
if b == nil {
b = (*workbuf)(persistentalloc(unsafe.Sizeof(*b), sys.CacheLineSize, &memstats.gc_sys))
}
- b.logget(entry)
return b
}
// putempty puts a workbuf onto the work.empty list.
// Upon entry this go routine owns b. The lfstackpush relinquishes ownership.
//go:nowritebarrier
-func putempty(b *workbuf, entry int) {
+func putempty(b *workbuf) {
b.checkempty()
- b.logput(entry)
lfstackpush(&work.empty, &b.node)
}
// putfull accepts partially full buffers so the GC can avoid competing
// with the mutators for ownership of partially full buffers.
//go:nowritebarrier
-func putfull(b *workbuf, entry int) {
+func putfull(b *workbuf) {
b.checknonempty()
- b.logput(entry)
lfstackpush(&work.full, &b.node)
// We just made more work available. Let the GC controller
// trygetfull tries to get a full or partially empty workbuffer.
// If one is not immediately available return nil
//go:nowritebarrier
-func trygetfull(entry int) *workbuf {
+func trygetfull() *workbuf {
b := (*workbuf)(lfstackpop(&work.full))
if b != nil {
- b.logget(entry)
b.checknonempty()
return b
}
// This is in fact the termination condition for the STW mark
// phase.
//go:nowritebarrier
-func getfull(entry int) *workbuf {
+func getfull() *workbuf {
b := (*workbuf)(lfstackpop(&work.full))
if b != nil {
- b.logget(entry)
b.checknonempty()
return b
}
}
b = (*workbuf)(lfstackpop(&work.full))
if b != nil {
- b.logget(entry)
b.checknonempty()
return b
}
//go:nowritebarrier
func handoff(b *workbuf) *workbuf {
// Make new buffer with half of b's pointers.
- b1 := getempty(915)
+ b1 := getempty()
n := b.nobj / 2
b.nobj -= n
b1.nobj = n
_g_.m.gcstats.nhandoffcnt += uint64(n)
// Put b on full list - let first half of b get stolen.
- putfull(b, 942)
+ putfull(b)
return b1
}