mirror of
https://github.com/golang/go
synced 2024-11-19 15:14:45 -07:00
runtime: mark gcWork methods nowritebarrierrec
Currently most of these are marked go:nowritebarrier as a hint, but it's actually important that these not invoke write barriers recursively. The danger is that some gcWork method would invoke the write barrier while the gcWork is in an inconsistent state and that the write barrier would in turn invoke some other gcWork method, which would crash or permanently corrupt the gcWork. Simply marking the write barrier itself as go:nowritebarrierrec isn't sufficient to prevent this if the write barrier doesn't use the outer method. Thankfully, this doesn't cause any build failures, so we were getting this right. :) For #22460. Change-Id: I35a7292a584200eb35a49507cd3fe359ba2206f6 Reviewed-on: https://go-review.googlesource.com/72554 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Rick Hudson <rlh@golang.org>
This commit is contained in:
parent
3beaf26e4f
commit
249b5cc945
@ -85,6 +85,13 @@ type gcWork struct {
|
|||||||
scanWork int64
|
scanWork int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Most of the methods of gcWork are go:nowritebarrierrec because the
|
||||||
|
// write barrier itself can invoke gcWork methods but the methods are
|
||||||
|
// not generally re-entrant. Hence, if a gcWork method invoked the
|
||||||
|
// write barrier while the gcWork was in an inconsistent state, and
|
||||||
|
// the write barrier in turn invoked a gcWork method, it could
|
||||||
|
// permanently corrupt the gcWork.
|
||||||
|
|
||||||
func (w *gcWork) init() {
|
func (w *gcWork) init() {
|
||||||
w.wbuf1 = getempty()
|
w.wbuf1 = getempty()
|
||||||
wbuf2 := trygetfull()
|
wbuf2 := trygetfull()
|
||||||
@ -96,7 +103,7 @@ func (w *gcWork) init() {
|
|||||||
|
|
||||||
// put enqueues a pointer for the garbage collector to trace.
|
// put enqueues a pointer for the garbage collector to trace.
|
||||||
// obj must point to the beginning of a heap object or an oblet.
|
// obj must point to the beginning of a heap object or an oblet.
|
||||||
//go:nowritebarrier
|
//go:nowritebarrierrec
|
||||||
func (w *gcWork) put(obj uintptr) {
|
func (w *gcWork) put(obj uintptr) {
|
||||||
flushed := false
|
flushed := false
|
||||||
wbuf := w.wbuf1
|
wbuf := w.wbuf1
|
||||||
@ -129,7 +136,7 @@ func (w *gcWork) put(obj uintptr) {
|
|||||||
|
|
||||||
// putFast does a put and returns true if it can be done quickly
|
// putFast does a put and returns true if it can be done quickly
|
||||||
// otherwise it returns false and the caller needs to call put.
|
// otherwise it returns false and the caller needs to call put.
|
||||||
//go:nowritebarrier
|
//go:nowritebarrierrec
|
||||||
func (w *gcWork) putFast(obj uintptr) bool {
|
func (w *gcWork) putFast(obj uintptr) bool {
|
||||||
wbuf := w.wbuf1
|
wbuf := w.wbuf1
|
||||||
if wbuf == nil {
|
if wbuf == nil {
|
||||||
@ -148,7 +155,7 @@ func (w *gcWork) putFast(obj uintptr) bool {
|
|||||||
// 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
|
||||||
// queue, tryGet returns 0. Note that there may still be pointers in
|
// queue, tryGet returns 0. Note that there may still be pointers in
|
||||||
// other gcWork instances or other caches.
|
// other gcWork instances or other caches.
|
||||||
//go:nowritebarrier
|
//go:nowritebarrierrec
|
||||||
func (w *gcWork) tryGet() uintptr {
|
func (w *gcWork) tryGet() uintptr {
|
||||||
wbuf := w.wbuf1
|
wbuf := w.wbuf1
|
||||||
if wbuf == nil {
|
if wbuf == nil {
|
||||||
@ -177,7 +184,7 @@ func (w *gcWork) tryGet() uintptr {
|
|||||||
// tryGetFast dequeues a pointer for the garbage collector to trace
|
// tryGetFast dequeues a pointer for the garbage collector to trace
|
||||||
// if one is readily available. Otherwise it returns 0 and
|
// if one is readily available. Otherwise it returns 0 and
|
||||||
// the caller is expected to call tryGet().
|
// the caller is expected to call tryGet().
|
||||||
//go:nowritebarrier
|
//go:nowritebarrierrec
|
||||||
func (w *gcWork) tryGetFast() uintptr {
|
func (w *gcWork) tryGetFast() uintptr {
|
||||||
wbuf := w.wbuf1
|
wbuf := w.wbuf1
|
||||||
if wbuf == nil {
|
if wbuf == nil {
|
||||||
@ -194,7 +201,7 @@ func (w *gcWork) tryGetFast() uintptr {
|
|||||||
// get dequeues a pointer for the garbage collector to trace, blocking
|
// get dequeues a pointer for the garbage collector to trace, blocking
|
||||||
// if necessary to ensure all pointers from all queues and caches have
|
// if necessary to ensure all pointers from all queues and caches have
|
||||||
// been retrieved. get returns 0 if there are no pointers remaining.
|
// been retrieved. get returns 0 if there are no pointers remaining.
|
||||||
//go:nowritebarrier
|
//go:nowritebarrierrec
|
||||||
func (w *gcWork) get() uintptr {
|
func (w *gcWork) get() uintptr {
|
||||||
wbuf := w.wbuf1
|
wbuf := w.wbuf1
|
||||||
if wbuf == nil {
|
if wbuf == nil {
|
||||||
@ -228,7 +235,7 @@ func (w *gcWork) get() uintptr {
|
|||||||
// GC can inspect them. This helps reduce the mutator's
|
// GC can inspect them. This helps reduce the mutator's
|
||||||
// ability to hide pointers during the concurrent mark phase.
|
// ability to hide pointers during the concurrent mark phase.
|
||||||
//
|
//
|
||||||
//go:nowritebarrier
|
//go:nowritebarrierrec
|
||||||
func (w *gcWork) dispose() {
|
func (w *gcWork) dispose() {
|
||||||
if wbuf := w.wbuf1; wbuf != nil {
|
if wbuf := w.wbuf1; wbuf != nil {
|
||||||
if wbuf.nobj == 0 {
|
if wbuf.nobj == 0 {
|
||||||
@ -262,7 +269,7 @@ func (w *gcWork) dispose() {
|
|||||||
|
|
||||||
// balance moves some work that's cached in this gcWork back on the
|
// balance moves some work that's cached in this gcWork back on the
|
||||||
// global queue.
|
// global queue.
|
||||||
//go:nowritebarrier
|
//go:nowritebarrierrec
|
||||||
func (w *gcWork) balance() {
|
func (w *gcWork) balance() {
|
||||||
if w.wbuf1 == nil {
|
if w.wbuf1 == nil {
|
||||||
return
|
return
|
||||||
@ -282,7 +289,7 @@ func (w *gcWork) balance() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// empty returns true if w has no mark work available.
|
// empty returns true if w has no mark work available.
|
||||||
//go:nowritebarrier
|
//go:nowritebarrierrec
|
||||||
func (w *gcWork) empty() bool {
|
func (w *gcWork) empty() bool {
|
||||||
return w.wbuf1 == nil || (w.wbuf1.nobj == 0 && w.wbuf2.nobj == 0)
|
return w.wbuf1 == nil || (w.wbuf1.nobj == 0 && w.wbuf2.nobj == 0)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user