Commit 3b1c8c9
authored
feat: replace Linux FFI with pure Dart cgroup implementation (#15)
* refactor: remove unused dylib.dart and fix incorrect comment
- 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
* fix: improve native library compatibility with older glibc
- 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).
* feat: add pure Dart implementation for Linux using cgroups
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.
* refactor: hybrid implementation - pure Dart for Linux, FFI for macOS
- 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.
* test: update tests for hybrid implementation
- 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
* docs(ci): simplify CI for hybrid implementation
- 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
* chore: rebuild macOS native binaries
* refactor: flatten OS/cgroup dispatch to single DetectedPlatform enum
- 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
* chore: rebuild macOS native binaries
* ci: merge binary rebuild into CI workflow
* chore: rebuild macOS native binaries
* fix: resolve process's actual cgroup path for memory metrics on native 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
* chore: rebuild macOS native binaries
* fix: unify CPU load normalization to respect SYSRES_CPU_CORES env var
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.
* chore: rebuild macOS native binaries
* refactor: rename files and classes from implementation to domain names
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.
* chore: rebuild macOS native binaries
* fix: upsert macOS binaries PR comment instead of creating duplicates
* chore: bump version to 2.2.0 and update changelog/readme
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.
* fix(ci): filter artifact download to exclude Docker cache blobs
* chore: rebuild macOS native binaries
---------
Co-authored-by: thiagoalmeidasa <1412716+thiagoalmeidasa@users.noreply.github.com>1 parent b37a668 commit 3b1c8c9
30 files changed
Lines changed: 1796 additions & 977 deletions
File tree
- .github/workflows
- ci
- doc
- example
- lib
- build
- src
- test
This file was deleted.
0 commit comments