unix,win: add uv_get_available_memory() (#3754)

This commit is contained in:
Tim Besard 2022-11-24 22:09:32 +01:00 committed by GitHub
parent a737255791
commit 988d225cf0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 242 additions and 32 deletions

View File

@ -561,7 +561,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`.
@ -572,6 +572,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

View File

@ -1718,6 +1718,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);

View File

@ -391,6 +391,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);

View File

@ -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();
}

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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();
}

View File

@ -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;

View File

@ -1343,47 +1343,169 @@ static uint64_t uv__read_uint64(const char* filename) {
}
/* This might return 0 if there was a problem getting the memory limit from
* cgroups. This is OK because a return value of 0 signifies that the memory
* limit is unknown.
*/
static uint64_t uv__get_constrained_memory_fallback(void) {
return uv__read_uint64("/sys/fs/cgroup/memory/memory.limit_in_bytes");
/* Given a buffer with the contents of a cgroup1 /proc/self/cgroups,
* finds the location and length of the memory controller mount path.
* This disregards the leading / for easy concatenation of paths.
* Returns NULL if the memory controller wasn't found. */
static char* uv__cgroup1_find_memory_controller(char buf[static 1024],
int* n) {
char* p;
/* Seek to the memory controller line. */
p = strchr(buf, ':');
while (p != NULL && strncmp(p, ":memory:", 8)) {
p = strchr(p, '\n');
if (p != NULL)
p = strchr(p, ':');
}
if (p != NULL) {
/* Determine the length of the mount path. */
p = p + strlen(":memory:/");
*n = (int) strcspn(p, "\n");
}
return p;
}
static void uv__get_cgroup1_memory_limits(char buf[static 1024], uint64_t* high,
uint64_t* max) {
char filename[4097];
char* p;
int n;
/* Find out where the controller is mounted. */
p = uv__cgroup1_find_memory_controller(buf, &n);
if (p != NULL) {
snprintf(filename, sizeof(filename),
"/sys/fs/cgroup/memory/%.*s/memory.soft_limit_in_bytes", n, p);
*high = uv__read_uint64(filename);
snprintf(filename, sizeof(filename),
"/sys/fs/cgroup/memory/%.*s/memory.limit_in_bytes", n, p);
*max = uv__read_uint64(filename);
/* If the controller wasn't mounted, the reads above will have failed,
* as indicated by uv__read_uint64 returning 0.
*/
if (*high != 0 && *max != 0)
return;
}
/* Fall back to the limits of the global memory controller. */
*high = uv__read_uint64("/sys/fs/cgroup/memory/memory.soft_limit_in_bytes");
*max = uv__read_uint64("/sys/fs/cgroup/memory/memory.limit_in_bytes");
}
static void uv__get_cgroup2_memory_limits(char buf[static 1024], uint64_t* high,
uint64_t* max) {
char filename[4097];
char* p;
int n;
/* Find out where the controller is mounted. */
p = buf + strlen("0::/");
n = (int) strcspn(p, "\n");
/* Read the memory limits of the controller. */
snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%.*s/memory.max", n, p);
*max = uv__read_uint64(filename);
snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%.*s/memory.high", n, p);
*high = uv__read_uint64(filename);
}
static uint64_t uv__get_cgroup_constrained_memory(char buf[static 1024]) {
uint64_t high;
uint64_t max;
/* In the case of cgroupv2, we'll only have a single entry. */
if (strncmp(buf, "0::/", 4))
uv__get_cgroup1_memory_limits(buf, &high, &max);
else
uv__get_cgroup2_memory_limits(buf, &high, &max);
if (high == 0 || max == 0)
return 0;
return high < max ? high : max;
}
uint64_t uv_get_constrained_memory(void) {
char buf[1024];
if (uv__slurp("/proc/self/cgroup", buf, sizeof(buf)))
return 0;
return uv__get_cgroup_constrained_memory(buf);
}
uint64_t uv_get_constrained_memory(void) {
static uint64_t uv__get_cgroup1_current_memory(char buf[static 1024]) {
char filename[4097];
char buf[1024];
uint64_t high;
uint64_t max;
uint64_t current;
char* p;
int n;
/* Find out where the controller is mounted. */
p = uv__cgroup1_find_memory_controller(buf, &n);
if (p != NULL) {
snprintf(filename, sizeof(filename),
"/sys/fs/cgroup/memory/%.*s/memory.usage_in_bytes", n, p);
current = uv__read_uint64(filename);
/* If the controller wasn't mounted, the reads above will have failed,
* as indicated by uv__read_uint64 returning 0.
*/
if (current != 0)
return current;
}
/* Fall back to the usage of the global memory controller. */
return uv__read_uint64("/sys/fs/cgroup/memory/memory.usage_in_bytes");
}
static uint64_t uv__get_cgroup2_current_memory(char buf[static 1024]) {
char filename[4097];
char* p;
int n;
/* Find out where the controller is mounted. */
p = buf + strlen("0::/");
n = (int) strcspn(p, "\n");
snprintf(filename, sizeof(filename),
"/sys/fs/cgroup/%.*s/memory.current", n, p);
return uv__read_uint64(filename);
}
uint64_t uv_get_available_memory(void) {
char buf[1024];
uint64_t constrained;
uint64_t current;
uint64_t total;
if (uv__slurp("/proc/self/cgroup", buf, sizeof(buf)))
return uv__get_constrained_memory_fallback();
return 0;
if (memcmp(buf, "0::/", 4))
return uv__get_constrained_memory_fallback();
constrained = uv__get_cgroup_constrained_memory(buf);
if (constrained == 0)
return uv_get_free_memory();
p = strchr(buf, '\n');
if (p != NULL)
*p = '\0';
total = uv_get_total_memory();
if (constrained > total)
return uv_get_free_memory();
p = buf + 4;
/* In the case of cgroupv2, we'll only have a single entry. */
if (strncmp(buf, "0::/", 4))
current = uv__get_cgroup1_current_memory(buf);
else
current = uv__get_cgroup2_current_memory(buf);
snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%s/memory.max", p);
max = uv__read_uint64(filename);
/* memory usage can be higher than the limit (for short bursts of time) */
if (constrained < current)
return 0;
if (max == 0)
return uv__get_constrained_memory_fallback();
snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%s/memory.high", p);
high = uv__read_uint64(filename);
if (high == 0)
return uv__get_constrained_memory_fallback();
return high < max ? high : max;
return constrained - current;
}

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -417,6 +417,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);
}

View File

@ -362,6 +362,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();
}

View File

@ -26,11 +26,14 @@ 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_mem=%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 +43,11 @@ TEST_IMPL(get_memory) {
#else
ASSERT(total_mem > free_mem);
#endif
ASSERT_LE(available_mem, total_mem);
/* we'd really want to test if available <= free, but that is fragile:
* with no limit set, get_available calls and returns get_free; so if
* any memory was freed between our calls to get_free and get_available
* we would fail such a test test (as observed on CI).
*/
return 0;
}