Skip to content

Commit 44b9737

Browse files
nixprimegvisor-bot
authored andcommitted
Increase GOMAXPROCS during aio.GoQueue usage
PiperOrigin-RevId: 735048540
1 parent 8153170 commit 44b9737

File tree

7 files changed

+208
-1
lines changed

7 files changed

+208
-1
lines changed

pkg/aio/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ go_library(
1313
],
1414
visibility = ["//pkg/sentry:internal"],
1515
deps = [
16+
"//pkg/gomaxprocs",
1617
"//pkg/sync",
1718
"@org_golang_x_sys//unix:go_default_library",
1819
],

pkg/aio/aio.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"fmt"
2020

2121
"golang.org/x/sys/unix"
22+
"gvisor.dev/gvisor/pkg/gomaxprocs"
2223
"gvisor.dev/gvisor/pkg/sync"
2324
)
2425

@@ -113,6 +114,7 @@ func NewGoQueue(cap int) *GoQueue {
113114
for range cap {
114115
go q.workerMain()
115116
}
117+
gomaxprocs.Add(cap)
116118
return q
117119
}
118120

@@ -153,6 +155,7 @@ func (q *GoQueue) workerMain() {
153155
func (q *GoQueue) Destroy() {
154156
close(q.shutdown)
155157
q.workers.Wait()
158+
gomaxprocs.Add(-q.Cap())
156159
}
157160

158161
// Cap implements Queue.Cap.

pkg/gomaxprocs/BUILD

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
load("//pkg/sync/locking:locking.bzl", "declare_mutex")
2+
load("//tools:defs.bzl", "go_library", "go_test")
3+
4+
package(
5+
default_applicable_licenses = ["//:license"],
6+
licenses = ["notice"],
7+
)
8+
9+
declare_mutex(
10+
name = "gomaxprocs_mutex",
11+
out = "gomaxprocs_mutex.go",
12+
package = "gomaxprocs",
13+
prefix = "gomaxprocs",
14+
)
15+
16+
go_library(
17+
name = "gomaxprocs",
18+
srcs = [
19+
"gomaxprocs.go",
20+
"gomaxprocs_mutex.go",
21+
],
22+
visibility = ["//:sandbox"],
23+
deps = [
24+
"//pkg/log",
25+
"//pkg/sync",
26+
"//pkg/sync/locking",
27+
],
28+
)
29+
30+
go_test(
31+
name = "gomaxprocs_test",
32+
size = "small",
33+
srcs = ["gomaxprocs_test.go"],
34+
library = ":gomaxprocs",
35+
)

pkg/gomaxprocs/gomaxprocs.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright 2025 The gVisor Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package gomaxprocs synchronizes adjustments to GOMAXPROCS. When this package
16+
// is active (i.e. after the first call to SetBase), it sets the value of
17+
// GOMAXPROCS to a "base" value (which should be set by a single goroutine,
18+
// without races) plus a non-negative "temporary" value (which may be
19+
// concurrently increased or decreased by multiple goroutines).
20+
//
21+
// Note that changing GOMAXPROCS stops the world, so callers should adjust
22+
// GOMAXPROCS infrequently.
23+
//
24+
// TODO: Add gomaxprocs.Get() and check that other gVisor packages don't call
25+
// runtime.GOMAXPROCS() at all.
26+
package gomaxprocs
27+
28+
import (
29+
"runtime"
30+
31+
"gvisor.dev/gvisor/pkg/log"
32+
)
33+
34+
var (
35+
mu gomaxprocsMutex
36+
// +checklocks:mu
37+
base int
38+
// +checklocks:mu
39+
temp int
40+
)
41+
42+
// SetBase sets base GOMAXPROCS.
43+
func SetBase(n int) {
44+
if n < 1 {
45+
log.Traceback("Invalid base GOMAXPROCS: %d", n)
46+
return
47+
}
48+
mu.Lock()
49+
defer mu.Unlock()
50+
oldBase := base
51+
base = n
52+
updateRuntime(oldBase, temp)
53+
}
54+
55+
// Add adds n temporary GOMAXPROCS. n may be negative; callers should call Add
56+
// with negative n to remove temporary GOMAXPROCS when they are no longer
57+
// needed.
58+
func Add(n int) {
59+
mu.Lock()
60+
defer mu.Unlock()
61+
t := temp + n
62+
if t < 0 {
63+
log.Traceback("gomaxprocs.Add(%d) would cause temp to become %d", n, t)
64+
return
65+
}
66+
oldTemp := temp
67+
temp = t
68+
if base != 0 {
69+
updateRuntime(base, oldTemp)
70+
}
71+
}
72+
73+
// +checklocks:mu
74+
func updateRuntime(oldBase, oldTemp int) {
75+
n := base + temp
76+
log.Debugf("Setting GOMAXPROCS to %d", n)
77+
got := runtime.GOMAXPROCS(n)
78+
if want := oldBase + oldTemp; oldBase != 0 && got != want {
79+
// Something changed GOMAXPROCS outside of our control.
80+
log.Warningf("Previous GOMAXPROCS was %d, expected %d = %d + %d", got, want, oldBase, oldTemp)
81+
}
82+
}

pkg/gomaxprocs/gomaxprocs_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2025 The gVisor Authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package gomaxprocs
16+
17+
import (
18+
"runtime"
19+
"testing"
20+
)
21+
22+
// reset cancels the effect of all previous calls to SetBase and Add and sets
23+
// GOMAXPROCS to the given value.
24+
func reset(n int) {
25+
mu.Lock()
26+
defer mu.Unlock()
27+
base = 0
28+
temp = 0
29+
runtime.GOMAXPROCS(n)
30+
}
31+
32+
func TestBasic(t *testing.T) {
33+
init := runtime.GOMAXPROCS(0)
34+
defer reset(init)
35+
36+
firstBase := init + 1
37+
SetBase(firstBase)
38+
if got, want := runtime.GOMAXPROCS(0), firstBase; got != want {
39+
t.Errorf("GOMAXPROCS after first SetBase(%d): got %d, want %d", firstBase, got, want)
40+
}
41+
Add(1)
42+
if got, want := runtime.GOMAXPROCS(0), firstBase+1; got != want {
43+
t.Errorf("GOMAXPROCS after first Add(1): got %d, want %d", got, want)
44+
}
45+
SetBase(firstBase + 1)
46+
if got, want := runtime.GOMAXPROCS(0), firstBase+2; got != want {
47+
t.Errorf("GOMAXPROCS after SetBase(%d): got %d, want %d", firstBase+1, got, want)
48+
}
49+
Add(1)
50+
if got, want := runtime.GOMAXPROCS(0), firstBase+3; got != want {
51+
t.Errorf("GOMAXPROCS after second Add(1): got %d, want %d", got, want)
52+
}
53+
SetBase(firstBase)
54+
if got, want := runtime.GOMAXPROCS(0), firstBase+2; got != want {
55+
t.Errorf("GOMAXPROCS after second SetBase(%d): got %d, want %d", firstBase, got, want)
56+
}
57+
Add(-2)
58+
if got, want := runtime.GOMAXPROCS(0), firstBase; got != want {
59+
t.Errorf("GOMAXPROCS after Add(-2): got %d, want %d", got, want)
60+
}
61+
}
62+
63+
func TestAddIgnoredUntilSetBase(t *testing.T) {
64+
init := runtime.GOMAXPROCS(0)
65+
defer reset(init)
66+
67+
Add(2)
68+
if got, want := runtime.GOMAXPROCS(0), init; got != want {
69+
t.Errorf("GOMAXPROCS after Add(2): got %d, want %d", got, want)
70+
}
71+
Add(1)
72+
if got, want := runtime.GOMAXPROCS(0), init; got != want {
73+
t.Errorf("GOMAXPROCS after Add(1): got %d, want %d", got, want)
74+
}
75+
newBase := init + 1
76+
SetBase(newBase)
77+
if got, want := runtime.GOMAXPROCS(0), newBase+3; got != want {
78+
t.Errorf("GOMAXPROCS after SetBase(%d): got %d, want %d", newBase, got, want)
79+
}
80+
Add(-3)
81+
if got, want := runtime.GOMAXPROCS(0), newBase; got != want {
82+
t.Errorf("GOMAXPROCS after Add(-3): got %d, want %d", got, want)
83+
}
84+
}

runsc/boot/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ go_library(
4747
"//pkg/fd",
4848
"//pkg/flipcall",
4949
"//pkg/fspath",
50+
"//pkg/gomaxprocs",
5051
"//pkg/hostos",
5152
"//pkg/log",
5253
"//pkg/memutil",

runsc/boot/loader.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"gvisor.dev/gvisor/pkg/coverage"
3535
"gvisor.dev/gvisor/pkg/cpuid"
3636
"gvisor.dev/gvisor/pkg/fd"
37+
"gvisor.dev/gvisor/pkg/gomaxprocs"
3738
"gvisor.dev/gvisor/pkg/log"
3839
"gvisor.dev/gvisor/pkg/memutil"
3940
"gvisor.dev/gvisor/pkg/metric"
@@ -560,7 +561,7 @@ func New(args Args) (*Loader, error) {
560561
args.NumCPU = runtime.NumCPU()
561562
}
562563
log.Infof("CPUs: %d", args.NumCPU)
563-
runtime.GOMAXPROCS(args.NumCPU)
564+
gomaxprocs.SetBase(args.NumCPU)
564565

565566
if args.TotalHostMem > 0 {
566567
// As per tmpfs(5), the default size limit is 50% of total physical RAM.

0 commit comments

Comments
 (0)