aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/export_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime/export_test.go')
-rw-r--r--src/runtime/export_test.go104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index 0cf2fb4ea7..3916eaf0e3 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -1372,3 +1372,107 @@ func NewPIController(kp, ti, tt, min, max float64) *PIController {
func (c *PIController) Next(input, setpoint, period float64) (float64, bool) {
return c.piController.next(input, setpoint, period)
}
+
+const ScavengePercent = scavengePercent
+
+type Scavenger struct {
+ Sleep func(int64) int64
+ Scavenge func(uintptr) (uintptr, int64)
+ ShouldStop func() bool
+ GoMaxProcs func() int32
+
+ released atomic.Uintptr
+ scavenger scavengerState
+ stop chan<- struct{}
+ done <-chan struct{}
+}
+
+func (s *Scavenger) Start() {
+ if s.Sleep == nil || s.Scavenge == nil || s.ShouldStop == nil || s.GoMaxProcs == nil {
+ panic("must populate all stubs")
+ }
+
+ // Install hooks.
+ s.scavenger.sleepStub = s.Sleep
+ s.scavenger.scavenge = s.Scavenge
+ s.scavenger.shouldStop = s.ShouldStop
+ s.scavenger.gomaxprocs = s.GoMaxProcs
+
+ // Start up scavenger goroutine, and wait for it to be ready.
+ stop := make(chan struct{})
+ s.stop = stop
+ done := make(chan struct{})
+ s.done = done
+ go func() {
+ // This should match bgscavenge, loosely.
+ s.scavenger.init()
+ s.scavenger.park()
+ for {
+ select {
+ case <-stop:
+ close(done)
+ return
+ default:
+ }
+ released, workTime := s.scavenger.run()
+ if released == 0 {
+ s.scavenger.park()
+ continue
+ }
+ s.released.Add(released)
+ s.scavenger.sleep(workTime)
+ }
+ }()
+ if !s.BlockUntilParked(1e9 /* 1 second */) {
+ panic("timed out waiting for scavenger to get ready")
+ }
+}
+
+// BlockUntilParked blocks until the scavenger parks, or until
+// timeout is exceeded. Returns true if the scavenger parked.
+//
+// Note that in testing, parked means something slightly different.
+// In anger, the scavenger parks to sleep, too, but in testing,
+// it only parks when it actually has no work to do.
+func (s *Scavenger) BlockUntilParked(timeout int64) bool {
+ // Just spin, waiting for it to park.
+ //
+ // The actual parking process is racy with respect to
+ // wakeups, which is fine, but for testing we need something
+ // a bit more robust.
+ start := nanotime()
+ for nanotime()-start < timeout {
+ lock(&s.scavenger.lock)
+ parked := s.scavenger.parked
+ unlock(&s.scavenger.lock)
+ if parked {
+ return true
+ }
+ Gosched()
+ }
+ return false
+}
+
+// Released returns how many bytes the scavenger released.
+func (s *Scavenger) Released() uintptr {
+ return s.released.Load()
+}
+
+// Wake wakes up a parked scavenger to keep running.
+func (s *Scavenger) Wake() {
+ s.scavenger.wake()
+}
+
+// Stop cleans up the scavenger's resources. The scavenger
+// must be parked for this to work.
+func (s *Scavenger) Stop() {
+ lock(&s.scavenger.lock)
+ parked := s.scavenger.parked
+ unlock(&s.scavenger.lock)
+ if !parked {
+ panic("tried to clean up scavenger that is not parked")
+ }
+ close(s.stop)
+ s.Wake()
+ <-s.done
+}