diff --git a/vlib/builtin/builtin_d_gcboehm.c.v b/vlib/builtin/builtin_d_gcboehm.c.v index 5f3188c01f..9cfa780f0f 100644 --- a/vlib/builtin/builtin_d_gcboehm.c.v +++ b/vlib/builtin/builtin_d_gcboehm.c.v @@ -134,13 +134,37 @@ fn C.GC_is_disabled() int // protect memory block from being freed before this call fn C.GC_reachable_here(voidptr) -// gc_collect explicitly performs a garbage collection run. -// Note, that garbage collections are done automatically when needed in most cases, -// so usually you should need to call that function often. +// gc_is_enabled() returns true, if the GC is enabled at runtime. +// See also gc_disable() and gc_enable(). +pub fn gc_is_enabled() bool { + return 0 == C.GC_is_disabled() +} + +// gc_collect explicitly performs a single garbage collection run. +// Note, that garbage collections, are done automatically, when needed in most cases, +// so usually you should NOT need to call gc_collect() often. +// Note that gc_collect() is a NOP with `-gc none`. pub fn gc_collect() { C.GC_gcollect() } +// gc_enable explicitly enables the GC. +// Note, that garbage collections are done automatically, when needed in most cases, +// and also that by default the GC is on, so you do not need to enable it. +// See also gc_disable() and gc_collect(). +// Note that gc_enable() is a NOP with `-gc none`. +pub fn gc_enable() { + C.GC_enable() +} + +// gc_disable explicitly disables the GC. Do not forget to enable it again by calling gc_enable(), +// when your program is otherwise idle, and can afford it. +// See also gc_enable() and gc_collect(). +// Note that gc_disable() is a NOP with `-gc none`. +pub fn gc_disable() { + C.GC_disable() +} + // for leak detection it is advisable to do explicit garbage collections pub fn gc_check_leaks() { $if gcboehm_leak ? { diff --git a/vlib/builtin/builtin_notd_gcboehm.c.v b/vlib/builtin/builtin_notd_gcboehm.c.v index 5f9eb189a3..f11d6cec03 100644 --- a/vlib/builtin/builtin_notd_gcboehm.c.v +++ b/vlib/builtin/builtin_notd_gcboehm.c.v @@ -24,8 +24,28 @@ fn C.GC_gcollect() // When GC is not used, it is a NOP. pub fn gc_check_leaks() {} +// gc_is_enabled() returns true, if the GC is enabled at runtime. +// It will always return false, with `-gc none`. +// See also gc_disable() and gc_enable(). +pub fn gc_is_enabled() bool { + return false +} + +// gc_enable explicitly enables the GC. +// Note, that garbage collections are done automatically, when needed in most cases, +// and also that by default the GC is on, so you do not need to enable it. +// See also gc_disable() and gc_collect(). +// Note that gc_enable() is a NOP with `-gc none`. +pub fn gc_enable() {} + +// gc_disable explicitly disables the GC. Do not forget to enable it again by calling gc_enable(), +// when your program is otherwise idle, and can afford it. +// See also gc_enable() and gc_collect(). +// Note that gc_disable() is a NOP with `-gc none`. +pub fn gc_disable() {} + // gc_collect explicitly performs a garbage collection. -// When the GC is not on, it is a NOP. +// When the GC is not on, (with `-gc none`), it is a NOP. pub fn gc_collect() {} type FnGC_WarnCB = fn (msg &char, arg usize) diff --git a/vlib/v/tests/bench/bench_gc_enable_disable_collect.v b/vlib/v/tests/bench/bench_gc_enable_disable_collect.v new file mode 100644 index 0000000000..b6c79e4460 --- /dev/null +++ b/vlib/v/tests/bench/bench_gc_enable_disable_collect.v @@ -0,0 +1,44 @@ +// The goal of this program, is to test V's garbage collector interaction with gc_disable(), gc_enable(), gc_collect() and `-gc none`. +// Note that this program is intended to be run on Linux, where /proc/PID/status exists. +// It should run on other platforms too, but the last columns of the output will be empty. +// Example invocation: `MAX_ITERATIONS=100 BLOCK_SIZE=1_000_000 v run gc.v` . +import os + +const block_size = os.getenv_opt('BLOCK_SIZE') or { '1_000' }.int() +const max_iterations = os.getenv_opt('MAX_ITERATIONS') or { '40' }.int() + +fn do_some_work_and_allocate_memory() u64 { + a := []u8{len: block_size, init: u8(index)} + mut s := u64(0) + for x in a { + s += x + } + return s +} + +fn process_mem_stats(pid int) string { + lines := os.read_lines('/proc/${pid}/status') or { [] } + mut vals := map[string]string{} + for line in lines { + x := line.split(':') + vals[x[0]] = x[1] + } + return 'VmSize: ${vals['VmSize']} | VmRSS: ${vals['VmRSS']}' +} + +fn main() { + println('BLOCK_SIZE: ${block_size:15}, MAX_ITERATIONS: ${max_iterations:5}, gc_is_enabled: ${gc_is_enabled()}') + gc_disable() + mypid := os.getpid() + for c := 0; c < max_iterations; c++ { + if c % 15 == 0 { + gc_enable() + } + gc_collect() + s := do_some_work_and_allocate_memory() + println('gc_is_enabled: ${gc_is_enabled():6}, c: ${c:5}, s: ${s:10}, gc_memory_use: ${gc_memory_use():10}, ${process_mem_stats(mypid):30}') + if c % 15 == 0 { + gc_disable() + } + } +}