diff --git a/build.zig b/build.zig index a414b2d6..e2e1a9c0 100644 --- a/build.zig +++ b/build.zig @@ -152,6 +152,10 @@ fn common( ) !void { try jsruntime_pkgs.add(step, options); linkNetSurf(step); + + // link mimalloc + step.addObjectFile(.{ .path = "vendor/mimalloc/out/libmimalloc.a" }); + step.addIncludePath(.{ .path = "vendor/mimalloc/include" }); } fn linkNetSurf(step: *std.build.LibExeObjStep) void { diff --git a/src/mimalloc.zig b/src/mimalloc.zig new file mode 100644 index 00000000..b9d563e4 --- /dev/null +++ b/src/mimalloc.zig @@ -0,0 +1,48 @@ +// This file makes the glue between mimalloc heap allocation and libdom memory +// management. +// We replace the libdom default usage of allocations with mimalloc heap +// allocation to be able to free all memory used at once, like an arena usage. + +const std = @import("std"); +const c = @cImport({ + @cInclude("mimalloc.h"); +}); + +const Error = error{ + HeapNotNull, + HeapNull, +}; + +var heap: ?*c.mi_heap_t = null; + +pub fn create() Error!void { + if (heap != null) return Error.HeapNotNull; + heap = c.mi_heap_new(); + if (heap == null) return Error.HeapNull; +} + +pub fn destroy() void { + if (heap == null) return; + c.mi_heap_destroy(heap.?); + heap = null; +} + +pub export fn m_alloc(size: usize) callconv(.C) ?*anyopaque { + if (heap == null) return null; + return c.mi_heap_malloc(heap.?, size); +} + +pub export fn re_alloc(ptr: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque { + if (heap == null) return null; + return c.mi_heap_realloc(heap.?, ptr, size); +} + +pub export fn c_alloc(nmemb: usize, size: usize) callconv(.C) ?*anyopaque { + if (heap == null) return null; + return c.mi_heap_calloc(heap.?, nmemb, size); +} + +// NOOP, use destroy to clear all the memory allocated at once. +pub export fn f_ree(_: ?*anyopaque) callconv(.C) void { + return; +} diff --git a/src/netsurf.zig b/src/netsurf.zig index 619c37c7..1bc48e16 100644 --- a/src/netsurf.zig +++ b/src/netsurf.zig @@ -8,9 +8,29 @@ const c = @cImport({ @cInclude("events/event.h"); }); +const mimalloc = @import("mimalloc.zig"); + const Callback = @import("jsruntime").Callback; const EventToInterface = @import("events/event.zig").Event.toInterface; +// init initializes netsurf lib. +// init starts a mimalloc heap arena for the netsurf session. The caller must +// call deinit() to free the arena memory. +pub fn init() !void { + try mimalloc.create(); +} + +// deinit frees the mimalloc heap arena memory. +// It also clean dom namespaces and lwc strings. +pub fn deinit() void { + _ = c.dom_namespace_finalise(); + + // destroy all lwc strings. + c.lwc_deinit_strings(); + + mimalloc.destroy(); +} + // Vtable // ------