From b88debbc91161c249114d752f3df99c0907c0464 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Mon, 19 Sep 2022 12:14:23 +0200 Subject: [PATCH 1/2] Fix the returned value of constrained memory getters. We should return 0 if there is no limit set. --- src/unix/linux-core.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index 24b61547d65..c567e75e955 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -794,13 +794,20 @@ static uint64_t uv__read_uint64(const char* filename) { * limit is unknown. */ static uint64_t uv__get_constrained_memory_fallback(void) { - return uv__read_uint64("/sys/fs/cgroup/memory/memory.limit_in_bytes"); + uint64_t limit; + + limit = uv__read_uint64("/sys/fs/cgroup/memory/memory.limit_in_bytes"); + if (limit == LONG_MAX / 4096 * 4096) + return 0; + + return limit; } uint64_t uv_get_constrained_memory(void) { char filename[4097]; char buf[1024]; + uint64_t limit; uint64_t high; uint64_t max; char* p; @@ -830,7 +837,10 @@ uint64_t uv_get_constrained_memory(void) { if (high == 0) return uv__get_constrained_memory_fallback(); - return high < max ? high : max; + limit = high < max ? high : max; + if (limit == ~0ull) + return 0; + return limit; } From e5928992b60d4642742d61d6257de09eaeb2e572 Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Mon, 19 Sep 2022 11:58:12 +0200 Subject: [PATCH 2/2] Add uv_get_available_memory to query free memory respecting constraints. --- docs/src/misc.rst | 16 +++++++++++++++- include/uv.h | 1 + src/unix/aix.c | 5 +++++ src/unix/cygwin.c | 4 ++++ src/unix/darwin.c | 5 +++++ src/unix/freebsd.c | 5 +++++ src/unix/haiku.c | 5 +++++ src/unix/hurd.c | 5 +++++ src/unix/ibmi.c | 5 +++++ src/unix/linux-core.c | 43 ++++++++++++++++++++++++++++++++++++++++++ src/unix/netbsd.c | 5 +++++ src/unix/openbsd.c | 5 +++++ src/unix/os390.c | 5 +++++ src/unix/qnx.c | 5 +++++ src/unix/sunos.c | 5 +++++ src/win/util.c | 5 +++++ test/test-get-memory.c | 7 +++++-- 17 files changed, 128 insertions(+), 3 deletions(-) diff --git a/docs/src/misc.rst b/docs/src/misc.rst index dd941677812..c0af97f49d5 100644 --- a/docs/src/misc.rst +++ b/docs/src/misc.rst @@ -562,7 +562,7 @@ API .. c:function:: uint64_t uv_get_constrained_memory(void) - Gets the amount of memory available to the process (in bytes) based on + Gets the total amount of memory available to the process (in bytes) based on limits imposed by the OS. If there is no such constraint, or the constraint is unknown, `0` is returned. Note that it is not unusual for this value to be less than or greater than :c:func:`uv_get_total_memory`. @@ -573,6 +573,20 @@ API .. versionadded:: 1.29.0 +.. c:function:: uint64_t uv_get_available_memory(void) + + Gets the amount of free memory that is still available to the process (in bytes). + This differs from :c:func:`uv_get_free_memory` in that it takes into account any + limits imposed by the OS. If there is no such constraint, or the constraint + is unknown, the amount returned will be identical to :c:func:`uv_get_free_memory`. + + .. note:: + This function currently only returns a value that is different from + what :c:func:`uv_get_free_memory` reports on Linux, based + on cgroups if it is present. + + .. versionadded:: 1.45.0 + .. c:function:: uint64_t uv_hrtime(void) Returns the current high-resolution real time. This is expressed in diff --git a/include/uv.h b/include/uv.h index e01cb6f3372..fb26fc63675 100644 --- a/include/uv.h +++ b/include/uv.h @@ -1793,6 +1793,7 @@ UV_EXTERN int uv_chdir(const char* dir); UV_EXTERN uint64_t uv_get_free_memory(void); UV_EXTERN uint64_t uv_get_total_memory(void); UV_EXTERN uint64_t uv_get_constrained_memory(void); +UV_EXTERN uint64_t uv_get_available_memory(void); UV_EXTERN uint64_t uv_hrtime(void); UV_EXTERN void uv_sleep(unsigned int msec); diff --git a/src/unix/aix.c b/src/unix/aix.c index 976e79817fc..8ed982494c2 100644 --- a/src/unix/aix.c +++ b/src/unix/aix.c @@ -382,6 +382,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { perfstat_cpu_total_t ps_total; int result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1); diff --git a/src/unix/cygwin.c b/src/unix/cygwin.c index 169958d55f2..4e5413963d6 100644 --- a/src/unix/cygwin.c +++ b/src/unix/cygwin.c @@ -51,3 +51,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. */ } + +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} diff --git a/src/unix/darwin.c b/src/unix/darwin.c index 7d2cacb60da..135cd61d11b 100644 --- a/src/unix/darwin.c +++ b/src/unix/darwin.c @@ -131,6 +131,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); diff --git a/src/unix/freebsd.c b/src/unix/freebsd.c index 658ff262d37..478916df371 100644 --- a/src/unix/freebsd.c +++ b/src/unix/freebsd.c @@ -116,6 +116,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); diff --git a/src/unix/haiku.c b/src/unix/haiku.c index cf17d836b4c..31284b66dc3 100644 --- a/src/unix/haiku.c +++ b/src/unix/haiku.c @@ -84,6 +84,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { area_info area; ssize_t cookie; diff --git a/src/unix/hurd.c b/src/unix/hurd.c index d19ea634790..63c878123f1 100644 --- a/src/unix/hurd.c +++ b/src/unix/hurd.c @@ -165,3 +165,8 @@ 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. */ } + + +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} diff --git a/src/unix/ibmi.c b/src/unix/ibmi.c index 8c6ae636329..837bba6e2fe 100644 --- a/src/unix/ibmi.c +++ b/src/unix/ibmi.c @@ -249,6 +249,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { SSTS0200 rcvr; diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index c567e75e955..fefa1c9223f 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -844,6 +844,49 @@ uint64_t uv_get_constrained_memory(void) { } +static uint64_t uv__get_available_memory_fallback(void) { + uint64_t current; + uint64_t constrained; + + constrained = uv__get_constrained_memory_fallback(); + if (constrained == 0) + return uv_get_free_memory(); + + current = uv__read_uint64("/sys/fs/cgroup/memory/memory.usage_in_bytes"); + return constrained - current; +} + + +uint64_t uv_get_available_memory(void) { + char filename[4097]; + char buf[1024]; + uint64_t current; + uint64_t constrained; + char* p; + + constrained = uv_get_constrained_memory(); + if (constrained == 0) + return uv__get_available_memory_fallback(); + + if (uv__slurp("/proc/self/cgroup", buf, sizeof(buf))) + return uv__get_available_memory_fallback(); + + if (memcmp(buf, "0::/", 4)) + return uv__get_available_memory_fallback(); + + p = strchr(buf, '\n'); + if (p != NULL) + *p = '\0'; + + p = buf + 4; + + snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%s/memory.current", p); + current = uv__read_uint64(filename); + + return constrained - current; +} + + void uv_loadavg(double avg[3]) { struct sysinfo info; char buf[128]; /* Large enough to hold all of /proc/loadavg. */ diff --git a/src/unix/netbsd.c b/src/unix/netbsd.c index c66333f522c..d5ece96d995 100644 --- a/src/unix/netbsd.c +++ b/src/unix/netbsd.c @@ -131,6 +131,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { kvm_t *kd = NULL; struct kinfo_proc2 *kinfo = NULL; diff --git a/src/unix/openbsd.c b/src/unix/openbsd.c index f32a94df387..4f3ad260b58 100644 --- a/src/unix/openbsd.c +++ b/src/unix/openbsd.c @@ -139,6 +139,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { struct kinfo_proc kinfo; size_t page_size = getpagesize(); diff --git a/src/unix/os390.c b/src/unix/os390.c index 73afb14e97e..3a412e4833e 100644 --- a/src/unix/os390.c +++ b/src/unix/os390.c @@ -198,6 +198,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { char* ascb; char* rax; diff --git a/src/unix/qnx.c b/src/unix/qnx.c index ca148d349f8..57ea9dfd9cc 100644 --- a/src/unix/qnx.c +++ b/src/unix/qnx.c @@ -88,6 +88,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + int uv_resident_set_memory(size_t* rss) { int fd; procfs_asinfo asinfo; diff --git a/src/unix/sunos.c b/src/unix/sunos.c index 35df8accc92..9e86042e3ae 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -403,6 +403,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + void uv_loadavg(double avg[3]) { (void) getloadavg(avg, 3); } diff --git a/src/win/util.c b/src/win/util.c index d1e4380adc5..ede83e32dbf 100644 --- a/src/win/util.c +++ b/src/win/util.c @@ -356,6 +356,11 @@ uint64_t uv_get_constrained_memory(void) { } +uint64_t uv_get_available_memory(void) { + return uv_get_free_memory(); +} + + uv_pid_t uv_os_getpid(void) { return GetCurrentProcessId(); } diff --git a/test/test-get-memory.c b/test/test-get-memory.c index 4555ba08895..fbde3fa9c7f 100644 --- a/test/test-get-memory.c +++ b/test/test-get-memory.c @@ -26,11 +26,13 @@ TEST_IMPL(get_memory) { uint64_t free_mem = uv_get_free_memory(); uint64_t total_mem = uv_get_total_memory(); uint64_t constrained_mem = uv_get_constrained_memory(); + uint64_t available_mem = uv_get_available_memory(); - printf("free_mem=%llu, total_mem=%llu, constrained_mem=%llu\n", + printf("free_mem=%llu, total_mem=%llu, constrained_mem=%llu, available_memo=%llu\n", (unsigned long long) free_mem, (unsigned long long) total_mem, - (unsigned long long) constrained_mem); + (unsigned long long) constrained_mem, + (unsigned long long) available_mem); ASSERT(free_mem > 0); ASSERT(total_mem > 0); @@ -40,5 +42,6 @@ TEST_IMPL(get_memory) { #else ASSERT(total_mem > free_mem); #endif + ASSERT(available_mem <= free_mem); return 0; }