diff --git a/cmd/v/help/build-c.txt b/cmd/v/help/build-c.txt index a219c009b0..7e57251cd1 100644 --- a/cmd/v/help/build-c.txt +++ b/cmd/v/help/build-c.txt @@ -69,6 +69,20 @@ see also `v help build`. and unsafe{free(x)} calls manually in this mode). Some short lived applications, like compilers and other CLI tools are more performant without autofree. + + -gc + Use and link an optional garbage collector. + Only `-gc boehm` is supported currently. You need to install a + `libgc-dev` package first, or install it manually from source: + https://github.com/ivmai/bdwgc + + Note, this option is complementary to -autofree. The Boehm garbage + collector is conservative, and it may make your program significantly + slower if it does many small allocations in a loop. This option + is intended *mainly* for reducing the memory usage of programs, that + process large amounts of text in *batch mode* on low/limited memory + environments like small VPSes, and for which a few ms of garbage + collection pauses from time to time *do not matter much*. # Miscellaneous: -printfn diff --git a/doc/docs.md b/doc/docs.md index c937422e81..28c16d3e5f 100644 --- a/doc/docs.md +++ b/doc/docs.md @@ -3698,6 +3698,9 @@ If you do need a custom flag file, that has platform dependent code, use the postfix `_d_customflag.v`, and then use plaftorm dependent compile time conditional blocks inside it, i.e. `$if linux {}` etc. +- `_notd_customflag.v` => similar to _d_customflag.v, but will be used +*only* if you do NOT pass `-d customflag` to V. + ## Compile time pseudo variables V also gives your code access to a set of pseudo string variables, diff --git a/vlib/builtin/array.v b/vlib/builtin/array.v index a8326ae6b7..b5084a5565 100644 --- a/vlib/builtin/array.v +++ b/vlib/builtin/array.v @@ -482,7 +482,7 @@ pub fn (a &array) free() { // if a.is_slice { // return // } - C.free(a.data) + unsafe { free(a.data) } } [unsafe] @@ -493,7 +493,7 @@ pub fn (mut a []string) free() { for s in a { unsafe { s.free() } } - C.free(a.data) + unsafe { free(a.data) } } // str returns a string representation of the array of strings diff --git a/vlib/builtin/builtin.c.v b/vlib/builtin/builtin.c.v index b478baa800..420fc4416b 100644 --- a/vlib/builtin/builtin.c.v +++ b/vlib/builtin/builtin.c.v @@ -184,7 +184,11 @@ pub fn malloc(n int) byteptr { } nr_mallocs++ } $else { - res = unsafe { C.malloc(n) } + $if gcboehm ? { + res = unsafe { C.GC_MALLOC(n) } + } $else { + res = unsafe { C.malloc(n) } + } if res == 0 { panic('malloc($n) failed') } @@ -214,7 +218,11 @@ pub fn v_realloc(b byteptr, n int) byteptr { C.memcpy(new_ptr, b, n) } } $else { - new_ptr = unsafe { C.realloc(b, n) } + $if gcboehm ? { + new_ptr = unsafe { C.GC_REALLOC(b, n) } + } $else { + new_ptr = unsafe { C.realloc(b, n) } + } if new_ptr == 0 { panic('realloc($n) failed') } @@ -254,11 +262,16 @@ pub fn realloc_data(old_data byteptr, old_size int, new_size int) byteptr { min_size := if old_size < new_size { old_size } else { new_size } C.memcpy(new_ptr, old_data, min_size) C.memset(old_data, 0x57, old_size) - C.free(old_data) + free(old_data) return new_ptr } } - nptr := unsafe { C.realloc(old_data, new_size) } + mut nptr := byteptr(0) + $if gcboehm ? { + nptr = unsafe { C.GC_REALLOC(old_data, new_size) } + } $else { + nptr = unsafe { C.realloc(old_data, new_size) } + } if nptr == 0 { panic('realloc_data($old_data, $old_size, $new_size) failed') } @@ -268,7 +281,11 @@ pub fn realloc_data(old_data byteptr, old_size int, new_size int) byteptr { // v_calloc dynamically allocates a zeroed `n` bytes block of memory on the heap. // v_calloc returns a `byteptr` pointing to the memory address of the allocated space. pub fn v_calloc(n int) byteptr { - return C.calloc(1, n) + $if gcboehm ? { + return C.GC_MALLOC(n) + } $else { + return C.calloc(1, n) + } } // vcalloc dynamically allocates a zeroed `n` bytes block of memory on the heap. @@ -280,7 +297,11 @@ pub fn vcalloc(n int) byteptr { } else if n == 0 { return byteptr(0) } - return C.calloc(1, n) + $if gcboehm ? { + return C.GC_MALLOC(n) + } $else { + return C.calloc(1, n) + } } // free allows for manually freeing memory allocated at the address `ptr`. @@ -289,6 +310,10 @@ pub fn free(ptr voidptr) { $if prealloc { return } + $if gcboehm ? { + C.GC_FREE(ptr) + return + } C.free(ptr) } @@ -306,14 +331,6 @@ pub fn memdup(src voidptr, sz int) voidptr { } } -// v_ptr_free is used internally to manually free up memory allocated at the address `ptr`. -fn v_ptr_free(ptr voidptr) { - $if prealloc { - return - } - C.free(ptr) -} - // is_atty returns 1 if the `fd` file descriptor is open and refers to a terminal pub fn is_atty(fd int) int { $if windows { diff --git a/vlib/builtin/builtin_d_gcboehm.v b/vlib/builtin/builtin_d_gcboehm.v new file mode 100644 index 0000000000..4f4d9f3e5b --- /dev/null +++ b/vlib/builtin/builtin_d_gcboehm.v @@ -0,0 +1,13 @@ +module builtin + +#define GC_THREADS 1 + +#include + +#flag -lgc + +fn C.GC_MALLOC(n size_t) voidptr + +fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr + +fn C.GC_FREE(ptr voidptr) diff --git a/vlib/builtin/builtin_notd_gcboehm.v b/vlib/builtin/builtin_notd_gcboehm.v new file mode 100644 index 0000000000..97b1650a30 --- /dev/null +++ b/vlib/builtin/builtin_notd_gcboehm.v @@ -0,0 +1,11 @@ +module builtin + +// Just define the C functions, so that V does not error because of the missing definitions. + +// NB: they will NOT be used, since calls to them are wrapped with `$if gcboehm ? { }` + +fn C.GC_MALLOC(n size_t) voidptr + +fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr + +fn C.GC_FREE(ptr voidptr) diff --git a/vlib/builtin/option.v b/vlib/builtin/option.v index ede9b50d7a..054965f560 100644 --- a/vlib/builtin/option.v +++ b/vlib/builtin/option.v @@ -91,6 +91,6 @@ pub fn (ie &IError) free() { unsafe { ie.msg.free() cie := &C.IError(ie) - C.free(cie._object) + free(cie._object) } } diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 17bd46a225..f985608e13 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -422,6 +422,9 @@ pub fn (mut g Gen) init() { } g.comptime_defines.writeln('') } + if g.pref.gc_mode == .boehm { + g.comptime_defines.writeln('#define _VGCBOEHM (1)') + } if g.pref.is_debug || 'debug' in g.pref.compile_defines { g.comptime_defines.writeln('#define _VDEBUG (1)') } diff --git a/vlib/v/gen/c/cheaders.v b/vlib/v/gen/c/cheaders.v index 12c26c4804..a8661675b0 100644 --- a/vlib/v/gen/c/cheaders.v +++ b/vlib/v/gen/c/cheaders.v @@ -454,9 +454,10 @@ void _vcleanup(); } #endif +void v_free(voidptr ptr); voidptr memdup(voidptr src, int sz); static voidptr memfreedup(voidptr ptr, voidptr src, int sz) { - free(ptr); + v_free(ptr); return memdup(src, sz); } ' diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 614dea9238..b3bc2d2d93 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -533,6 +533,9 @@ fn (mut g Gen) comp_if_to_ifdef(name string, is_comptime_optional bool) ?string return '__cplusplus' } // other: + 'gcboehm' { + return '_VGCBOEHM' + } 'debug' { return '_VDEBUG' } diff --git a/vlib/v/gen/c/str.v b/vlib/v/gen/c/str.v index 3e531089e2..357a51d3fa 100644 --- a/vlib/v/gen/c/str.v +++ b/vlib/v/gen/c/str.v @@ -26,7 +26,11 @@ void _STR_PRINT_ARG(const char *fmt, char** refbufp, int *nbytes, int *memsize, } // increase buffer (somewhat exponentially) *memsize += (*memsize + *memsize) / 3 + guess; +#ifdef _VGCBOEHM + *refbufp = (char*)GC_REALLOC((void*)*refbufp, *memsize); +#else *refbufp = (char*)realloc((void*)*refbufp, *memsize); +#endif } va_end(args); } @@ -35,7 +39,11 @@ string _STR(const char *fmt, int nfmts, ...) { va_list argptr; int memsize = 128; int nbytes = 0; +#ifdef _VGCBOEHM + char* buf = (char*)GC_MALLOC(memsize); +#else char* buf = (char*)malloc(memsize); +#endif va_start(argptr, nfmts); for (int i=0; i