From 1eacb1386f67445404da66b68ab34a602af7a00f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Fri, 13 Feb 2026 20:54:29 -0500 Subject: [PATCH] unix: check RLIMIT_AS and RLIMIT_DATA in uv_get_constrained_memory (#4899) Add uv__get_rlimit_max_memory() helper that checks RLIMIT_AS and RLIMIT_DATA resource limits across all Unix platforms. This helper returns the minimum of these rlimits if set. Update all Unix platform implementations of uv_get_constrained_memory() to use this helper, ensuring consistent handling of resource limits: - Linux: combine cgroup limits with rlimits - z/OS: combine RLIMIT_MEMLIMIT with rlimits - Other Unix platforms: use rlimits (previously returned 0) This provides a more accurate view of available memory when processes have rlimit constraints in addition to platform-specific limits. Related: https://github.com/JuliaLang/julia/pull/51656 Co-Authored-By: Claude --- src/unix/aix.c | 2 +- src/unix/core.c | 24 ++++++++++++++++++++++++ src/unix/cygwin.c | 2 +- src/unix/darwin.c | 2 +- src/unix/freebsd.c | 2 +- src/unix/haiku.c | 2 +- src/unix/hurd.c | 2 +- src/unix/ibmi.c | 2 +- src/unix/internal.h | 1 + src/unix/linux.c | 15 ++++++++++++--- src/unix/netbsd.c | 2 +- src/unix/openbsd.c | 2 +- src/unix/os390.c | 17 ++++++++++++++--- src/unix/qnx.c | 2 +- src/unix/sunos.c | 2 +- 15 files changed, 62 insertions(+), 17 deletions(-) diff --git a/src/unix/aix.c b/src/unix/aix.c index 30a2a894f..4c0f4adb4 100644 --- a/src/unix/aix.c +++ b/src/unix/aix.c @@ -380,7 +380,7 @@ uint64_t uv_get_total_memory(void) { uint64_t uv_get_constrained_memory(void) { - return 0; /* Memory constraints are unknown. */ + return uv__get_rlimit_max_memory(); } diff --git a/src/unix/core.c b/src/unix/core.c index 5d6160cb1..73027cbc5 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -2182,3 +2182,27 @@ int uv__sock_reuseport(int fd) { return 0; } + +/* Check if rlimit has any expressed restrictions on usable memory. */ +uint64_t uv__get_rlimit_max_memory(void) { + struct rlimit rl; + uint64_t result = 0; + uint64_t rlimit_value; + +#if defined(RLIMIT_AS) + if (getrlimit(RLIMIT_AS, &rl) == 0 && rl.rlim_cur != RLIM_INFINITY) { + rlimit_value = rl.rlim_cur; + result = rlimit_value; + } +#endif + +#if defined(RLIMIT_DATA) + if (getrlimit(RLIMIT_DATA, &rl) == 0 && rl.rlim_cur != RLIM_INFINITY) { + rlimit_value = rl.rlim_cur; + if (result == 0 || rlimit_value < result) + result = rlimit_value; + } +#endif + + return result; +} diff --git a/src/unix/cygwin.c b/src/unix/cygwin.c index 491310822..29861ce8b 100644 --- a/src/unix/cygwin.c +++ b/src/unix/cygwin.c @@ -85,7 +85,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { } uint64_t uv_get_constrained_memory(void) { - return 0; /* Memory constraints are unknown. */ + return uv__get_rlimit_max_memory(); } uint64_t uv_get_available_memory(void) { diff --git a/src/unix/darwin.c b/src/unix/darwin.c index 98ddf2728..c26a1facc 100644 --- a/src/unix/darwin.c +++ b/src/unix/darwin.c @@ -121,7 +121,7 @@ uint64_t uv_get_total_memory(void) { uint64_t uv_get_constrained_memory(void) { - return 0; /* Memory constraints are unknown. */ + return uv__get_rlimit_max_memory(); } diff --git a/src/unix/freebsd.c b/src/unix/freebsd.c index a6de29c55..d7ac94b0c 100644 --- a/src/unix/freebsd.c +++ b/src/unix/freebsd.c @@ -117,7 +117,7 @@ uint64_t uv_get_total_memory(void) { uint64_t uv_get_constrained_memory(void) { - return 0; /* Memory constraints are unknown. */ + return uv__get_rlimit_max_memory(); } diff --git a/src/unix/haiku.c b/src/unix/haiku.c index 0d3645f01..22c165916 100644 --- a/src/unix/haiku.c +++ b/src/unix/haiku.c @@ -80,7 +80,7 @@ uint64_t uv_get_total_memory(void) { uint64_t uv_get_constrained_memory(void) { - return 0; /* Memory constraints are unknown. */ + return uv__get_rlimit_max_memory(); } diff --git a/src/unix/hurd.c b/src/unix/hurd.c index 63c878123..99f4f2ae4 100644 --- a/src/unix/hurd.c +++ b/src/unix/hurd.c @@ -163,7 +163,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { } uint64_t uv_get_constrained_memory(void) { - return 0; /* Memory constraints are unknown. */ + return uv__get_rlimit_max_memory(); } diff --git a/src/unix/ibmi.c b/src/unix/ibmi.c index f4f8748a8..a534dfc56 100644 --- a/src/unix/ibmi.c +++ b/src/unix/ibmi.c @@ -245,7 +245,7 @@ uint64_t uv_get_total_memory(void) { uint64_t uv_get_constrained_memory(void) { - return 0; /* Memory constraints are unknown. */ + return uv__get_rlimit_max_memory(); } diff --git a/src/unix/internal.h b/src/unix/internal.h index 6359694c2..360e425a5 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -379,6 +379,7 @@ int uv__signal_loop_fork(uv_loop_t* loop); /* platform specific */ uint64_t uv__hrtime(uv_clocktype_t type); +uint64_t uv__get_rlimit_max_memory(void); int uv__kqueue_init(uv_loop_t* loop); int uv__platform_loop_init(uv_loop_t* loop); void uv__platform_loop_delete(uv_loop_t* loop); diff --git a/src/unix/linux.c b/src/unix/linux.c index 60551cbf7..4f0b7049b 100644 --- a/src/unix/linux.c +++ b/src/unix/linux.c @@ -2201,11 +2201,20 @@ static uint64_t uv__get_cgroup_constrained_memory(char buf[static 1024]) { uint64_t uv_get_constrained_memory(void) { char buf[1024]; + uint64_t cgroup_limit; + uint64_t rlimit_limit; - if (uv__slurp("/proc/self/cgroup", buf, sizeof(buf))) - return 0; + cgroup_limit = 0; + if (uv__slurp("/proc/self/cgroup", buf, sizeof(buf)) == 0) + cgroup_limit = uv__get_cgroup_constrained_memory(buf); + rlimit_limit = uv__get_rlimit_max_memory(); - return uv__get_cgroup_constrained_memory(buf); + /* Return the minimum of cgroup and rlimit constraints. */ + if (cgroup_limit == 0) + return rlimit_limit; + if (rlimit_limit == 0) + return cgroup_limit; + return cgroup_limit < rlimit_limit ? cgroup_limit : rlimit_limit; } diff --git a/src/unix/netbsd.c b/src/unix/netbsd.c index fa21e98e4..37d962c4b 100644 --- a/src/unix/netbsd.c +++ b/src/unix/netbsd.c @@ -127,7 +127,7 @@ uint64_t uv_get_total_memory(void) { uint64_t uv_get_constrained_memory(void) { - return 0; /* Memory constraints are unknown. */ + return uv__get_rlimit_max_memory(); } diff --git a/src/unix/openbsd.c b/src/unix/openbsd.c index a6d58b948..bc6631695 100644 --- a/src/unix/openbsd.c +++ b/src/unix/openbsd.c @@ -94,7 +94,7 @@ uint64_t uv_get_total_memory(void) { uint64_t uv_get_constrained_memory(void) { - return 0; /* Memory constraints are unknown. */ + return uv__get_rlimit_max_memory(); } diff --git a/src/unix/os390.c b/src/unix/os390.c index c049884c4..6ba2f9158 100644 --- a/src/unix/os390.c +++ b/src/unix/os390.c @@ -188,12 +188,23 @@ uint64_t uv_get_total_memory(void) { uint64_t uv_get_constrained_memory(void) { struct rlimit rl; + uint64_t memlimit; + uint64_t rlimit_limit; /* RLIMIT_MEMLIMIT return value is in megabytes rather than bytes. */ - if (getrlimit(RLIMIT_MEMLIMIT, &rl) == 0) - return rl.rlim_cur * 1024 * 1024; + memlimit = 0; + if (getrlimit(RLIMIT_MEMLIMIT, &rl) == 0 && rl.rlim_cur != RLIM_INFINITY) + memlimit = rl.rlim_cur * 1024 * 1024; - return 0; /* There is no memory limit set. */ + /* Also check RLIMIT_AS and RLIMIT_DATA. */ + rlimit_limit = uv__get_rlimit_max_memory(); + + /* Return the minimum of RLIMIT_MEMLIMIT and other rlimits. */ + if (memlimit == 0) + return rlimit_limit; + if (rlimit_limit == 0) + return memlimit; + return memlimit < rlimit_limit ? memlimit : rlimit_limit; } diff --git a/src/unix/qnx.c b/src/unix/qnx.c index d873165fe..c1030caf7 100644 --- a/src/unix/qnx.c +++ b/src/unix/qnx.c @@ -118,7 +118,7 @@ uint64_t uv_get_total_memory(void) { uint64_t uv_get_constrained_memory(void) { - return 0; + return uv__get_rlimit_max_memory(); } diff --git a/src/unix/sunos.c b/src/unix/sunos.c index f3fbca390..c241e3506 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -397,7 +397,7 @@ uint64_t uv_get_total_memory(void) { uint64_t uv_get_constrained_memory(void) { - return 0; /* Memory constraints are unknown. */ + return uv__get_rlimit_max_memory(); }