runtime: make NumGoroutine wait for system goroutines to register
authorIan Lance Taylor <ian@gcc.gnu.org>
Thu, 22 Jun 2017 15:46:47 +0000 (15:46 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Thu, 22 Jun 2017 15:46:47 +0000 (15:46 +0000)
    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

gcc/go/gofrontend/MERGE
libgo/go/runtime/debug.go
libgo/go/runtime/mfinal.go
libgo/go/runtime/mgc.go
libgo/go/runtime/proc.go
libgo/go/runtime/time.go

index dbff708d9323730c1fca27e987b5416b4082f775..ccc7919d2c22ee5cab7a6a4f7bedc4c7abbe53f4 100644 (file)
@@ -1,4 +1,4 @@
-54d83c2d67c35ad4f622936d2fbf81c17354fff9
+681c8a7b0a9d52c0b81e7a4b1c55fe65ed889573
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 6a9efcd7da7a59cbb26fe4c50b13a7000705707f..fdd73463aba0c22c12bd0e00371ffeb40c446b28 100644 (file)
@@ -54,6 +54,7 @@ func NumCgoCall() int64 {
 
 // NumGoroutine returns the number of goroutines that currently exist.
 func NumGoroutine() int {
+       waitForSystemGoroutines()
        return int(gcount())
 }
 
index 615a2b295222fa86bed4bc7a870b98ab555b4de0..229ccb55387892a4cf9c09ac9a2cfb6cbdced87c 100644 (file)
@@ -106,6 +106,7 @@ var (
 func createfing() {
        // start the finalizer goroutine exactly once
        if fingCreate == 0 && atomic.Cas(&fingCreate, 0, 1) {
+               expectSystemGoroutine()
                go runfinq()
        }
 }
index 5cee12d8b81cae7da393dbc74dc35730c80e294e..ebb00e155d02f754091a6400bdb64344eda32f01 100644 (file)
@@ -209,6 +209,7 @@ func readgogc() int32 {
 // It kicks off the background sweeper goroutine and enables GC.
 func gcenable() {
        c := make(chan int, 1)
+       expectSystemGoroutine()
        go bgsweep(c)
        <-c
        memstats.enablegc = true // now that runtime is initialized, GC is okay
@@ -1399,6 +1400,7 @@ func gcBgMarkStartWorkers() {
                        break
                }
                if p.gcBgMarkWorker == 0 {
+                       expectSystemGoroutine()
                        go gcBgMarkWorker(p)
                        notetsleepg(&work.bgMarkReady, -1)
                        noteclear(&work.bgMarkReady)
index 066d0e52de6ee411e58eb61b44fc32352546f713..cb1e974eb53613a3fbd1a4ad93f8d6985323f561 100644 (file)
@@ -233,6 +233,7 @@ func os_beforeExit() {
 
 // start forcegc helper goroutine
 func init() {
+       expectSystemGoroutine()
        go forcegchelper()
 }
 
@@ -2728,6 +2729,28 @@ func newproc(fn uintptr, arg unsafe.Pointer) *g {
        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".
 // 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
@@ -2738,6 +2761,7 @@ func newproc(fn uintptr, arg unsafe.Pointer) *g {
 func setSystemGoroutine() {
        getg().isSystemGoroutine = true
        atomic.Xadd(&sched.ngsys, +1)
+       atomic.Xadd(&expectedSystemGoroutines, -1)
 }
 
 // Put on gfree list.
index e85fc7a54fa2af5cbad857c665ba77cbe1748914..cc167a8bcfe96cfcf2174fb980b14c75f5d8ed69 100644 (file)
@@ -113,6 +113,7 @@ func addtimerLocked(t *timer) {
        }
        if !timers.created {
                timers.created = true
+               expectSystemGoroutine()
                go timerproc()
        }
 }