]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: test that periodic GC works
authorAustin Clements <austin@google.com>
Wed, 5 Aug 2015 15:35:28 +0000 (11:35 -0400)
committerAustin Clements <austin@google.com>
Wed, 30 Sep 2015 19:24:07 +0000 (19:24 +0000)
We've broken periodic GC a few times without noticing because there's
no test for it, partly because you have to wait two minutes to see if
it happens. This exposes control of the periodic GC timeout to runtime
tests and adds a test that cranks it down to zero and sleeps for a bit
to make sure periodic GCs happen.

Change-Id: I3ec44e967e99f4eda752f85c329eebd18b87709e
Reviewed-on: https://go-review.googlesource.com/13169
Reviewed-by: Rick Hudson <rlh@golang.org>
Run-TryBot: Austin Clements <austin@google.com>

src/runtime/export_test.go
src/runtime/gc_test.go
src/runtime/proc1.go

index f14dc30a7f1027c38b13d87fd8662a920841b398..06ffbf6191812f6bd9dcb1ba298b0c09fabf72b1 100644 (file)
@@ -155,3 +155,5 @@ const PtrSize = ptrSize
 
 var TestingAssertE2I2GC = &testingAssertE2I2GC
 var TestingAssertE2T2GC = &testingAssertE2T2GC
+
+var ForceGCPeriod = &forcegcperiod
index 6c9b314c65e0dbc58f301737c2072e0f0d1d77b1..61bbc1494545dd7343ee2f31e5543d8a7404a683 100644 (file)
@@ -199,6 +199,37 @@ func TestHugeGCInfo(t *testing.T) {
        }
 }
 
+func TestPeriodicGC(t *testing.T) {
+       // Make sure we're not in the middle of a GC.
+       runtime.GC()
+
+       var ms1, ms2 runtime.MemStats
+       runtime.ReadMemStats(&ms1)
+
+       // Make periodic GC run continuously.
+       orig := *runtime.ForceGCPeriod
+       *runtime.ForceGCPeriod = 0
+
+       // Let some periodic GCs happen. In a heavily loaded system,
+       // it's possible these will be delayed, so this is designed to
+       // succeed quickly if things are working, but to give it some
+       // slack if things are slow.
+       var numGCs uint32
+       const want = 2
+       for i := 0; i < 20 && numGCs < want; i++ {
+               time.Sleep(5 * time.Millisecond)
+
+               // Test that periodic GC actually happened.
+               runtime.ReadMemStats(&ms2)
+               numGCs = ms2.NumGC - ms1.NumGC
+       }
+       *runtime.ForceGCPeriod = orig
+
+       if numGCs < want {
+               t.Fatalf("no periodic GC: got %v GCs, want >= 2", numGCs)
+       }
+}
+
 func BenchmarkSetTypePtr(b *testing.B) {
        benchSetType(b, new(*byte))
 }
index 47f11b6ee10ee9554a97a8be05dee5ecb588301a..ec60f8c0d0482c7e56c92ee0973631aead57e85f 100644 (file)
@@ -2962,10 +2962,14 @@ func checkdead() {
        throw("all goroutines are asleep - deadlock!")
 }
 
-func sysmon() {
-       // If we go two minutes without a garbage collection, force one to run.
-       forcegcperiod := int64(2 * 60 * 1e9)
+// forcegcperiod is the maximum time in nanoseconds between garbage
+// collections. If we go this long without a garbage collection, one
+// is forced to run.
+//
+// This is a variable for testing purposes. It normally doesn't change.
+var forcegcperiod int64 = 2 * 60 * 1e9
 
+func sysmon() {
        // If a heap span goes unused for 5 minutes after a garbage collection,
        // we hand it back to the operating system.
        scavengelimit := int64(5 * 60 * 1e9)
@@ -2979,12 +2983,6 @@ func sysmon() {
        lastscavenge := nanotime()
        nscavenge := 0
 
-       // Make wake-up period small enough for the sampling to be correct.
-       maxsleep := forcegcperiod / 2
-       if scavengelimit < forcegcperiod {
-               maxsleep = scavengelimit / 2
-       }
-
        lasttrace := int64(0)
        idle := 0 // how many cycles in succession we had not wokeup somebody
        delay := uint32(0)
@@ -3003,6 +3001,12 @@ func sysmon() {
                        if atomicload(&sched.gcwaiting) != 0 || atomicload(&sched.npidle) == uint32(gomaxprocs) {
                                atomicstore(&sched.sysmonwait, 1)
                                unlock(&sched.lock)
+                               // Make wake-up period small enough
+                               // for the sampling to be correct.
+                               maxsleep := forcegcperiod / 2
+                               if scavengelimit < forcegcperiod {
+                                       maxsleep = scavengelimit / 2
+                               }
                                notetsleep(&sched.sysmonnote, maxsleep)
                                lock(&sched.lock)
                                atomicstore(&sched.sysmonwait, 0)