| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "fmt" |
| "os" |
| "runtime" |
| "runtime/debug" |
| "sync/atomic" |
| "time" |
| ) |
| |
| func init() { |
| register("GCFairness", GCFairness) |
| register("GCFairness2", GCFairness2) |
| register("GCSys", GCSys) |
| } |
| |
| func GCSys() { |
| runtime.GOMAXPROCS(1) |
| memstats := new(runtime.MemStats) |
| runtime.GC() |
| runtime.ReadMemStats(memstats) |
| sys := memstats.Sys |
| |
| runtime.MemProfileRate = 0 // disable profiler |
| |
| itercount := 100000 |
| for i := 0; i < itercount; i++ { |
| workthegc() |
| } |
| |
| // Should only be using a few MB. |
| // We allocated 100 MB or (if not short) 1 GB. |
| runtime.ReadMemStats(memstats) |
| if sys > memstats.Sys { |
| sys = 0 |
| } else { |
| sys = memstats.Sys - sys |
| } |
| if sys > 16<<20 { |
| fmt.Printf("using too much memory: %d bytes\n", sys) |
| return |
| } |
| fmt.Printf("OK\n") |
| } |
| |
| func workthegc() []byte { |
| return make([]byte, 1029) |
| } |
| |
| func GCFairness() { |
| runtime.GOMAXPROCS(1) |
| f, err := os.Open("/dev/null") |
| if os.IsNotExist(err) { |
| // This test tests what it is intended to test only if writes are fast. |
| // If there is no /dev/null, we just don't execute the test. |
| fmt.Println("OK") |
| return |
| } |
| if err != nil { |
| fmt.Println(err) |
| os.Exit(1) |
| } |
| for i := 0; i < 2; i++ { |
| go func() { |
| for { |
| f.Write([]byte(".")) |
| } |
| }() |
| } |
| time.Sleep(10 * time.Millisecond) |
| fmt.Println("OK") |
| } |
| |
| func GCFairness2() { |
| // Make sure user code can't exploit the GC's high priority |
| // scheduling to make scheduling of user code unfair. See |
| // issue #15706. |
| runtime.GOMAXPROCS(1) |
| debug.SetGCPercent(1) |
| var count [3]int64 |
| var sink [3]interface{} |
| for i := range count { |
| go func(i int) { |
| for { |
| sink[i] = make([]byte, 1024) |
| atomic.AddInt64(&count[i], 1) |
| } |
| }(i) |
| } |
| // Note: If the unfairness is really bad, it may not even get |
| // past the sleep. |
| // |
| // If the scheduling rules change, this may not be enough time |
| // to let all goroutines run, but for now we cycle through |
| // them rapidly. |
| // |
| // OpenBSD's scheduler makes every usleep() take at least |
| // 20ms, so we need a long time to ensure all goroutines have |
| // run. If they haven't run after 30ms, give it another 1000ms |
| // and check again. |
| time.Sleep(30 * time.Millisecond) |
| var fail bool |
| for i := range count { |
| if atomic.LoadInt64(&count[i]) == 0 { |
| fail = true |
| } |
| } |
| if fail { |
| time.Sleep(1 * time.Second) |
| for i := range count { |
| if atomic.LoadInt64(&count[i]) == 0 { |
| fmt.Printf("goroutine %d did not run\n", i) |
| return |
| } |
| } |
| } |
| fmt.Println("OK") |
| } |