Commit 47f68dc2 by Ian Lance Taylor

runtime: make NumGoroutine wait for system goroutines to register

    
    In libgo system goroutines register themselves after they start.
    That means that there is a small race between the goroutine being
    seen by the scheduler and the scheduler knowing that the goroutine
    is a system goroutine. That in turn means that runtime.NumGoroutines
    can overestimate the number of goroutines at times.
    
    This patch fixes the overestimate by counting the number of system
    goroutines waiting to start, and pausing NumGoroutines until those
    goroutines have all registered.
    
    This is kind of a lot of mechanism for this not very important
    problem, but I couldn't think of a better approach.
    
    The test for this is TestNumGoroutine in runtime/proc_test.go.
    The test is not currently run, but it will be soon.
    
    Reviewed-on: https://go-review.googlesource.com/46457

From-SVN: r249565
parent b8f70831
54d83c2d67c35ad4f622936d2fbf81c17354fff9 681c8a7b0a9d52c0b81e7a4b1c55fe65ed889573
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -54,6 +54,7 @@ func NumCgoCall() int64 { ...@@ -54,6 +54,7 @@ func NumCgoCall() int64 {
// NumGoroutine returns the number of goroutines that currently exist. // NumGoroutine returns the number of goroutines that currently exist.
func NumGoroutine() int { func NumGoroutine() int {
waitForSystemGoroutines()
return int(gcount()) return int(gcount())
} }
......
...@@ -106,6 +106,7 @@ var ( ...@@ -106,6 +106,7 @@ var (
func createfing() { func createfing() {
// start the finalizer goroutine exactly once // start the finalizer goroutine exactly once
if fingCreate == 0 && atomic.Cas(&fingCreate, 0, 1) { if fingCreate == 0 && atomic.Cas(&fingCreate, 0, 1) {
expectSystemGoroutine()
go runfinq() go runfinq()
} }
} }
......
...@@ -209,6 +209,7 @@ func readgogc() int32 { ...@@ -209,6 +209,7 @@ func readgogc() int32 {
// It kicks off the background sweeper goroutine and enables GC. // It kicks off the background sweeper goroutine and enables GC.
func gcenable() { func gcenable() {
c := make(chan int, 1) c := make(chan int, 1)
expectSystemGoroutine()
go bgsweep(c) go bgsweep(c)
<-c <-c
memstats.enablegc = true // now that runtime is initialized, GC is okay memstats.enablegc = true // now that runtime is initialized, GC is okay
...@@ -1399,6 +1400,7 @@ func gcBgMarkStartWorkers() { ...@@ -1399,6 +1400,7 @@ func gcBgMarkStartWorkers() {
break break
} }
if p.gcBgMarkWorker == 0 { if p.gcBgMarkWorker == 0 {
expectSystemGoroutine()
go gcBgMarkWorker(p) go gcBgMarkWorker(p)
notetsleepg(&work.bgMarkReady, -1) notetsleepg(&work.bgMarkReady, -1)
noteclear(&work.bgMarkReady) noteclear(&work.bgMarkReady)
......
...@@ -233,6 +233,7 @@ func os_beforeExit() { ...@@ -233,6 +233,7 @@ func os_beforeExit() {
// start forcegc helper goroutine // start forcegc helper goroutine
func init() { func init() {
expectSystemGoroutine()
go forcegchelper() go forcegchelper()
} }
...@@ -2728,6 +2729,28 @@ func newproc(fn uintptr, arg unsafe.Pointer) *g { ...@@ -2728,6 +2729,28 @@ func newproc(fn uintptr, arg unsafe.Pointer) *g {
return newg return newg
} }
// expectedSystemGoroutines counts the number of goroutines expected
// to mark themselves as system goroutines. After they mark themselves
// by calling setSystemGoroutine, this is decremented. NumGoroutines
// uses this to wait for all system goroutines to mark themselves
// before it counts them.
var expectedSystemGoroutines uint32
// expectSystemGoroutine is called when starting a goroutine that will
// call setSystemGoroutine. It increments expectedSystemGoroutines.
func expectSystemGoroutine() {
atomic.Xadd(&expectedSystemGoroutines, +1)
}
// waitForSystemGoroutines waits for all currently expected system
// goroutines to register themselves.
func waitForSystemGoroutines() {
for atomic.Load(&expectedSystemGoroutines) > 0 {
Gosched()
osyield()
}
}
// setSystemGoroutine marks this goroutine as a "system goroutine". // setSystemGoroutine marks this goroutine as a "system goroutine".
// In the gc toolchain this is done by comparing startpc to a list of // In the gc toolchain this is done by comparing startpc to a list of
// saved special PCs. In gccgo that approach does not work as startpc // saved special PCs. In gccgo that approach does not work as startpc
...@@ -2738,6 +2761,7 @@ func newproc(fn uintptr, arg unsafe.Pointer) *g { ...@@ -2738,6 +2761,7 @@ func newproc(fn uintptr, arg unsafe.Pointer) *g {
func setSystemGoroutine() { func setSystemGoroutine() {
getg().isSystemGoroutine = true getg().isSystemGoroutine = true
atomic.Xadd(&sched.ngsys, +1) atomic.Xadd(&sched.ngsys, +1)
atomic.Xadd(&expectedSystemGoroutines, -1)
} }
// Put on gfree list. // Put on gfree list.
......
...@@ -113,6 +113,7 @@ func addtimerLocked(t *timer) { ...@@ -113,6 +113,7 @@ func addtimerLocked(t *timer) {
} }
if !timers.created { if !timers.created {
timers.created = true timers.created = true
expectSystemGoroutine()
go timerproc() go timerproc()
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment