Skip to content

Commit a69113a

Browse files
committed
inline automaxprocs to simplify dependencies; this will be removed when Go 1.26 comes out
1 parent a99c6e0 commit a69113a

File tree

14 files changed

+919
-6
lines changed

14 files changed

+919
-6
lines changed

ginkgo/maxprocs.go renamed to ginkgo/automaxprocs.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
package main
55

66
import (
7-
_ "go.uber.org/automaxprocs"
7+
_ "github.com/onsi/ginkgo/v2/ginkgo/automaxprocs"
88
)

ginkgo/automaxprocs/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This entire directory is a lightly modified clone of https://github.com/uber-go/automaxprocs
2+
3+
It will be removed when Go 1.26 ships and we no longer need to support Go 1.24 (which does not correctly autodetect maxprocs in containers).
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright (c) 2017 Uber Technologies, Inc.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
// Package maxprocs lets Go programs easily configure runtime.GOMAXPROCS to
22+
// match the configured Linux CPU quota. Unlike the top-level automaxprocs
23+
// package, it lets the caller configure logging and handle errors.
24+
package automaxprocs
25+
26+
import (
27+
"os"
28+
"runtime"
29+
)
30+
31+
func init() {
32+
Set()
33+
}
34+
35+
const _maxProcsKey = "GOMAXPROCS"
36+
37+
type config struct {
38+
procs func(int, func(v float64) int) (int, CPUQuotaStatus, error)
39+
minGOMAXPROCS int
40+
roundQuotaFunc func(v float64) int
41+
}
42+
43+
// Set GOMAXPROCS to match the Linux container CPU quota (if any), returning
44+
// any error encountered and an undo function.
45+
//
46+
// Set is a no-op on non-Linux systems and in Linux environments without a
47+
// configured CPU quota.
48+
func Set() error {
49+
cfg := &config{
50+
procs: CPUQuotaToGOMAXPROCS,
51+
roundQuotaFunc: DefaultRoundFunc,
52+
minGOMAXPROCS: 1,
53+
}
54+
55+
// Honor the GOMAXPROCS environment variable if present. Otherwise, amend
56+
// `runtime.GOMAXPROCS()` with the current process' CPU quota if the OS is
57+
// Linux, and guarantee a minimum value of 1. The minimum guaranteed value
58+
// can be overridden using `maxprocs.Min()`.
59+
if _, exists := os.LookupEnv(_maxProcsKey); exists {
60+
return nil
61+
}
62+
maxProcs, status, err := cfg.procs(cfg.minGOMAXPROCS, cfg.roundQuotaFunc)
63+
if err != nil {
64+
return err
65+
}
66+
if status == CPUQuotaUndefined {
67+
return nil
68+
}
69+
runtime.GOMAXPROCS(maxProcs)
70+
return nil
71+
}

ginkgo/automaxprocs/cgroup.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright (c) 2017 Uber Technologies, Inc.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
//go:build linux
22+
// +build linux
23+
24+
package automaxprocs
25+
26+
import (
27+
"bufio"
28+
"io"
29+
"os"
30+
"path/filepath"
31+
"strconv"
32+
)
33+
34+
// CGroup represents the data structure for a Linux control group.
35+
type CGroup struct {
36+
path string
37+
}
38+
39+
// NewCGroup returns a new *CGroup from a given path.
40+
func NewCGroup(path string) *CGroup {
41+
return &CGroup{path: path}
42+
}
43+
44+
// Path returns the path of the CGroup*.
45+
func (cg *CGroup) Path() string {
46+
return cg.path
47+
}
48+
49+
// ParamPath returns the path of the given cgroup param under itself.
50+
func (cg *CGroup) ParamPath(param string) string {
51+
return filepath.Join(cg.path, param)
52+
}
53+
54+
// readFirstLine reads the first line from a cgroup param file.
55+
func (cg *CGroup) readFirstLine(param string) (string, error) {
56+
paramFile, err := os.Open(cg.ParamPath(param))
57+
if err != nil {
58+
return "", err
59+
}
60+
defer paramFile.Close()
61+
62+
scanner := bufio.NewScanner(paramFile)
63+
if scanner.Scan() {
64+
return scanner.Text(), nil
65+
}
66+
if err := scanner.Err(); err != nil {
67+
return "", err
68+
}
69+
return "", io.ErrUnexpectedEOF
70+
}
71+
72+
// readInt parses the first line from a cgroup param file as int.
73+
func (cg *CGroup) readInt(param string) (int, error) {
74+
text, err := cg.readFirstLine(param)
75+
if err != nil {
76+
return 0, err
77+
}
78+
return strconv.Atoi(text)
79+
}

