runtime: make free_memory() and total_memory() return Result types to allow for reporting errors (#24651)

This commit is contained in:
kbkpbot 2025-06-05 03:35:49 +08:00 committed by GitHub
parent 061da6aac6
commit 8c573cf355
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 99 additions and 31 deletions

View File

@ -104,8 +104,8 @@ fn (mut a App) collect_info() {
}
a.line('OS', '${os_kind}, ${os_details}')
a.line('Processor', arch_details.join(', '))
total_memory := f32(runtime.total_memory()) / (1024.0 * 1024.0 * 1024.0)
free_memory := f32(runtime.free_memory()) / (1024.0 * 1024.0 * 1024.0)
total_memory := f32(runtime.total_memory() or { 0 }) / (1024.0 * 1024.0 * 1024.0)
free_memory := f32(runtime.free_memory() or { 0 }) / (1024.0 * 1024.0 * 1024.0)
if total_memory != 0 && free_memory != 0 {
a.line('Memory', '${free_memory:.2}GB/${total_memory:.2}GB')
} else {

View File

@ -1,6 +1,7 @@
module runtime
#include <mach/mach.h>
#include <mach/task.h>
@[typedef]
pub struct C.vm_size_t {
@ -17,21 +18,46 @@ pub struct C.vm_statistics64_data_t {
@[typedef]
pub struct C.host_t {}
@[typedef]
pub struct C.task_t {}
fn C.mach_host_self() C.host_t
fn C.mach_task_self() C.task_t
fn C.mach_port_deallocate(task C.task_t, host C.host_t) int
fn C.host_page_size(host C.host_t, out_page_size &C.vm_size_t) int
fn C.host_statistics64(host C.host_t, flavor int, host_info_out &int, host_info_outCnt &u32) int
fn free_memory_impl() usize {
fn free_memory_impl() !usize {
$if macos {
mut hs := C.vm_statistics64_data_t{}
mut vmsz := u32(C.HOST_VM_INFO64_COUNT)
mut hps := u32(0)
mut host := C.mach_host_self()
defer {
// Critical: Release send right for host port
// --------------------------------------------------
// Mach ports are system resources. Calling mach_host_self()
// increments the port's reference count. We must manually release
// to prevent resource leaks (port exhaustion can cause kernel failures).
// mach_port_deallocate decrements the reference count, allowing
// system resource reclamation when count reaches zero.
// Parameters:
// C.mach_task_self() - Port for current task
// host - Host port to release
// Return value ignored (_) since we only care about resource cleanup
_ := C.mach_port_deallocate(C.mach_task_self(), host)
}
unsafe {
C.host_statistics64(host, C.HOST_VM_INFO64, &int(&hs), &vmsz)
C.host_page_size(host, &C.vm_size_t(&hps))
retval_1 := C.host_statistics64(host, C.HOST_VM_INFO64, &int(&hs), &vmsz)
if retval_1 != C.KERN_SUCCESS {
return error('free_memory: `C.host_statistics64()` return = ${retval_1}')
}
retval_2 := C.host_page_size(host, &C.vm_size_t(&hps))
if retval_2 != C.KERN_SUCCESS {
return error('free_memory: `C.host_page_size()` return = ${retval_2}')
}
}
return usize(u64(hs.free_count) * u64(hps))
}
return 1
return error('free_memory: not implemented')
}

View File

@ -1,5 +1,5 @@
module runtime
fn free_memory_impl() usize {
return 1
fn free_memory_impl() !usize {
return error('free_memory: not implemented')
}

View File

@ -2,21 +2,37 @@ module runtime
fn C.sysctlnametomib(name charptr, mib &int, len &usize) int
fn free_memory_impl() usize {
fn free_memory_impl() !usize {
$if cross ? {
return 1
return error('free_memory: not implemented')
}
$if !cross ? {
$if freebsd {
page_size := usize(C.sysconf(C._SC_PAGESIZE))
c_errno_1 := C.errno
if page_size == usize(-1) {
return error('free_memory: `C.sysconf()` return error code = ${c_errno_1}')
}
mut mib := [4]int{}
mut len := usize(4)
unsafe { C.sysctlnametomib(c'vm.stats.vm.v_free_count', &mib[0], &len) }
retval_2 := unsafe {
C.sysctlnametomib(c'vm.stats.vm.v_free_count', &mib[0], &len)
}
c_errno_2 := C.errno
if retval_2 == -1 {
return error('free_memory: `C.sysctlnametomib()` return error code = ${c_errno_2}')
}
mut free_pages := int(0)
bufsize := usize(4)
unsafe { C.sysctl(&mib[0], mib.len, &free_pages, &bufsize, 0, 0) }
retval_3 := unsafe {
C.sysctl(&mib[0], mib.len, &free_pages, &bufsize, 0, 0)
}
c_errno_3 := C.errno
if retval_3 == -1 {
return error('free_memory: `C.sysctl()` return error code = ${c_errno_3}')
}
return page_size * usize(free_pages)
}
}
return 1
return error('free_memory: not implemented')
}

View File

@ -1,15 +1,23 @@
module runtime
fn free_memory_impl() usize {
fn free_memory_impl() !usize {
$if cross ? {
return 1
return error('free_memory: not implemented')
}
$if !cross ? {
$if linux {
page_size := usize(C.sysconf(C._SC_PAGESIZE))
c_errno_1 := C.errno
if page_size == usize(-1) {
return error('free_memory: `C.sysconf(C._SC_PAGESIZE)` return error code = ${c_errno_1}')
}
av_phys_pages := usize(C.sysconf(C._SC_AVPHYS_PAGES))
c_errno_2 := C.errno
if av_phys_pages == usize(-1) {
return error('free_memory: `C.sysconf(C._SC_AVPHYS_PAGES)` return error code = ${c_errno_2}')
}
return page_size * av_phys_pages
}
}
return 1
return error('free_memory: not implemented')
}

View File

@ -8,18 +8,22 @@ struct C.uvmexp {
free int
}
fn free_memory_impl() usize {
fn free_memory_impl() !usize {
$if cross ? {
return 1
return error('free_memory: not implemented')
}
$if !cross ? {
$if openbsd {
mib := [C.CTL_VM, C.VM_UVMEXP]!
mut uvm := C.uvmexp{0, 0}
mut len := sizeof(C.uvmexp)
unsafe { C.sysctl(&mib[0], mib.len, &uvm, &len, C.NULL, 0) }
retval := unsafe { C.sysctl(&mib[0], mib.len, &uvm, &len, C.NULL, 0) }
c_errno := C.errno
if retval == -1 {
return error('free_memory: `C.sysctl()` return error code = ${c_errno}')
}
return usize(uvm.pagesize) * usize(uvm.free)
}
}
return 1
return error('free_memory: not implemented')
}

View File

@ -8,15 +8,23 @@ pub fn nr_cpus() int {
}
// total_memory returns total physical memory found on the system.
pub fn total_memory() usize {
pub fn total_memory() !usize {
page_size := usize(C.sysconf(C._SC_PAGESIZE))
c_errno_1 := C.errno
if page_size == usize(-1) {
return error('total_memory: `C.sysconf(C._SC_PAGESIZE)` return error code = ${c_errno_1}')
}
phys_pages := usize(C.sysconf(C._SC_PHYS_PAGES))
c_errno_2 := C.errno
if phys_pages == usize(-1) {
return error('total_memory: `C.sysconf(C._SC_PHYS_PAGES)` return error code = ${c_errno_2}')
}
return page_size * phys_pages
}
// free_memory returns free physical memory found on the system.
// Note: implementation available only on Darwin, FreeBSD, Linux, OpenBSD and
// Windows. Otherwise, returns 1.
pub fn free_memory() usize {
return free_memory_impl()
// Windows. Otherwise, returns 'free_memory: not implemented'.
pub fn free_memory() !usize {
return free_memory_impl()!
}

View File

@ -1,11 +1,17 @@
import runtime
fn test_physical_memory() {
total := runtime.total_memory()
free := runtime.free_memory()
println('total memory: ${total}')
println('free memory: ${free}')
assert total > 0 && free > 0
$if windows || linux || darwin || freebsd || openbsd {
total := runtime.total_memory()!
free := runtime.free_memory()!
println('total memory: ${total}')
println('free memory: ${free}')
assert total > 0 && free > 0
} $else {
total := runtime.total_memory()!
_ := runtime.free_memory() or { assert err.msg().contains('not implemented') }
assert total > 0
}
}
fn test_nr_cpus() {

View File

@ -23,14 +23,14 @@ pub fn nr_cpus() int {
}
// total_memory returns total physical memory found on the system.
pub fn total_memory() usize {
pub fn total_memory() !usize {
memory_status := C.MEMORYSTATUS{}
C.GlobalMemoryStatus(&memory_status)
return memory_status.dwTotalPhys
}
// free_memory returns free physical memory found on the system.
pub fn free_memory() usize {
pub fn free_memory() !usize {
memory_status := C.MEMORYSTATUS{}
C.GlobalMemoryStatus(&memory_status)
return memory_status.dwAvailPhys