Skip to content

feat: add getCPU() with cgroup limit support#38

Merged
loks0n merged 8 commits into
mainfrom
feat/cpu-lmits
May 5, 2026
Merged

feat: add getCPU() with cgroup limit support#38
loks0n merged 8 commits into
mainfrom
feat/cpu-lmits

Conversation

@loks0n
Copy link
Copy Markdown
Contributor

@loks0n loks0n commented May 5, 2026

Summary

  • Adds System::getCPU(): float, returning CPU capacity that honours cgroup v2 (cpu.max) and cgroup v1 (cpu.cfs_quota_us / cpu.cfs_period_us) — so Docker/Kubernetes limits like --cpus=0.5 or limits.cpu: 500m are reflected as 0.5.
  • Also honours cpuset pinning (--cpuset-cpus=0-3); when both a cfs quota and a cpuset are configured, the more restrictive value is returned.
  • Falls back to the host's physical core count when no cgroup limit is set, and uses the existing sysctl / wmic paths on Darwin / Windows.
  • Marks getCPUCores() as @deprecated in favour of the new method.

Test plan

  • php -l clean on src/System/System.php and tests/System/SystemTest.php
  • Added testGetCPU asserting float and > 0
  • Run full PHPUnit suite in CI
  • Manual verification inside a container with --cpus=0.5 returns 0.5
  • Manual verification with --cpuset-cpus=0-1 returns 2.0

Returns CPU capacity as a float, honouring cgroup v2/v1 cfs quota and
cpuset pinning so Docker/Kubernetes CPU limits are reflected (e.g. a
500m limit returns 0.5). When both quota and cpuset are set, the more
restrictive value wins. Falls back to the host core count when no limit
is configured.

Marks getCPUCores() as deprecated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 5, 2026

Greptile Summary

  • Introduces getCPU(): float that correctly honours cgroup v2 (cpu.max) and cgroup v1 (cpu.cfs_quota_us/cpu.cfs_period_us) quotas, cpuset pinning, and falls back to /proc/cpuinfo on Linux; Darwin and Windows use sysctl/wmic with proper error handling.
  • All five issues raised in the prior review cycle have been addressed: Windows multi-socket sum uses preg_match_all + array_sum, unreadable /proc/cpuinfo now throws, Darwin/Windows both throw on failure, and the cgroup v2 cpuset.cpus.effective always-present false-positive is neutralised by comparing against /sys/devices/system/cpu/online.
  • getCPUCores() is marked @deprecated in favour of the new method.

Confidence Score: 5/5

This PR is safe to merge; no new P1 or P0 issues were found and all previously flagged concerns have been resolved.

Every issue from the prior review thread has a concrete fix in the diff. The cgroup parsing logic handles edge cases (max keyword, zero-period guard, unrestricted cpuset detection). No new bugs were identified.

No files require special attention.

Important Files Changed

Filename Overview
src/System/System.php Adds getCPU(): float with cgroup v2/v1 quota support, cpuset detection (with unrestricted guard), private helper methods, and deprecates getCPUCores(). All five issues from the prior review thread have been addressed.
tests/System/SystemTest.php Adds testGetCPU() asserting the return is a float greater than 0, consistent with the existing testGetCPUCores() pattern.

Reviews (5): Last reviewed commit: "fix: guard against inverted ranges in co..." | Re-trigger Greptile

Comment thread src/System/System.php Outdated
Comment thread src/System/System.php Outdated
Comment thread src/System/System.php
loks0n and others added 3 commits May 5, 2026 13:15
wmic prints a header line before the numeric value, so intval() on the
raw output returns 0. Extract the first numeric token instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously, if /proc/cpuinfo was unreadable and no cgroup limit applied,
getCPU() would silently return 0.0. Throw a descriptive exception in
that case so callers don't get a value that violates the contract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cgroup v2 always exposes cpuset.cpus.effective listing every CPU even
when no user-visible cpuset restriction is configured. Compare the set
against /sys/devices/system/cpu/online and return null when it covers
all of them, so the v1 fallback remains reachable and "no restriction"
is no longer misrepresented as "cpuset = all CPUs".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread src/System/System.php Outdated
loks0n and others added 2 commits May 5, 2026 13:19
Both branches previously fell through to 0.0 when shell_exec was
disabled or returned no numeric output, breaking the >0 contract that
the Linux branch and tests rely on. Throw a descriptive exception
instead, matching the Linux behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
shell_exec can return string|false|null; use is_string() before passing
to preg_match to satisfy static analysis.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread src/System/System.php Outdated
loks0n and others added 2 commits May 5, 2026 13:24
wmic emits one NumberOfCores row per socket, so preg_match captured
only the first and undercounted multi-socket systems. Use preg_match_all
and sum the values.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
An input like "3-1" previously produced a negative increment; require
start <= end before counting.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@loks0n loks0n merged commit 04229a8 into main May 5, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants