|  | // 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") | 
|  | } |