feat: replace Linux FFI with pure Dart cgroup implementation#15
Merged
thiagoalmeidasa merged 21 commits intomainfrom Feb 6, 2026
Merged
feat: replace Linux FFI with pure Dart cgroup implementation#15thiagoalmeidasa merged 21 commits intomainfrom
thiagoalmeidasa merged 21 commits intomainfrom
Conversation
- Delete lib/src/dylib.dart: unused code that duplicated library loading logic from base.dart - Fix comment in base.dart that incorrectly claimed File.existsSync() fails in distroless containers (testing proved this is false) - Update comment to reflect actual reason for direct DynamicLibrary.open(): single operation and TOCTOU race condition avoidance
- Build with gcc:10 to require only glibc 2.7 (was 2.38) - Add glibc version detection with concise error diagnostics - Test compatibility on dart:3.8, 3.9, 3.10, stable - Run compat tests after binary rebuild to use fresh binaries - Document glibc 2.7+ requirement (Ubuntu 14.04+, CentOS 7+) Tested on glibc 2.17 (CentOS 7) through 2.41 (dart:stable).
Implement system resource monitoring without FFI on Linux: - cgroup_detector.dart: Detect cgroup v1/v2 and container environment - cgroup_cpu.dart: CPU monitoring via cgroup accounting + /proc/loadavg fallback - cgroup_memory.dart: Memory monitoring via cgroups + /proc/meminfo fallback - system_resources.dart: Public API with Serverpod-compatible methods This approach reads cgroup files directly, eliminating the need for native libraries on Linux and enabling gVisor compatibility.
- Add macos_ffi.dart with FFI bindings for macOS only - Remove base.dart (replaced by hybrid system_resources.dart) - Remove Linux native libraries (.so files) - no longer needed - Keep macOS dylibs for FFI support Linux now uses pure Dart file I/O with no glibc dependencies.
- Add setUpAll() to call init() for macOS FFI - Update expectations to handle both Linux and macOS - Update example to show cgroup version and new API methods
- Remove Linux native builds from build-binaries.yml (macOS only) - Simplify ci.yml: Linux tests no longer need 'make' - Update Dockerfile.test with multi-stage build and compiled executable - Update README with new architecture and platform support - Update GVISOR.md to reflect cgroup-based CPU monitoring
- Add DetectedPlatform enum (macOS, linuxCgroupV2, linuxCgroupV1, linuxHost, unsupported) and cached detectPlatform() to CgroupDetector - Make cgroup v1/v2/proc reader methods public in CgroupCpu and CgroupMemory, removing their internal version dispatch - CgroupCpu delta methods now accept reader callbacks instead of calling detectVersion() internally - All SystemResources methods use a single switch expression for dispatch - Public API surface unchanged; all tests pass
…e Linux On native Linux hosts with systemd cgroup v2, memory.max and memory.current don't exist at the root cgroup (/sys/fs/cgroup/) — they live in the process's child cgroup (e.g. /sys/fs/cgroup/user.slice/ .../session.scope/). CPU files worked because cpu.stat exists at root. This caused memoryLimitBytes and memoryUsedBytes to return 0, failing CI on test-linux (amd64) and test-linux (arm64) while container tests passed (containers have their own cgroup namespace at root). Changes: - Parse /proc/self/cgroup to resolve the process's v2 cgroup directory, matching how JDK, .NET, and Go runtimes discover cgroup paths - Convert hardcoded v2 path constants to dynamic getters using the resolved directory - Fall back to /proc/meminfo when cgroup memory files are unreadable - Add docs/cgroup-path-resolution.md with research and rationale
getLoad() had its own fallback logic that skipped the SYSRES_CPU_CORES environment variable when the cgroup CPU limit was unavailable, falling back directly to Platform.numberOfProcessors. This caused cpuLoadAvg() to report ~half the actual CPU utilization in gVisor environments. Delegate limit resolution in getLoad() to getLimitCores(), which correctly handles the full fallback chain: cgroup limit, SYSRES_CPU_CORES env var, then Platform.numberOfProcessors.
Rename source files and classes to reflect domain rather than mechanism: - cgroup_cpu -> cpu_monitor (CgroupCpu -> CpuMonitor) - cgroup_memory -> memory_monitor (CgroupMemory -> MemoryMonitor) - cgroup_detector -> platform_detector (CgroupDetector -> PlatformDetector) - macos_ffi -> macos_native (MacOsFfi -> MacOsNative) Extract CpuMonitor unit tests into dedicated cpu_monitor_test.dart.
Add changelog entry for 2.2.0 covering pure Dart Linux support, cgroup v1 compatibility, new APIs, and bug fixes. Update README with gVisor CPU limit guidance using SYSRES_CPU_CORES env var.
macOS Binaries RebuiltThe macOS native binaries have been automatically rebuilt from source. MD5 ChecksumsThese checksums can be used to verify the provenance of the binaries. |
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
What's included
CpuMonitor(CPU accounting viacpu.stat/cpuacct.usage),MemoryMonitor(memory viamemory.current/memory.usage_in_bytes), with/proc/loadavgand/proc/meminfofallbacks for non-container Linux hostsPlatformDetectorwithDetectedPlatformenum (macOS,linuxCgroupV2,linuxCgroupV1,linuxHost,unsupported) for flat single-switch dispatch across allSystemResourcesmethods/proc/self/cgroupto resolve the process's actual cgroup v2 directory, fixing memory metrics on native Linux hosts wherememory.max/memory.currentdon't exist at rootMacOsNativeclass isolating all FFI bindings, loaded only when running on macOSgetLoad()now delegates togetLimitCores()for the full fallback chain (cgroup limit ->SYSRES_CPU_CORESenv var ->Platform.numberOfProcessors), fixing ~50% underreporting in gVisorcgroup_cpu->cpu_monitor,cgroup_memory->memory_monitor,cgroup_detector->platform_detector,macos_ffi->macos_native)makeor native compilationUsage
Test plan
dart testpasses on macOS (arm64 and Intel)dart testpasses on Linux (amd64 and arm64)--memoryand--cpuslimits (cgroup v2)CpuMonitorunit tests verify limit resolution fallback chain