diff options
author | 2022-11-19 04:56:46 -0800 | |
---|---|---|
committer | 2022-11-19 04:56:46 -0800 | |
commit | bb95f90a62f3bbccd63288876870d1b107f510c3 (patch) | |
tree | 8e1db00ae697a69db516f8ed618778ce42c7130f /src | |
parent | 180632255488cf89009b2549951102340cd4997c (diff) | |
download | bun-bb95f90a62f3bbccd63288876870d1b107f510c3.tar.gz bun-bb95f90a62f3bbccd63288876870d1b107f510c3.tar.zst bun-bb95f90a62f3bbccd63288876870d1b107f510c3.zip |
Introduce `BUN_GARBAGE_COLLECTOR_LEVEL` debug environment variable
Diffstat (limited to 'src')
-rw-r--r-- | src/bun.js/api/bun.zig | 6 | ||||
-rw-r--r-- | src/bun.js/api/server.zig | 2 | ||||
-rw-r--r-- | src/bun.js/event_loop.zig | 24 | ||||
-rw-r--r-- | src/bun.js/javascript.zig | 48 | ||||
-rw-r--r-- | src/bun.js/test/jest.zig | 27 | ||||
-rw-r--r-- | src/bun_js.zig | 7 | ||||
-rw-r--r-- | src/cli/test_command.zig | 7 |
7 files changed, 83 insertions, 38 deletions
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig index 49143ec5c..131e08b6e 100644 --- a/src/bun.js/api/bun.zig +++ b/src/bun.js/api/bun.zig @@ -894,11 +894,7 @@ pub fn runGC( arguments: []const js.JSValueRef, _: js.ExceptionRef, ) js.JSValueRef { - // it should only force cleanup on thread exit - - Global.mimalloc_cleanup(false); - - return ctx.ptr().vm().runGC(arguments.len > 0 and JSValue.fromRef(arguments[0]).toBoolean()).asRef(); + return ctx.bunVM().garbageCollect(arguments.len > 0 and JSC.JSValue.c(arguments[0]).toBoolean()).asObjectRef(); } pub fn shrink( diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index 866b08e34..92a3c23d0 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -4490,7 +4490,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { this.config.hostname; this.ref(); - + this.vm.autoGarbageCollect(); this.app.listenWithConfig(*ThisServer, this, onListen, .{ .port = this.config.port, .host = host, diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index b62ac0123..28e12fb84 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -31,7 +31,7 @@ pub fn ConcurrentPromiseTask(comptime Context: type) type { task: WorkPoolTask = .{ .callback = runFromThreadPool }, event_loop: *JSC.EventLoop, allocator: std.mem.Allocator, - promise: JSValue, + promise: JSC.JSPromise.Strong = .{}, globalThis: *JSGlobalObject, concurrent_task: JSC.ConcurrentTask = .{}, @@ -44,10 +44,10 @@ pub fn ConcurrentPromiseTask(comptime Context: type) type { .event_loop = VirtualMachine.vm.event_loop, .ctx = value, .allocator = allocator, - .promise = JSValue.createInternalPromise(globalThis), .globalThis = globalThis, }; - this.promise.protect(); + var promise = JSC.JSPromise.create(globalThis); + this.promise.strong.set(globalThis, promise.asValue(globalThis)); this.ref.ref(this.event_loop.virtual_machine); return this; @@ -60,19 +60,9 @@ pub fn ConcurrentPromiseTask(comptime Context: type) type { } pub fn runFromJS(this: *This) void { - var promise_value = this.promise; + var promise = this.promise.swap(); this.ref.unref(this.event_loop.virtual_machine); - promise_value.ensureStillAlive(); - promise_value.unprotect(); - - var promise = promise_value.asInternalPromise() orelse { - if (comptime @hasDecl(Context, "deinit")) { - @call(.{}, Context.deinit, .{this.ctx}); - } - return; - }; - var ctx = this.ctx; ctx.then(promise); @@ -340,8 +330,9 @@ pub const EventLoop = struct { } pub fn autoTick(this: *EventLoop) void { - if (this.virtual_machine.uws_event_loop.?.num_polls > 0 or this.virtual_machine.uws_event_loop.?.active > 0) { - this.virtual_machine.uws_event_loop.?.tick(); + var loop = this.virtual_machine.uws_event_loop.?; + if (loop.num_polls > 0 or loop.active > 0) { + loop.tick(); // this.afterUSocketsTick(); } } @@ -392,6 +383,7 @@ pub const EventLoop = struct { this.start_server_on_next_tick = false; ctx.enterUWSLoop(); ctx.is_us_loop_entered = false; + ctx.autoGarbageCollect(); } } diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index fe559fac8..24633eea7 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -314,6 +314,7 @@ pub export fn Bun__handleRejectedPromise(global: *JSGlobalObject, promise: *JSC. const result = promise.result(global.vm()); var jsc_vm = global.bunVM(); jsc_vm.onUnhandledError(global, result); + jsc_vm.autoGarbageCollect(); } pub export fn Bun__onDidAppendPlugin(jsc_vm: *VirtualMachine, globalObject: *JSGlobalObject) void { @@ -428,6 +429,13 @@ pub const VirtualMachine = struct { unhandled_error_counter: usize = 0, modules: ModuleLoader.AsyncModule.Queue = .{}, + aggressive_garbage_collection: GCLevel = GCLevel.none, + + pub const GCLevel = enum { + none, + mild, + aggressive, + }; pub threadlocal var is_main_thread_vm: bool = false; @@ -435,6 +443,27 @@ pub const VirtualMachine = struct { this.onUnhandledRejection = defaultOnUnhandledRejection; } + pub fn loadExtraEnv(this: *VirtualMachine) void { + var map = this.bundler.env.map; + + if (map.get("BUN_SHOW_BUN_STACKFRAMES") != null) + this.hide_bun_stackframes = false; + + if (map.get("BUN_OVERRIDE_MODULE_PATH")) |override_path| { + if (override_path.len > 0) { + this.load_builtins_from_path = override_path; + } + } + + if (map.get("BUN_GARBAGE_COLLECTOR_LEVEL")) |gc_level| { + if (strings.eqlComptime(gc_level, "1")) { + this.aggressive_garbage_collection = .mild; + } else if (strings.eqlComptime(gc_level, "2")) { + this.aggressive_garbage_collection = .aggressive; + } + } + } + pub fn onUnhandledError(this: *JSC.VirtualMachine, globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) void { this.unhandled_error_counter += 1; this.onUnhandledRejection(this, globalObject, value); @@ -448,6 +477,22 @@ pub const VirtualMachine = struct { return this.bundler.getPackageManager(); } + pub fn garbageCollect(this: *const VirtualMachine, sync: bool) JSValue { + @setCold(true); + Global.mimalloc_cleanup(false); + if (sync) + return this.global.vm().runGC(true); + + this.global.vm().collectAsync(); + return JSValue.jsNumber(this.global.vm().heapSize()); + } + + pub inline fn autoGarbageCollect(this: *const VirtualMachine) void { + if (this.aggressive_garbage_collection != .none) { + _ = this.garbageCollect(this.aggressive_garbage_collection == .aggressive); + } + } + pub fn reload(this: *VirtualMachine) void { Output.debug("Reloading...", .{}); this.global.reload(); @@ -677,9 +722,6 @@ pub const VirtualMachine = struct { VirtualMachine.vm.bundler.configureLinker(); try VirtualMachine.vm.bundler.configureFramework(false); - if (VirtualMachine.vm.bundler.env.get("BUN_SHOW_BUN_STACKFRAMES") != null) - VirtualMachine.vm.hide_bun_stackframes = false; - vm.bundler.macro_context = js_ast.Macro.MacroContext.init(&vm.bundler); if (_args.serve orelse false) { diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index 3439e46a4..a9f4b13ac 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -275,7 +275,10 @@ pub const Expect = struct { .test_id = Jest.runner.?.pending_test.?.test_id, }; const expect_js_value = expect.toJS(globalObject); + expect_js_value.ensureStillAlive(); JSC.Jest.Expect.capturedValueSetCached(expect_js_value, globalObject, value); + expect_js_value.ensureStillAlive(); + expect.postMatch(globalObject); return expect_js_value; } @@ -294,6 +297,7 @@ pub const Expect = struct { globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, ) callconv(.C) JSC.JSValue { + defer this.postMatch(globalObject); const thisValue = callframe.this(); const arguments_ = callframe.arguments(1); const arguments = arguments_.ptr[0..arguments_.len]; @@ -346,6 +350,7 @@ pub const Expect = struct { globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, ) callconv(.C) JSC.JSValue { + defer this.postMatch(globalObject); const thisValue = callframe.this(); const arguments_ = callframe.arguments(1); const arguments = arguments_.ptr[0..arguments_.len]; @@ -431,6 +436,7 @@ pub const Expect = struct { globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame, ) callconv(.C) JSC.JSValue { + defer this.postMatch(globalObject); const thisValue = callFrame.this(); const arguments_ = callFrame.arguments(1); const arguments = arguments_.ptr[0..arguments_.len]; @@ -491,6 +497,7 @@ pub const Expect = struct { } pub fn toBeTruthy(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + defer this.postMatch(globalObject); const thisValue = callFrame.this(); const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { globalObject.throw("Internal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); @@ -525,6 +532,7 @@ pub const Expect = struct { } pub fn toBeUndefined(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + defer this.postMatch(globalObject); const thisValue = callFrame.this(); const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { globalObject.throw("Interal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); @@ -552,6 +560,8 @@ pub const Expect = struct { } pub fn toBeNaN(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + defer this.postMatch(globalObject); + const thisValue = callFrame.this(); const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { globalObject.throw("Interal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); @@ -582,6 +592,8 @@ pub const Expect = struct { } pub fn toBeNull(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + defer this.postMatch(globalObject); + const thisValue = callFrame.this(); const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { globalObject.throw("Interal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); @@ -607,6 +619,8 @@ pub const Expect = struct { } pub fn toBeDefined(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + defer this.postMatch(globalObject); + const thisValue = callFrame.this(); const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { globalObject.throw("Interal consistency error: the expect(value) was garbage collected but it should not have been!", .{}); @@ -632,6 +646,8 @@ pub const Expect = struct { } pub fn toBeFalsy(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSC.JSValue { + defer this.postMatch(globalObject); + const thisValue = callFrame.this(); const value: JSValue = Expect.capturedValueGetCached(thisValue) orelse { @@ -733,6 +749,11 @@ pub const Expect = struct { globalObject.throw("Not implemented", .{}); return .zero; } + + pub fn postMatch(_: *Expect, globalObject: *JSC.JSGlobalObject) void { + var vm = globalObject.bunVM(); + vm.autoGarbageCollect(); + } }; pub const TestScope = struct { @@ -824,13 +845,15 @@ pub const TestScope = struct { globalThis.bunVM().runErrorHandler(err, null); var task: *TestRunnerTask = arguments.ptr[1].asPromisePtr(TestRunnerTask); task.handleResult(.{ .fail = active_test_expectation_counter.actual }, .promise); + globalThis.bunVM().autoGarbageCollect(); return JSValue.jsUndefined(); } - pub fn onResolve(_: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { + pub fn onResolve(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue { const arguments = callframe.arguments(2); var task: *TestRunnerTask = arguments.ptr[1].asPromisePtr(TestRunnerTask); task.handleResult(.{ .pass = active_test_expectation_counter.actual }, .promise); + globalThis.bunVM().autoGarbageCollect(); return JSValue.jsUndefined(); } @@ -840,6 +863,7 @@ pub const TestScope = struct { ) callconv(.C) JSValue { const function = callframe.callee(); const args = callframe.arguments(1); + defer globalThis.bunVM().autoGarbageCollect(); if (JSC.getFunctionData(function)) |data| { var task = bun.cast(*TestRunnerTask, data); @@ -866,6 +890,7 @@ pub const TestScope = struct { defer { js.JSValueUnprotect(vm.global, callback); this.callback = null; + vm.autoGarbageCollect(); } JSC.markBinding(@src()); diff --git a/src/bun_js.zig b/src/bun_js.zig index 2510183aa..07d7e4adf 100644 --- a/src/bun_js.zig +++ b/src/bun_js.zig @@ -120,12 +120,7 @@ pub const Run = struct { } } - if (run.vm.bundler.env.map.get("BUN_OVERRIDE_MODULE_PATH")) |override_path| { - if (override_path.len > 0) { - run.vm.load_builtins_from_path = override_path; - } - } - + run.vm.loadExtraEnv(); run.vm.is_main_thread = true; JSC.VirtualMachine.is_main_thread_vm = true; diff --git a/src/cli/test_command.zig b/src/cli/test_command.zig index 3f0082940..9211be7f3 100644 --- a/src/cli/test_command.zig +++ b/src/cli/test_command.zig @@ -332,12 +332,7 @@ pub const TestCommand = struct { try vm.bundler.configureDefines(); vm.bundler.options.rewrite_jest_for_tests = true; - if (vm.bundler.env.map.get("BUN_OVERRIDE_MODULE_PATH")) |override_path| { - if (override_path.len > 0) { - vm.load_builtins_from_path = override_path; - } - } - + vm.loadExtraEnv(); vm.is_main_thread = true; JSC.VirtualMachine.is_main_thread_vm = true; |