| // Copyright 2025 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "fmt" |
| "os" |
| "runtime" |
| "strconv" |
| "time" |
| ) |
| |
| func init() { |
| register("PrintGOMAXPROCS", PrintGOMAXPROCS) |
| register("SetLimitThenDefaultGOMAXPROCS", SetLimitThenDefaultGOMAXPROCS) |
| register("UpdateGOMAXPROCS", UpdateGOMAXPROCS) |
| register("DontUpdateGOMAXPROCS", DontUpdateGOMAXPROCS) |
| } |
| |
| func PrintGOMAXPROCS() { |
| println(runtime.GOMAXPROCS(0)) |
| } |
| |
| func mustSetCPUMax(path string, quota int64) { |
| q := "max" |
| if quota >= 0 { |
| q = strconv.FormatInt(quota, 10) |
| } |
| buf := fmt.Sprintf("%s 100000", q) |
| if err := os.WriteFile(path, []byte(buf), 0); err != nil { |
| panic(fmt.Sprintf("error setting cpu.max: %v", err)) |
| } |
| } |
| |
| func mustParseInt64(s string) int64 { |
| v, err := strconv.ParseInt(s, 10, 64) |
| if err != nil { |
| panic(err) |
| } |
| return v |
| } |
| |
| // Inputs: |
| // GO_TEST_CPU_MAX_PATH: Path to cgroup v2 cpu.max file. |
| // GO_TEST_CPU_MAX_QUOTA: CPU quota to set. |
| func SetLimitThenDefaultGOMAXPROCS() { |
| path := os.Getenv("GO_TEST_CPU_MAX_PATH") |
| quota := mustParseInt64(os.Getenv("GO_TEST_CPU_MAX_QUOTA")) |
| |
| mustSetCPUMax(path, quota) |
| |
| runtime.SetDefaultGOMAXPROCS() |
| println(runtime.GOMAXPROCS(0)) |
| } |
| |
| // Wait for GOMAXPROCS to change from from to to. Times out after 10s. |
| func waitForMaxProcsChange(from, to int) { |
| start := time.Now() |
| for { |
| if time.Since(start) > 10*time.Second { |
| panic("no update for >10s") |
| } |
| |
| procs := runtime.GOMAXPROCS(0) |
| println("GOMAXPROCS:", procs) |
| if procs == to { |
| return |
| } |
| if procs != from { |
| panic(fmt.Sprintf("GOMAXPROCS change got %d want %d", procs, to)) |
| } |
| |
| time.Sleep(100*time.Millisecond) |
| } |
| } |
| |
| // Make sure that GOMAXPROCS does not change from curr. |
| // |
| // It is impossible to assert that it never changes, so this just makes sure it |
| // stays for 5s. |
| func mustNotChangeMaxProcs(curr int) { |
| start := time.Now() |
| for { |
| if time.Since(start) > 5*time.Second { |
| return |
| } |
| |
| procs := runtime.GOMAXPROCS(0) |
| println("GOMAXPROCS:", procs) |
| if procs != curr { |
| panic(fmt.Sprintf("GOMAXPROCS change got %d want %d", procs, curr)) |
| } |
| |
| time.Sleep(100*time.Millisecond) |
| } |
| } |
| |
| // Inputs: |
| // GO_TEST_CPU_MAX_PATH: Path to cgroup v2 cpu.max file. |
| func UpdateGOMAXPROCS() { |
| // We start with no limit. |
| |
| ncpu := runtime.NumCPU() |
| |
| procs := runtime.GOMAXPROCS(0) |
| println("GOMAXPROCS:", procs) |
| if procs != ncpu { |
| panic(fmt.Sprintf("GOMAXPROCS got %d want %d", procs, ncpu)) |
| } |
| |
| path := os.Getenv("GO_TEST_CPU_MAX_PATH") |
| |
| // Drop down to 3 CPU. |
| mustSetCPUMax(path, 300000) |
| waitForMaxProcsChange(ncpu, 3) |
| |
| // Drop even further. Now we hit the minimum GOMAXPROCS=2. |
| mustSetCPUMax(path, 100000) |
| waitForMaxProcsChange(3, 2) |
| |
| // Increase back up. |
| mustSetCPUMax(path, 300000) |
| waitForMaxProcsChange(2, 3) |
| |
| // Remove limit entirely. |
| mustSetCPUMax(path, -1) |
| waitForMaxProcsChange(3, ncpu) |
| |
| // Setting GOMAXPROCS explicitly disables updates. |
| runtime.GOMAXPROCS(3) |
| mustSetCPUMax(path, 200000) |
| mustNotChangeMaxProcs(3) |
| |
| // Re-enable updates. Change is immediately visible. |
| runtime.SetDefaultGOMAXPROCS() |
| procs = runtime.GOMAXPROCS(0) |
| println("GOMAXPROCS:", procs) |
| if procs != 2 { |
| panic(fmt.Sprintf("GOMAXPROCS got %d want %d", procs, 2)) |
| } |
| |
| // Setting GOMAXPROCS to itself also disables updates, despite not |
| // changing the value itself. |
| runtime.GOMAXPROCS(runtime.GOMAXPROCS(0)) |
| mustSetCPUMax(path, 300000) |
| mustNotChangeMaxProcs(2) |
| |
| println("OK") |
| } |
| |
| // Inputs: |
| // GO_TEST_CPU_MAX_PATH: Path to cgroup v2 cpu.max file. |
| func DontUpdateGOMAXPROCS() { |
| // The caller has disabled updates. Make sure they don't happen. |
| |
| curr := runtime.GOMAXPROCS(0) |
| println("GOMAXPROCS:", curr) |
| |
| path := os.Getenv("GO_TEST_CPU_MAX_PATH") |
| mustSetCPUMax(path, 300000) |
| mustNotChangeMaxProcs(curr) |
| |
| println("OK") |
| } |