default:
throw("gcBgMarkWorker: unexpected gcMarkWorkerMode")
case gcMarkWorkerDedicatedMode:
- gcDrain(&p.gcw, gcBgCreditSlack)
+ gcDrain(&p.gcw, gcBgCreditSlack, gcDrainBlock)
// gcDrain did the xadd(&work.nwait +1) to
// match the decrement above. It only returns
// at a mark completion point.
throw("gcDrain returned with buffer")
}
case gcMarkWorkerFractionalMode, gcMarkWorkerIdleMode:
- gcDrainUntilPreempt(&p.gcw, gcBgCreditSlack)
+ gcDrain(&p.gcw, gcBgCreditSlack, gcDrainUntilPreempt)
// If we are nearing the end of mark, dispose
// of the cache promptly. We must do this
parfordo(work.markfor)
var gcw gcWork
- gcDrain(&gcw, -1)
+ gcDrain(&gcw, -1, gcDrainBlock)
gcw.dispose()
if work.full != 0 {
parfordo(work.markfor)
if gcphase != _GCscan {
var gcw gcWork
- gcDrain(&gcw, -1) // blocks in getfull
+ gcDrain(&gcw, -1, gcDrainBlock) // blocks in getfull
gcw.dispose()
}
}
}
-// TODO(austin): Can we consolidate the gcDrain* functions?
+type gcDrainFlags int
-// gcDrain scans objects in work buffers, blackening grey
-// objects until all work buffers have been drained.
+const (
+ gcDrainUntilPreempt gcDrainFlags = 1 << iota
+
+ // gcDrainBlock is the opposite of gcDrainUntilPreempt. This
+ // is the default, but callers should use the constant for
+ // documentation purposes.
+ gcDrainBlock gcDrainFlags = 0
+)
+
+// gcDrain scans objects in work buffers, blackening grey objects
+// until all work buffers have been drained.
+//
+// If flags&gcDrainUntilPreempt != 0, gcDrain also returns if
+// g.preempt is set. Otherwise, this will block until all dedicated
+// workers are blocked in gcDrain.
+//
// If flushScanCredit != -1, gcDrain flushes accumulated scan work
// credit to gcController.bgScanCredit whenever gcw's local scan work
// credit exceeds flushScanCredit.
//go:nowritebarrier
-func gcDrain(gcw *gcWork, flushScanCredit int64) {
+func gcDrain(gcw *gcWork, flushScanCredit int64, flags gcDrainFlags) {
if !writeBarrierEnabled {
throw("gcDrain phase incorrect")
}
+ blocking := flags&gcDrainUntilPreempt == 0
+
var lastScanFlush, nextScanFlush int64
if flushScanCredit != -1 {
lastScanFlush = gcw.scanWork
nextScanFlush = int64(^uint64(0) >> 1)
}
- for {
+ gp := getg()
+ for blocking || !gp.preempt {
// If another proc wants a pointer, give it some.
if work.nwait > 0 && work.full == 0 {
gcw.balance()
}
- b := gcw.get()
+ var b uintptr
+ if blocking {
+ b = gcw.get()
+ } else {
+ b = gcw.tryGet()
+ }
if b == 0 {
- // work barrier reached
+ // work barrier reached or tryGet failed.
break
}
// If the current wbuf is filled by the scan a new wbuf might be
}
}
-// gcDrainUntilPreempt blackens grey objects until g.preempt is set.
-// This is best-effort, so it will return as soon as it is unable to
-// get work, even though there may be more work in the system.
-//go:nowritebarrier
-func gcDrainUntilPreempt(gcw *gcWork, flushScanCredit int64) {
- if !writeBarrierEnabled {
- println("gcphase =", gcphase)
- throw("gcDrainUntilPreempt phase incorrect")
- }
-
- var lastScanFlush, nextScanFlush int64
- if flushScanCredit != -1 {
- lastScanFlush = gcw.scanWork
- nextScanFlush = lastScanFlush + flushScanCredit
- } else {
- nextScanFlush = int64(^uint64(0) >> 1)
- }
-
- gp := getg()
- for !gp.preempt {
- // If the work queue is empty, balance. During
- // concurrent mark we don't really know if anyone else
- // can make use of this work, but even if we're the
- // only worker, the total cost of this per cycle is
- // only O(_WorkbufSize) pointer copies.
- if work.full == 0 && work.partial == 0 {
- gcw.balance()
- }
-
- b := gcw.tryGet()
- if b == 0 {
- // No more work
- break
- }
- scanobject(b, gcw)
-
- // Flush background scan work credit to the global
- // account if we've accumulated enough locally so
- // mutator assists can draw on it.
- if gcw.scanWork >= nextScanFlush {
- credit := gcw.scanWork - lastScanFlush
- xaddint64(&gcController.bgScanCredit, credit)
- lastScanFlush = gcw.scanWork
- nextScanFlush = lastScanFlush + flushScanCredit
- }
- }
- if flushScanCredit != -1 {
- credit := gcw.scanWork - lastScanFlush
- xaddint64(&gcController.bgScanCredit, credit)
- }
-}
-
// gcDrainN blackens grey objects until it has performed roughly
// scanWork units of scan work. This is best-effort, so it may perform
// less work if it fails to get a work buffer. Otherwise, it will