]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/runtime/mgc.go
runtime: refactor runtime->tracer API to appear more like a lock
[gostls13.git] / src / runtime / mgc.go
index d3658df489bc156dec5b636ece5fb98d6b68a766..30d2f1d3852b4430c6ac2039e5a0f3d1dcf43ba3 100644 (file)
@@ -135,9 +135,12 @@ import (
 )
 
 const (
-       _DebugGC         = 0
-       _ConcurrentSweep = true
-       _FinBlockSize    = 4 * 1024
+       _DebugGC      = 0
+       _FinBlockSize = 4 * 1024
+
+       // concurrentSweep is a debug flag. Disabling this flag
+       // ensures all spans are swept while the world is stopped.
+       concurrentSweep = true
 
        // debugScanConservative enables debug logging for stack
        // frames that are scanned conservatively.
@@ -149,7 +152,7 @@ const (
        sweepMinHeapDistance = 1024 * 1024
 )
 
-// heapObjectsCanMove is always false in the current garbage collector.
+// heapObjectsCanMove always returns false in the current garbage collector.
 // It exists for go4.org/unsafe/assume-no-moving-gc, which is an
 // unfortunate idea that had an even more unfortunate implementation.
 // Every time a new Go release happened, the package stopped building,
@@ -165,7 +168,11 @@ const (
 //
 // If the Go garbage collector ever does move heap objects, we can set
 // this to true to break all the programs using assume-no-moving-gc.
-var heapObjectsCanMove = false
+//
+//go:linkname heapObjectsCanMove
+func heapObjectsCanMove() bool {
+       return false
+}
 
 func gcinit() {
        if unsafe.Sizeof(workbuf{}) != _WorkbufSize {
@@ -211,7 +218,6 @@ var gcphase uint32
 var writeBarrier struct {
        enabled bool    // compiler emits a check of this before calling write barrier
        pad     [3]byte // compiler uses 32-bit load for "enabled" field
-       needed  bool    // identical to enabled, for now (TODO: dedup)
        alignme uint64  // guarantee alignment so that compiler can use a 32 or 64-bit load
 }
 
@@ -229,8 +235,7 @@ const (
 //go:nosplit
 func setGCPhase(x uint32) {
        atomic.Store(&gcphase, x)
-       writeBarrier.needed = gcphase == _GCmark || gcphase == _GCmarktermination
-       writeBarrier.enabled = writeBarrier.needed
+       writeBarrier.enabled = gcphase == _GCmark || gcphase == _GCmarktermination
 }
 
 // gcMarkWorkerMode represents the mode that a concurrent mark worker
@@ -469,7 +474,6 @@ func GC() {
        // as part of tests and benchmarks to get the system into a
        // relatively stable and isolated state.
        for work.cycles.Load() == n+1 && sweepone() != ^uintptr(0) {
-               sweep.nbgsweep++
                Gosched()
        }
 
@@ -568,10 +572,6 @@ func (t gcTrigger) test() bool {
        }
        switch t.kind {
        case gcTriggerHeap:
-               // Non-atomic access to gcController.heapLive for performance. If
-               // we are going to trigger on this, this thread just
-               // atomically wrote gcController.heapLive anyway and we'll see our
-               // own write.
                trigger, _ := gcController.trigger()
                return gcController.heapLive.Load() >= trigger
        case gcTriggerTime:
@@ -617,7 +617,6 @@ func gcStart(trigger gcTrigger) {
        // We check the transition condition continuously here in case
        // this G gets delayed in to the next GC cycle.
        for trigger.test() && sweepone() != ^uintptr(0) {
-               sweep.nbgsweep++
        }
 
        // Perform GC initialization and the sweep termination
@@ -648,8 +647,10 @@ func gcStart(trigger gcTrigger) {
        // Update it under gcsema to avoid gctrace getting wrong values.
        work.userForced = trigger.kind == gcTriggerCycle
 
-       if traceEnabled() {
-               traceGCStart()
+       trace := traceAcquire()
+       if trace.ok() {
+               trace.GCStart()
+               traceRelease(trace)
        }
 
        // Check that all Ps have finished deferred mcache flushes.
@@ -683,7 +684,7 @@ func gcStart(trigger gcTrigger) {
                finishsweep_m()
        })
 
-       // clearpools before we start the GC. If we wait they memory will not be
+       // clearpools before we start the GC. If we wait the memory will not be
        // reclaimed until the next GC cycle.
        clearpools()
 
@@ -715,11 +716,11 @@ func gcStart(trigger gcTrigger) {
        // enabled because they must be enabled before
        // any non-leaf heap objects are marked. Since
        // allocations are blocked until assists can
-       // happen, we want enable assists as early as
+       // happen, we want to enable assists as early as
        // possible.
        setGCPhase(_GCmark)
 
-       gcBgMarkPrepare() // Must happen before assist enable.
+       gcBgMarkPrepare() // Must happen before assists are enabled.
        gcMarkRootPrepare()
 
        // Mark all active tinyalloc blocks. Since we're
@@ -965,6 +966,7 @@ func gcMarkTermination() {
                // before continuing.
        })
 
+       var stwSwept bool
        systemstack(func() {
                work.heap2 = work.bytesMarked
                if debug.gccheckmark > 0 {
@@ -983,14 +985,16 @@ func gcMarkTermination() {
 
                // marking is complete so we can turn the write barrier off
                setGCPhase(_GCoff)
-               gcSweep(work.mode)
+               stwSwept = gcSweep(work.mode)
        })
 
        mp.traceback = 0
        casgstatus(curgp, _Gwaiting, _Grunning)
 
-       if traceEnabled() {
-               traceGCDone()
+       trace := traceAcquire()
+       if trace.ok() {
+               trace.GCDone()
+               traceRelease(trace)
        }
 
        // all done
@@ -1043,10 +1047,6 @@ func gcMarkTermination() {
        // Reset idle time stat.
        sched.idleTime.Store(0)
 
-       // Reset sweep state.
-       sweep.nbgsweep = 0
-       sweep.npausesweep = 0
-
        if work.userForced {
                memstats.numforcedgc++
        }
@@ -1075,9 +1075,19 @@ func gcMarkTermination() {
        // Those aren't tracked in any sweep lists, so we need to
        // count them against sweep completion until we ensure all
        // those spans have been forced out.
+       //
+       // If gcSweep fully swept the heap (for example if the sweep
+       // is not concurrent due to a GODEBUG setting), then we expect
+       // the sweepLocker to be invalid, since sweeping is done.
+       //
+       // N.B. Below we might duplicate some work from gcSweep; this is
+       // fine as all that work is idempotent within a GC cycle, and
+       // we're still holding worldsema so a new cycle can't start.
        sl := sweep.active.begin()
-       if !sl.valid {
+       if !stwSwept && !sl.valid {
                throw("failed to set sweep barrier")
+       } else if stwSwept && sl.valid {
+               throw("non-concurrent sweep failed to drain all sweep queues")
        }
 
        systemstack(func() { startTheWorldWithSema() })
@@ -1119,9 +1129,15 @@ func gcMarkTermination() {
                        pp.pinnerCache = nil
                })
        })
-       // Now that we've swept stale spans in mcaches, they don't
-       // count against unswept spans.
-       sweep.active.end(sl)
+       if sl.valid {
+               // Now that we've swept stale spans in mcaches, they don't
+               // count against unswept spans.
+               //
+               // Note: this sweepLocker may not be valid if sweeping had
+               // already completed during the STW. See the corresponding
+               // begin() call that produced sl.
+               sweep.active.end(sl)
+       }
 
        // Print gctrace before dropping worldsema. As soon as we drop
        // worldsema another cycle could start and smash the stats
@@ -1359,7 +1375,7 @@ func gcBgMarkWorker() {
                        default:
                                throw("gcBgMarkWorker: unexpected gcMarkWorkerMode")
                        case gcMarkWorkerDedicatedMode:
-                               gcDrain(&pp.gcw, gcDrainUntilPreempt|gcDrainFlushBgCredit)
+                               gcDrainMarkWorkerDedicated(&pp.gcw, true)
                                if gp.preempt {
                                        // We were preempted. This is
                                        // a useful signal to kick
@@ -1374,11 +1390,11 @@ func gcBgMarkWorker() {
                                }
                                // Go back to draining, this time
                                // without preemption.
-                               gcDrain(&pp.gcw, gcDrainFlushBgCredit)
+                               gcDrainMarkWorkerDedicated(&pp.gcw, false)
                        case gcMarkWorkerFractionalMode:
-                               gcDrain(&pp.gcw, gcDrainFractional|gcDrainUntilPreempt|gcDrainFlushBgCredit)
+                               gcDrainMarkWorkerFractional(&pp.gcw)
                        case gcMarkWorkerIdleMode:
-                               gcDrain(&pp.gcw, gcDrainIdle|gcDrainUntilPreempt|gcDrainFlushBgCredit)
+                               gcDrainMarkWorkerIdle(&pp.gcw)
                        }
                        casgstatus(gp, _Gwaiting, _Grunning)
                })
@@ -1534,10 +1550,12 @@ func gcMark(startTime int64) {
 // gcSweep must be called on the system stack because it acquires the heap
 // lock. See mheap for details.
 //
+// Returns true if the heap was fully swept by this function.
+//
 // The world must be stopped.
 //
 //go:systemstack
-func gcSweep(mode gcMode) {
+func gcSweep(mode gcMode) bool {
        assertWorldStopped()
 
        if gcphase != _GCoff {
@@ -1555,15 +1573,18 @@ func gcSweep(mode gcMode) {
 
        sweep.centralIndex.clear()
 
-       if !_ConcurrentSweep || mode == gcForceBlockMode {
+       if !concurrentSweep || mode == gcForceBlockMode {
                // Special case synchronous sweep.
                // Record that no proportional sweeping has to happen.
                lock(&mheap_.lock)
                mheap_.sweepPagesPerByte = 0
                unlock(&mheap_.lock)
+               // Flush all mcaches.
+               for _, pp := range allp {
+                       pp.mcache.prepareForSweep()
+               }
                // Sweep all spans eagerly.
                for sweepone() != ^uintptr(0) {
-                       sweep.npausesweep++
                }
                // Free workbufs eagerly.
                prepareFreeWorkbufs()
@@ -1574,7 +1595,7 @@ func gcSweep(mode gcMode) {
                // available immediately.
                mProf_NextCycle()
                mProf_Flush()
-               return
+               return true
        }
 
        // Background sweep.
@@ -1584,6 +1605,7 @@ func gcSweep(mode gcMode) {
                ready(sweep.g, 0, true)
        }
        unlock(&sweep.lock)
+       return false
 }
 
 // gcResetMarkState resets global state prior to marking (concurrent