Commit cadd4f81 authored by Austin Clements's avatar Austin Clements

runtime: combine gcWorkProducer into gcWork

The distinction between gcWorkProducer and gcWork (producer and
consumer) is not serving us as originally intended, so merge these
into just gcWork.

The original intent was to replace the currentwbuf cache with a
gcWorkProducer. However, with gchelpwork (aka mutator assists),
mutators can both produce and consume work, so it will make more sense
to cache a whole gcWork.

Change-Id: I6e633e96db7cb23a64fbadbfc4607e3ad32bcfb3
Reviewed-on: https://go-review.googlesource.com/7733Reviewed-by: default avatarRick Hudson <rlh@golang.org>
parent 77fcf36a
...@@ -55,7 +55,7 @@ var oneptr = [...]uint8{typePointer} ...@@ -55,7 +55,7 @@ var oneptr = [...]uint8{typePointer}
//go:nowritebarrier //go:nowritebarrier
func markroot(desc *parfor, i uint32) { func markroot(desc *parfor, i uint32) {
var gcw gcWorkProducer var gcw gcWork
// Note: if you add a case here, please also update heapdump.go:dumproots. // Note: if you add a case here, please also update heapdump.go:dumproots.
switch i { switch i {
...@@ -246,7 +246,7 @@ func scanstack(gp *g) { ...@@ -246,7 +246,7 @@ func scanstack(gp *g) {
throw("can't scan gchelper stack") throw("can't scan gchelper stack")
} }
var gcw gcWorkProducer var gcw gcWork
gcw.initFromCache() gcw.initFromCache()
scanframe := func(frame *stkframe, unused unsafe.Pointer) bool { scanframe := func(frame *stkframe, unused unsafe.Pointer) bool {
// Pick up gcw as free variable so gentraceback and friends can // Pick up gcw as free variable so gentraceback and friends can
...@@ -262,7 +262,7 @@ func scanstack(gp *g) { ...@@ -262,7 +262,7 @@ func scanstack(gp *g) {
// Scan a stack frame: local variables and function arguments/results. // Scan a stack frame: local variables and function arguments/results.
//go:nowritebarrier //go:nowritebarrier
func scanframeworker(frame *stkframe, unused unsafe.Pointer, gcw *gcWorkProducer) { func scanframeworker(frame *stkframe, unused unsafe.Pointer, gcw *gcWork) {
f := frame.fn f := frame.fn
targetpc := frame.continpc targetpc := frame.continpc
...@@ -360,7 +360,7 @@ func gcDrain(gcw *gcWork) { ...@@ -360,7 +360,7 @@ func gcDrain(gcw *gcWork) {
// out of the wbuf passed in + a single object placed // out of the wbuf passed in + a single object placed
// into an empty wbuf in scanobject so there could be // into an empty wbuf in scanobject so there could be
// a performance hit as we keep fetching fresh wbufs. // a performance hit as we keep fetching fresh wbufs.
scanobject(b, 0, nil, &gcw.gcWorkProducer) scanobject(b, 0, nil, gcw)
} }
checknocurrentwbuf() checknocurrentwbuf()
} }
...@@ -378,14 +378,14 @@ func gcDrainN(gcw *gcWork, n int) { ...@@ -378,14 +378,14 @@ func gcDrainN(gcw *gcWork, n int) {
if b == 0 { if b == 0 {
return return
} }
scanobject(b, 0, nil, &gcw.gcWorkProducer) scanobject(b, 0, nil, gcw)
} }
} }
// scanblock scans b as scanobject would. // scanblock scans b as scanobject would.
// If the gcphase is GCscan, scanblock performs additional checks. // If the gcphase is GCscan, scanblock performs additional checks.
//go:nowritebarrier //go:nowritebarrier
func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWorkProducer) { func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork) {
// Use local copies of original parameters, so that a stack trace // Use local copies of original parameters, so that a stack trace
// due to one of the throws below shows the original block // due to one of the throws below shows the original block
// base and extent. // base and extent.
...@@ -411,7 +411,7 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWorkProducer) { ...@@ -411,7 +411,7 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWorkProducer) {
// In this case, n may be an overestimate of the size; the GC bitmap // In this case, n may be an overestimate of the size; the GC bitmap
// must also be used to make sure the scan stops at the end of b. // must also be used to make sure the scan stops at the end of b.
//go:nowritebarrier //go:nowritebarrier
func scanobject(b, n uintptr, ptrmask *uint8, gcw *gcWorkProducer) { func scanobject(b, n uintptr, ptrmask *uint8, gcw *gcWork) {
arena_start := mheap_.arena_start arena_start := mheap_.arena_start
arena_used := mheap_.arena_used arena_used := mheap_.arena_used
...@@ -489,7 +489,7 @@ func shade(b uintptr) { ...@@ -489,7 +489,7 @@ func shade(b uintptr) {
// throw("shade during harvest") // throw("shade during harvest")
// } // }
var gcw gcWorkProducer var gcw gcWork
greyobject(obj, 0, 0, hbits, &gcw) greyobject(obj, 0, 0, hbits, &gcw)
// This is part of the write barrier so put the wbuf back. // This is part of the write barrier so put the wbuf back.
if gcphase == _GCmarktermination { if gcphase == _GCmarktermination {
...@@ -512,7 +512,7 @@ func shade(b uintptr) { ...@@ -512,7 +512,7 @@ func shade(b uintptr) {
// Return possibly new workbuf to use. // Return possibly new workbuf to use.
// base and off are for debugging only and could be removed. // base and off are for debugging only and could be removed.
//go:nowritebarrier //go:nowritebarrier
func greyobject(obj, base, off uintptr, hbits heapBits, gcw *gcWorkProducer) { func greyobject(obj, base, off uintptr, hbits heapBits, gcw *gcWork) {
// obj should be start of allocation, and so must be at least pointer-aligned. // obj should be start of allocation, and so must be at least pointer-aligned.
if obj&(ptrSize-1) != 0 { if obj&(ptrSize-1) != 0 {
throw("greyobject: obj not pointer-aligned") throw("greyobject: obj not pointer-aligned")
......
...@@ -35,36 +35,25 @@ func (wp wbufptr) ptr() *workbuf { ...@@ -35,36 +35,25 @@ func (wp wbufptr) ptr() *workbuf {
return (*workbuf)(unsafe.Pointer(wp)) return (*workbuf)(unsafe.Pointer(wp))
} }
// A gcWorkProducer provides the interface to produce work for the // A gcWork provides the interface to produce and consume work for the
// garbage collector. // garbage collector.
// //
// The usual pattern for using gcWorkProducer is: // The usual pattern for using gcWork is:
// //
// var gcw gcWorkProducer // var gcw gcWork
// .. call gcw.put() .. // .. call gcw.put() to produce and gcw.get() to consume ..
// gcw.dispose() // gcw.dispose()
type gcWorkProducer struct { type gcWork struct {
// Invariant: wbuf is never full or empty // Invariant: wbuf is never full or empty
wbuf wbufptr wbuf wbufptr
} }
// A gcWork provides the interface to both produce and consume work
// for the garbage collector.
//
// The pattern for using gcWork is the same as gcWorkProducer.
type gcWork struct {
gcWorkProducer
}
// Note that there is no need for a gcWorkConsumer because everything
// that consumes pointers also produces them.
// initFromCache fetches work from this M's currentwbuf cache. // initFromCache fetches work from this M's currentwbuf cache.
//go:nowritebarrier //go:nowritebarrier
func (w *gcWorkProducer) initFromCache() { func (w *gcWork) initFromCache() {
// TODO: Instead of making gcWorkProducer pull from the // TODO: Instead of making gcWork pull from the currentwbuf
// currentwbuf cache, use a gcWorkProducer as the cache and // cache, use a gcWork as the cache and make shade pass around
// make shade pass around that gcWorkProducer. // that gcWork.
if w.wbuf == 0 { if w.wbuf == 0 {
w.wbuf = wbufptr(xchguintptr(&getg().m.currentwbuf, 0)) w.wbuf = wbufptr(xchguintptr(&getg().m.currentwbuf, 0))
} }
...@@ -72,8 +61,8 @@ func (w *gcWorkProducer) initFromCache() { ...@@ -72,8 +61,8 @@ func (w *gcWorkProducer) initFromCache() {
// put enqueues a pointer for the garbage collector to trace. // put enqueues a pointer for the garbage collector to trace.
//go:nowritebarrier //go:nowritebarrier
func (ww *gcWorkProducer) put(obj uintptr) { func (ww *gcWork) put(obj uintptr) {
w := (*gcWorkProducer)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed w := (*gcWork)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed
wbuf := w.wbuf.ptr() wbuf := w.wbuf.ptr()
if wbuf == nil { if wbuf == nil {
...@@ -90,28 +79,6 @@ func (ww *gcWorkProducer) put(obj uintptr) { ...@@ -90,28 +79,6 @@ func (ww *gcWorkProducer) put(obj uintptr) {
} }
} }
// dispose returns any cached pointers to the global queue.
//go:nowritebarrier
func (w *gcWorkProducer) dispose() {
if wbuf := w.wbuf; wbuf != 0 {
putpartial(wbuf.ptr(), 58)
w.wbuf = 0
}
}
// disposeToCache returns any cached pointers to this M's currentwbuf.
// It calls throw if currentwbuf is non-nil.
//go:nowritebarrier
func (w *gcWorkProducer) disposeToCache() {
if wbuf := w.wbuf; wbuf != 0 {
wbuf = wbufptr(xchguintptr(&getg().m.currentwbuf, uintptr(wbuf)))
if wbuf != 0 {
throw("m.currentwbuf non-nil in disposeToCache")
}
w.wbuf = 0
}
}
// tryGet dequeues a pointer for the garbage collector to trace. // tryGet dequeues a pointer for the garbage collector to trace.
// //
// If there are no pointers remaining in this gcWork or in the global // If there are no pointers remaining in this gcWork or in the global
...@@ -175,11 +142,20 @@ func (ww *gcWork) get() uintptr { ...@@ -175,11 +142,20 @@ func (ww *gcWork) get() uintptr {
//go:nowritebarrier //go:nowritebarrier
func (w *gcWork) dispose() { func (w *gcWork) dispose() {
if wbuf := w.wbuf; wbuf != 0 { if wbuf := w.wbuf; wbuf != 0 {
// Even though wbuf may only be partially full, we putpartial(wbuf.ptr(), 167)
// want to keep it on the consumer's queues rather w.wbuf = 0
// than putting it back on the producer's queues. }
// Hence, we use putfull here. }
putfull(wbuf.ptr(), 133)
// disposeToCache returns any cached pointers to this M's currentwbuf.
// It calls throw if currentwbuf is non-nil.
//go:nowritebarrier
func (w *gcWork) disposeToCache() {
if wbuf := w.wbuf; wbuf != 0 {
wbuf = wbufptr(xchguintptr(&getg().m.currentwbuf, uintptr(wbuf)))
if wbuf != 0 {
throw("m.currentwbuf non-nil in disposeToCache")
}
w.wbuf = 0 w.wbuf = 0
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment