-
Notifications
You must be signed in to change notification settings - Fork 164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a Reserved option #14
Changes from all commits
1c41cb6
c4a7e99
1dbf04e
e1129b9
f5cbd97
d0fd858
ff9bd29
82b4fde
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,20 +32,13 @@ import ( | |
|
||
const _maxProcsKey = "GOMAXPROCS" | ||
|
||
func currentMaxProcs() int { | ||
return runtime.GOMAXPROCS(0) | ||
} | ||
func noopLog(fmt string, args ...interface{}) {} | ||
|
||
type config struct { | ||
printf func(string, ...interface{}) | ||
procs func(int) (int, iruntime.CPUQuotaStatus, error) | ||
minGOMAXPROCS int | ||
} | ||
|
||
func (c *config) log(fmt string, args ...interface{}) { | ||
if c.printf != nil { | ||
c.printf(fmt, args...) | ||
} | ||
iruntime.CPUQuotaConfig | ||
log func(string, ...interface{}) | ||
quotaFunc iruntime.CPUQuotaFunc | ||
origValue int | ||
} | ||
|
||
// An Option alters the behavior of Set. | ||
|
@@ -57,7 +50,7 @@ type Option interface { | |
// Set doesn't log anything. | ||
func Logger(printf func(string, ...interface{})) Option { | ||
return optionFunc(func(cfg *config) { | ||
cfg.printf = printf | ||
cfg.log = printf | ||
}) | ||
} | ||
|
||
|
@@ -66,7 +59,23 @@ func Logger(printf func(string, ...interface{})) Option { | |
func Min(n int) Option { | ||
return optionFunc(func(cfg *config) { | ||
if n >= 1 { | ||
cfg.minGOMAXPROCS = n | ||
cfg.MinValue = n | ||
} | ||
}) | ||
} | ||
|
||
// ReservedCPUQuota specifies a CPU quota amount to consider reserved for use | ||
// outside of the Go runtime; e.g. by a cgo extension, or another process | ||
// within the container. | ||
// | ||
// GOMAXPROCS will be set to the remaining (rounded down) quota, clamped to the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is "rounded down" still the intention? This part of the comment contradicts the use of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes it is, this doc comment is out of line with the code reality above, good catch. |
||
// minimum limit (default 1). | ||
// | ||
// Any non-positive value is ignored. | ||
func ReservedCPUQuota(reserved float64) Option { | ||
return optionFunc(func(cfg *config) { | ||
if reserved >= 0 { | ||
cfg.Reserved = reserved | ||
} | ||
}) | ||
} | ||
|
@@ -75,46 +84,50 @@ type optionFunc func(*config) | |
|
||
func (of optionFunc) apply(cfg *config) { of(cfg) } | ||
|
||
func (c config) undoNoop() { | ||
c.log("maxprocs: No GOMAXPROCS change to reset") | ||
} | ||
|
||
func (c config) undo() { | ||
c.log("maxprocs: Resetting GOMAXPROCS to %d", c.origValue) | ||
runtime.GOMAXPROCS(c.origValue) | ||
} | ||
|
||
// 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, | ||
minGOMAXPROCS: 1, | ||
cfg := config{ | ||
log: noopLog, | ||
quotaFunc: iruntime.CPUQuotaToGOMAXPROCS, | ||
CPUQuotaConfig: iruntime.CPUQuotaConfig{ | ||
MinValue: 1, | ||
}, | ||
} | ||
for _, o := range opts { | ||
o.apply(cfg) | ||
} | ||
|
||
undoNoop := func() { | ||
cfg.log("maxprocs: No GOMAXPROCS change to reset") | ||
o.apply(&cfg) | ||
} | ||
|
||
// 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 2 to ensure efficiency. | ||
if max, exists := os.LookupEnv(_maxProcsKey); exists { | ||
cfg.log("maxprocs: Honoring GOMAXPROCS=%d as set in environment", max) | ||
return undoNoop, nil | ||
return cfg.undoNoop, nil | ||
} | ||
|
||
maxProcs, status, err := cfg.procs(cfg.minGOMAXPROCS) | ||
maxProcs, status, err := cfg.quotaFunc(cfg.CPUQuotaConfig) | ||
if err != nil { | ||
return undoNoop, err | ||
return cfg.undoNoop, err | ||
} | ||
|
||
if status == iruntime.CPUQuotaUndefined { | ||
cfg.log("maxprocs: Leaving GOMAXPROCS=%d: CPU quota undefined", currentMaxProcs()) | ||
return undoNoop, nil | ||
} | ||
cfg.origValue = runtime.GOMAXPROCS(0) | ||
|
||
prev := currentMaxProcs() | ||
undo := func() { | ||
cfg.log("maxprocs: Resetting GOMAXPROCS to %d", prev) | ||
runtime.GOMAXPROCS(prev) | ||
if status == iruntime.CPUQuotaUndefined { | ||
cfg.log("maxprocs: Leaving GOMAXPROCS=%d: CPU quota undefined", cfg.origValue) | ||
return cfg.undoNoop, nil | ||
} | ||
|
||
switch status { | ||
|
@@ -125,5 +138,5 @@ func Set(opts ...Option) (func(), error) { | |
} | ||
|
||
runtime.GOMAXPROCS(maxProcs) | ||
return undo, nil | ||
return cfg.undo, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want
Floor
here after all? If we useCeil
, we will not be respecting the full reservation for theReserved
quota. CanReserved
ever be fractional?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, we do want floor, this PR was just written orthogonally to #13.