aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/proc.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtime/proc.go')
-rw-r--r--src/runtime/proc.go63
1 files changed, 60 insertions, 3 deletions
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index 4ccce90e93..5b8db2bee4 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -835,6 +835,7 @@ func schedinit() {
lockInit(&reflectOffs.lock, lockRankReflectOffs)
lockInit(&finlock, lockRankFin)
lockInit(&cpuprof.lock, lockRankCpuprof)
+ lockInit(&computeMaxProcsLock, lockRankComputeMaxProcs)
allocmLock.init(lockRankAllocmR, lockRankAllocmRInternal, lockRankAllocmW)
execLock.init(lockRankExecR, lockRankExecRInternal, lockRankExecW)
traceLockInit()
@@ -1727,7 +1728,6 @@ func startTheWorldWithSema(now int64, w worldStop) int64 {
procs := gomaxprocs
if newprocs != 0 {
procs = newprocs
- sched.customGOMAXPROCS = newprocsCustom
newprocs = 0
}
p1 := procresize(procs)
@@ -6560,7 +6560,58 @@ var (
// GOMAXPROCS updates actually change the value of GOMAXPROCS.
updatemaxprocs = &godebugInc{name: "updatemaxprocs"}
+ // Synchronization and state between updateMaxProcsGoroutine and
+ // sysmon.
updateMaxProcsG updateMaxProcsGState
+
+ // Synchronization between GOMAXPROCS and sysmon.
+ //
+ // Setting GOMAXPROCS via a call to GOMAXPROCS disables automatic
+ // GOMAXPROCS updates.
+ //
+ // We want to make two guarantees to callers of GOMAXPROCS. After
+ // GOMAXPROCS returns:
+ //
+ // 1. The runtime will not make any automatic changes to GOMAXPROCS.
+ //
+ // 2. The runtime will not perform any of the system calls used to
+ // determine the appropriate value of GOMAXPROCS (i.e., it won't
+ // call defaultGOMAXPROCS).
+ //
+ // (1) is the baseline guarantee that everyone needs. The GOMAXPROCS
+ // API isn't useful to anyone if automatic updates may occur after it
+ // returns. This is easily achieved by double-checking the state under
+ // STW before committing an automatic GOMAXPROCS update.
+ //
+ // (2) doesn't matter to most users, as it is isn't observable as long
+ // as (1) holds. However, it can be important to users sandboxing Go.
+ // They want disable these system calls and need some way to know when
+ // they are guaranteed the calls will stop.
+ //
+ // This would be simple to achieve if we simply called
+ // defaultGOMAXPROCS under STW in updateMaxProcsGoroutine below.
+ // However, we would like to avoid scheduling this goroutine every
+ // second when it will almost never do anything. Instead, sysmon calls
+ // defaultGOMAXPROCS to decide whether to schedule
+ // updateMaxProcsGoroutine. Thus we need to synchronize between sysmon
+ // and GOMAXPROCS calls.
+ //
+ // GOMAXPROCS can't hold a runtime mutex across STW. It could hold a
+ // semaphore, but sysmon cannot take semaphores. Instead, we have a
+ // more complex scheme:
+ //
+ // * sysmon holds computeMaxProcsLock while calling defaultGOMAXPROCS.
+ // * sysmon skips the current update if sched.customGOMAXPROCS is
+ // set.
+ // * GOMAXPROCS sets sched.customGOMAXPROCS once it is committed to
+ // changing GOMAXPROCS.
+ // * GOMAXPROCS takes computeMaxProcsLock to wait for outstanding
+ // defaultGOMAXPROCS calls to complete.
+ //
+ // N.B. computeMaxProcsLock could simply be sched.lock, but we want to
+ // avoid holding that lock during the potentially slow
+ // defaultGOMAXPROCS.
+ computeMaxProcsLock mutex
)
// Start GOMAXPROCS update helper goroutine.
@@ -6614,25 +6665,31 @@ func updateMaxProcsGoroutine() {
// TODO(prattmic): this could use a nicer API. Perhaps add it to the
// stw parameter?
newprocs = updateMaxProcsG.procs
- newprocsCustom = false
+ lock(&sched.lock)
+ sched.customGOMAXPROCS = false
+ unlock(&sched.lock)
startTheWorldGC(stw)
}
}
func sysmonUpdateGOMAXPROCS() {
+ // Synchronize with GOMAXPROCS. See comment on computeMaxProcsLock.
+ lock(&computeMaxProcsLock)
+
// No update if GOMAXPROCS was set manually.
lock(&sched.lock)
custom := sched.customGOMAXPROCS
curr := gomaxprocs
unlock(&sched.lock)
if custom {
+ unlock(&computeMaxProcsLock)
return
}
// Don't hold sched.lock while we read the filesystem.
procs := defaultGOMAXPROCS(0)
-
+ unlock(&computeMaxProcsLock)
if procs == curr {
// Nothing to do.
return