ginkgo/automaxprocs/cgroups.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright (c) 2017 Uber Technologies, Inc.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
//go:build linux
22+
// +build linux
23+
24+
package automaxprocs
25+
26+
const (
27+
// _cgroupFSType is the Linux CGroup file system type used in
28+
// `/proc/$PID/mountinfo`.
29+
_cgroupFSType = "cgroup"
30+
// _cgroupSubsysCPU is the CPU CGroup subsystem.
31+
_cgroupSubsysCPU = "cpu"
32+
// _cgroupSubsysCPUAcct is the CPU accounting CGroup subsystem.
33+
_cgroupSubsysCPUAcct = "cpuacct"
34+
// _cgroupSubsysCPUSet is the CPUSet CGroup subsystem.
35+
_cgroupSubsysCPUSet = "cpuset"
36+
// _cgroupSubsysMemory is the Memory CGroup subsystem.
37+
_cgroupSubsysMemory = "memory"
38+
39+
// _cgroupCPUCFSQuotaUsParam is the file name for the CGroup CFS quota
40+
// parameter.
41+
_cgroupCPUCFSQuotaUsParam = "cpu.cfs_quota_us"
42+
// _cgroupCPUCFSPeriodUsParam is the file name for the CGroup CFS period
43+
// parameter.
44+
_cgroupCPUCFSPeriodUsParam = "cpu.cfs_period_us"
45+
)
46+
47+
const (
48+
_procPathCGroup = "/proc/self/cgroup"
49+
_procPathMountInfo = "/proc/self/mountinfo"
50+
)
51+
52+
// CGroups is a map that associates each CGroup with its subsystem name.
53+
type CGroups map[string]*CGroup
54+
55+
// NewCGroups returns a new *CGroups from given `mountinfo` and `cgroup` files
56+
// under for some process under `/proc` file system (see also proc(5) for more
57+
// information).
58+
func NewCGroups(procPathMountInfo, procPathCGroup string) (CGroups, error) {
59+
cgroupSubsystems, err := parseCGroupSubsystems(procPathCGroup)
60+
if err != nil {
61+
return nil, err
62+
}
63+
64+
cgroups := make(CGroups)
65+
newMountPoint := func(mp *MountPoint) error {
66+
if mp.FSType != _cgroupFSType {
67+
return nil
68+
}
69+
70+
for _, opt := range mp.SuperOptions {
71+
subsys, exists := cgroupSubsystems[opt]
72+
if !exists {
73+
continue
74+
}
75+
76+
cgroupPath, err := mp.Translate(subsys.Name)
77+
if err != nil {
78+
return err
79+
}
80+
cgroups[opt] = NewCGroup(cgroupPath)
81+
}
82+
83+
return nil
84+
}
85+
86+
if err := parseMountInfo(procPathMountInfo, newMountPoint); err != nil {
87+
return nil, err
88+
}
89+
return cgroups, nil
90+
}
91+
92+
// NewCGroupsForCurrentProcess returns a new *CGroups instance for the current
93+
// process.
94+
func NewCGroupsForCurrentProcess() (CGroups, error) {
95+
return NewCGroups(_procPathMountInfo, _procPathCGroup)
96+
}
97+
98+
// CPUQuota returns the CPU quota applied with the CPU cgroup controller.
99+
// It is a result of `cpu.cfs_quota_us / cpu.cfs_period_us`. If the value of
100+
// `cpu.cfs_quota_us` was not set (-1), the method returns `(-1, nil)`.
101+
func (cg CGroups) CPUQuota() (float64, bool, error) {
102+
cpuCGroup, exists := cg[_cgroupSubsysCPU]
103+
if !exists {
104+
return -1, false, nil
105+
}
106+
107+
cfsQuotaUs, err := cpuCGroup.readInt(_cgroupCPUCFSQuotaUsParam)
108+
if defined := cfsQuotaUs > 0; err != nil || !defined {
109+
return -1, defined, err
110+
}
111+
112+
cfsPeriodUs, err := cpuCGroup.readInt(_cgroupCPUCFSPeriodUsParam)
113+
if defined := cfsPeriodUs > 0; err != nil || !defined {
114+
return -1, defined, err
115+
}
116+
117+
return float64(cfsQuotaUs) / float64(cfsPeriodUs), true, nil
118+
}

0 commit comments

Comments
 (0)