]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: support disabling goroutine scheduling by class
authorAustin Clements <austin@google.com>
Tue, 11 Sep 2018 15:28:24 +0000 (11:28 -0400)
committerAustin Clements <austin@google.com>
Tue, 2 Oct 2018 20:35:29 +0000 (20:35 +0000)
This adds support for disabling the scheduling of user goroutines
while allowing system goroutines like the garbage collector to
continue running. User goroutines pass through the usual state
transitions, but if we attempt to actually schedule one, it will get
put on a deferred scheduling list.

Updates #26903. This is preparation for unifying STW GC and concurrent
GC.

Updates #25578. This same mechanism can form the basis for disabling
all but a single user goroutine for the purposes of debugger function
call injection.

Change-Id: Ib72a808e00c25613fe6982f5528160d3de3dbbc6
Reviewed-on: https://go-review.googlesource.com/c/134779
Run-TryBot: Austin Clements <austin@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
src/runtime/proc.go
src/runtime/runtime2.go

index c477910c9e7e5bf55a7273b5650d8f706618a5f4..0a7321254cbdca0263bb18fc7a767af8a8b717cb 100644 (file)
@@ -2615,6 +2615,23 @@ top:
                resetspinning()
        }
 
+       if sched.disable.user && !schedEnabled(gp) {
+               // Scheduling of this goroutine is disabled. Put it on
+               // the list of pending runnable goroutines for when we
+               // re-enable user scheduling and look again.
+               lock(&sched.lock)
+               if schedEnabled(gp) {
+                       // Something re-enabled scheduling while we
+                       // were acquiring the lock.
+                       unlock(&sched.lock)
+               } else {
+                       sched.disable.runnable.pushBack(gp)
+                       sched.disable.n++
+                       unlock(&sched.lock)
+                       goto top
+               }
+       }
+
        if gp.lockedm != 0 {
                // Hands off own p to the locked m,
                // then blocks waiting for a new p.
@@ -3033,6 +3050,12 @@ func exitsyscall() {
                        _g_.stackguard0 = _g_.stack.lo + _StackGuard
                }
                _g_.throwsplit = false
+
+               if sched.disable.user && !schedEnabled(_g_) {
+                       // Scheduling of this goroutine is disabled.
+                       Gosched()
+               }
+
                return
        }
 
@@ -3168,7 +3191,10 @@ func exitsyscall0(gp *g) {
        casgstatus(gp, _Gsyscall, _Grunnable)
        dropg()
        lock(&sched.lock)
-       _p_ := pidleget()
+       var _p_ *p
+       if schedEnabled(_g_) {
+               _p_ = pidleget()
+       }
        if _p_ == nil {
                globrunqput(gp)
        } else if atomic.Load(&sched.sysmonwait) != 0 {
@@ -4625,6 +4651,40 @@ func schedtrace(detailed bool) {
        unlock(&sched.lock)
 }
 
+// schedEnableUser enables or disables the scheduling of user
+// goroutines.
+//
+// This does not stop already running user goroutines, so the caller
+// should first stop the world when disabling user goroutines.
+func schedEnableUser(enable bool) {
+       lock(&sched.lock)
+       if sched.disable.user == !enable {
+               unlock(&sched.lock)
+               return
+       }
+       sched.disable.user = !enable
+       if enable {
+               n := sched.disable.n
+               sched.disable.n = 0
+               globrunqputbatch(&sched.disable.runnable, n)
+               unlock(&sched.lock)
+               for ; n != 0 && sched.npidle != 0; n-- {
+                       startm(nil, false)
+               }
+       } else {
+               unlock(&sched.lock)
+       }
+}
+
+// schedEnabled returns whether gp should be scheduled. It returns
+// false is scheduling of gp is disabled.
+func schedEnabled(gp *g) bool {
+       if sched.disable.user {
+               return isSystemGoroutine(gp, true)
+       }
+       return true
+}
+
 // Put mp on midle list.
 // Sched must be locked.
 // May run during STW, so write barriers are not allowed.
index 259bb376ae2fd05df8ca03f40e91fe403ffb078b..fbca3d3ba6278756f8b27daeb01572fc9143593e 100644 (file)
@@ -580,6 +580,18 @@ type schedt struct {
        runq     gQueue
        runqsize int32
 
+       // disable controls selective disabling of the scheduler.
+       //
+       // Use schedEnableUser to control this.
+       //
+       // disable is protected by sched.lock.
+       disable struct {
+               // user disables scheduling of user goroutines.
+               user     bool
+               runnable gQueue // pending runnable Gs
+               n        int32  // length of runnable
+       }
+
        // Global cache of dead G's.
        gFree struct {
                lock    mutex