Skip to content

Commit 6e9fb11

Browse files
committed
runtime: support disabling goroutine scheduling by class
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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Rick Hudson <[email protected]>
1 parent 29b21ec commit 6e9fb11

File tree

2 files changed

+73
-1
lines changed

2 files changed

+73
-1
lines changed

src/runtime/proc.go

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2615,6 +2615,23 @@ top:
26152615
resetspinning()
26162616
}
26172617

2618+
if sched.disable.user && !schedEnabled(gp) {
2619+
// Scheduling of this goroutine is disabled. Put it on
2620+
// the list of pending runnable goroutines for when we
2621+
// re-enable user scheduling and look again.
2622+
lock(&sched.lock)
2623+
if schedEnabled(gp) {
2624+
// Something re-enabled scheduling while we
2625+
// were acquiring the lock.
2626+
unlock(&sched.lock)
2627+
} else {
2628+
sched.disable.runnable.pushBack(gp)
2629+
sched.disable.n++
2630+
unlock(&sched.lock)
2631+
goto top
2632+
}
2633+
}
2634+
26182635
if gp.lockedm != 0 {
26192636
// Hands off own p to the locked m,
26202637
// then blocks waiting for a new p.
@@ -3033,6 +3050,12 @@ func exitsyscall() {
30333050
_g_.stackguard0 = _g_.stack.lo + _StackGuard
30343051
}
30353052
_g_.throwsplit = false
3053+
3054+
if sched.disable.user && !schedEnabled(_g_) {
3055+
// Scheduling of this goroutine is disabled.
3056+
Gosched()
3057+
}
3058+
30363059
return
30373060
}
30383061

@@ -3168,7 +3191,10 @@ func exitsyscall0(gp *g) {
31683191
casgstatus(gp, _Gsyscall, _Grunnable)
31693192
dropg()
31703193
lock(&sched.lock)
3171-
_p_ := pidleget()
3194+
var _p_ *p
3195+
if schedEnabled(_g_) {
3196+
_p_ = pidleget()
3197+
}
31723198
if _p_ == nil {
31733199
globrunqput(gp)
31743200
} else if atomic.Load(&sched.sysmonwait) != 0 {
@@ -4625,6 +4651,40 @@ func schedtrace(detailed bool) {
46254651
unlock(&sched.lock)
46264652
}
46274653

4654+
// schedEnableUser enables or disables the scheduling of user
4655+
// goroutines.
4656+
//
4657+
// This does not stop already running user goroutines, so the caller
4658+
// should first stop the world when disabling user goroutines.
4659+
func schedEnableUser(enable bool) {
4660+
lock(&sched.lock)
4661+
if sched.disable.user == !enable {
4662+
unlock(&sched.lock)
4663+
return
4664+
}
4665+
sched.disable.user = !enable
4666+
if enable {
4667+
n := sched.disable.n
4668+
sched.disable.n = 0
4669+
globrunqputbatch(&sched.disable.runnable, n)
4670+
unlock(&sched.lock)
4671+
for ; n != 0 && sched.npidle != 0; n-- {
4672+
startm(nil, false)
4673+
}
4674+
} else {
4675+
unlock(&sched.lock)
4676+
}
4677+
}
4678+
4679+
// schedEnabled returns whether gp should be scheduled. It returns
4680+
// false is scheduling of gp is disabled.
4681+
func schedEnabled(gp *g) bool {
4682+
if sched.disable.user {
4683+
return isSystemGoroutine(gp, true)
4684+
}
4685+
return true
4686+
}
4687+
46284688
// Put mp on midle list.
46294689
// Sched must be locked.
46304690
// May run during STW, so write barriers are not allowed.

src/runtime/runtime2.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,18 @@ type schedt struct {
580580
runq gQueue
581581
runqsize int32
582582

583+
// disable controls selective disabling of the scheduler.
584+
//
585+
// Use schedEnableUser to control this.
586+
//
587+
// disable is protected by sched.lock.
588+
disable struct {
589+
// user disables scheduling of user goroutines.
590+
user bool
591+
runnable gQueue // pending runnable Gs
592+
n int32 // length of runnable
593+
}
594+
583595
// Global cache of dead G's.
584596
gFree struct {
585597
lock mutex

0 commit comments

Comments
 (0)