140 lines
4.3 KiB
Go
140 lines
4.3 KiB
Go
// Copyright (c) 2017 Uber Technologies, Inc.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
// Package maxprocs lets Go programs easily configure runtime.GOMAXPROCS to
|
|
// match the configured Linux CPU quota. Unlike the top-level automaxprocs
|
|
// package, it lets the caller configure logging and handle errors.
|
|
package maxprocs // import "go.uber.org/automaxprocs/maxprocs"
|
|
|
|
import (
|
|
"os"
|
|
"runtime"
|
|
|
|
iruntime "go.uber.org/automaxprocs/internal/runtime"
|
|
)
|
|
|
|
const _maxProcsKey = "GOMAXPROCS"
|
|
|
|
func currentMaxProcs() int {
|
|
return runtime.GOMAXPROCS(0)
|
|
}
|
|
|
|
type config struct {
|
|
printf func(string, ...interface{})
|
|
procs func(int, func(v float64) int) (int, iruntime.CPUQuotaStatus, error)
|
|
minGOMAXPROCS int
|
|
roundQuotaFunc func(v float64) int
|
|
}
|
|
|
|
func (c *config) log(fmt string, args ...interface{}) {
|
|
if c.printf != nil {
|
|
c.printf(fmt, args...)
|
|
}
|
|
}
|
|
|
|
// An Option alters the behavior of Set.
|
|
type Option interface {
|
|
apply(*config)
|
|
}
|
|
|
|
// Logger uses the supplied printf implementation for log output. By default,
|
|
// Set doesn't log anything.
|
|
func Logger(printf func(string, ...interface{})) Option {
|
|
return optionFunc(func(cfg *config) {
|
|
cfg.printf = printf
|
|
})
|
|
}
|
|
|
|
// Min sets the minimum GOMAXPROCS value that will be used.
|
|
// Any value below 1 is ignored.
|
|
func Min(n int) Option {
|
|
return optionFunc(func(cfg *config) {
|
|
if n >= 1 {
|
|
cfg.minGOMAXPROCS = n
|
|
}
|
|
})
|
|
}
|
|
|
|
// RoundQuotaFunc sets the function that will be used to covert the CPU quota from float to int.
|
|
func RoundQuotaFunc(rf func(v float64) int) Option {
|
|
return optionFunc(func(cfg *config) {
|
|
cfg.roundQuotaFunc = rf
|
|
})
|
|
}
|
|
|
|
type optionFunc func(*config)
|
|
|
|
func (of optionFunc) apply(cfg *config) { of(cfg) }
|
|
|
|
// Set GOMAXPROCS to match the Linux container CPU quota (if any), returning
|
|
// any error encountered and an undo function.
|
|
//
|
|
// Set is a no-op on non-Linux systems and in Linux environments without a
|
|
// configured CPU quota.
|
|
func Set(opts ...Option) (func(), error) {
|
|
cfg := &config{
|
|
procs: iruntime.CPUQuotaToGOMAXPROCS,
|
|
roundQuotaFunc: iruntime.DefaultRoundFunc,
|
|
minGOMAXPROCS: 1,
|
|
}
|
|
for _, o := range opts {
|
|
o.apply(cfg)
|
|
}
|
|
|
|
undoNoop := func() {
|
|
cfg.log("maxprocs: No GOMAXPROCS change to reset")
|
|
}
|
|
|
|
// Honor the GOMAXPROCS environment variable if present. Otherwise, amend
|
|
// `runtime.GOMAXPROCS()` with the current process' CPU quota if the OS is
|
|
// Linux, and guarantee a minimum value of 1. The minimum guaranteed value
|
|
// can be overridden using `maxprocs.Min()`.
|
|
if max, exists := os.LookupEnv(_maxProcsKey); exists {
|
|
cfg.log("maxprocs: Honoring GOMAXPROCS=%q as set in environment", max)
|
|
return undoNoop, nil
|
|
}
|
|
|
|
maxProcs, status, err := cfg.procs(cfg.minGOMAXPROCS, cfg.roundQuotaFunc)
|
|
if err != nil {
|
|
return undoNoop, err
|
|
}
|
|
|
|
if status == iruntime.CPUQuotaUndefined {
|
|
cfg.log("maxprocs: Leaving GOMAXPROCS=%v: CPU quota undefined", currentMaxProcs())
|
|
return undoNoop, nil
|
|
}
|
|
|
|
prev := currentMaxProcs()
|
|
undo := func() {
|
|
cfg.log("maxprocs: Resetting GOMAXPROCS to %v", prev)
|
|
runtime.GOMAXPROCS(prev)
|
|
}
|
|
|
|
switch status {
|
|
case iruntime.CPUQuotaMinUsed:
|
|
cfg.log("maxprocs: Updating GOMAXPROCS=%v: using minimum allowed GOMAXPROCS", maxProcs)
|
|
case iruntime.CPUQuotaUsed:
|
|
cfg.log("maxprocs: Updating GOMAXPROCS=%v: determined from CPU quota", maxProcs)
|
|
}
|
|
|
|
runtime.GOMAXPROCS(maxProcs)
|
|
return undo, nil
|
|
}
|