diff options
author | 2023-09-28 19:06:04 -0700 | |
---|---|---|
committer | 2023-09-28 19:06:04 -0700 | |
commit | 4bdec01619ee7db408b21477e1f7860d9bfdf789 (patch) | |
tree | 07adedc411dfcaae4a4f75834c9b9ade147a2f91 | |
parent | 2e8d9d46077887dd75f3d2d0e428cd2d491946f3 (diff) | |
download | bun-4bdec01619ee7db408b21477e1f7860d9bfdf789.tar.gz bun-4bdec01619ee7db408b21477e1f7860d9bfdf789.tar.zst bun-4bdec01619ee7db408b21477e1f7860d9bfdf789.zip |
Reduce memory usage of HTTP server (#6135)
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
-rw-r--r-- | packages/bun-usockets/src/eventing/epoll_kqueue.c | 15 | ||||
-rw-r--r-- | src/bun.js/api/server.zig | 3 | ||||
-rw-r--r-- | src/bun.js/bindings/JSCUSocketsLoopIntegration.cpp | 12 | ||||
-rw-r--r-- | src/bun.js/event_loop.zig | 35 | ||||
-rw-r--r-- | src/deps/uws.zig | 10 |
5 files changed, 50 insertions, 25 deletions
diff --git a/packages/bun-usockets/src/eventing/epoll_kqueue.c b/packages/bun-usockets/src/eventing/epoll_kqueue.c index df694dd04..44212d3be 100644 --- a/packages/bun-usockets/src/eventing/epoll_kqueue.c +++ b/packages/bun-usockets/src/eventing/epoll_kqueue.c @@ -30,7 +30,7 @@ void Bun__internal_dispatch_ready_poll(void* loop, void* poll); #include <stdint.h> #endif -void us_loop_run_bun_tick(struct us_loop_t *loop, int64_t timeoutMs); +void us_loop_run_bun_tick(struct us_loop_t *loop, int64_t timeoutMs, void*); /* Pointer tags are used to indicate a Bun pointer versus a uSockets pointer */ #define UNSET_BITS_49_UNTIL_64 0x0000FFFFFFFFFFFF @@ -174,13 +174,20 @@ void us_loop_run(struct us_loop_t *loop) { } } +void bun_on_tick_before(void* ctx); +void bun_on_tick_after(void* ctx); -void us_loop_run_bun_tick(struct us_loop_t *loop, int64_t timeoutMs) { + +void us_loop_run_bun_tick(struct us_loop_t *loop, int64_t timeoutMs, void* tickCallbackContext) { us_loop_integrate(loop); if (loop->num_polls == 0) return; + if (tickCallbackContext) { + bun_on_tick_before(tickCallbackContext); + } + /* Emit pre callback */ us_internal_loop_pre(loop); @@ -202,6 +209,10 @@ void us_loop_run_bun_tick(struct us_loop_t *loop, int64_t timeoutMs) { } #endif + if (tickCallbackContext) { + bun_on_tick_after(tickCallbackContext); + } + /* Iterate ready polls, dispatching them by type */ for (loop->current_ready_poll = 0; loop->current_ready_poll < loop->num_ready_polls; loop->current_ready_poll++) { struct us_poll_t *poll = GET_READY_POLL(loop, loop->current_ready_poll); diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index 685267bfd..fd6cc1f2b 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -5118,7 +5118,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp return JSPromise.rejectedPromiseValue(ctx, err); } - var request = ctx.bunVM().allocator.create(Request) catch unreachable; + var request = bun.default_allocator.create(Request) catch unreachable; request.* = existing_request; const response_value = this.config.onRequest.callWithThis( @@ -5538,6 +5538,7 @@ pub fn NewServer(comptime NamespaceType: type, comptime ssl_enabled_: bool, comp req.setYield(false); var ctx = this.request_pool_allocator.tryGet() catch @panic("ran out of memory"); ctx.create(this, req, resp); + this.vm.jsc.reportExtraMemory(@sizeOf(RequestContext)); var request_object = this.allocator.create(JSC.WebCore.Request) catch unreachable; var body = JSC.WebCore.InitRequestBodyValue(.{ .Null = {} }) catch unreachable; diff --git a/src/bun.js/bindings/JSCUSocketsLoopIntegration.cpp b/src/bun.js/bindings/JSCUSocketsLoopIntegration.cpp new file mode 100644 index 000000000..f607d586a --- /dev/null +++ b/src/bun.js/bindings/JSCUSocketsLoopIntegration.cpp @@ -0,0 +1,12 @@ +#include "root.h" +#include "JavaScriptCore/VM.h" + +extern "C" void bun_on_tick_before(JSC::VM* vm) +{ + // Let the GC do some work while we are idle + vm->heap.releaseAccess(); +} +extern "C" void bun_on_tick_after(JSC::VM* vm) +{ + vm->heap.acquireAccess(); +}
\ No newline at end of file diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index 7f175a1a1..278e62327 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -535,7 +535,7 @@ pub const GarbageCollectionController = struct { pub fn processGCTimer(this: *GarbageCollectionController) void { if (this.disabled) return; - var vm = this.bunVM().global.vm(); + var vm = this.bunVM().jsc; this.processGCTimerWithHeapSize(vm, vm.blockBytesAllocated()); } @@ -576,7 +576,7 @@ pub const GarbageCollectionController = struct { pub fn performGC(this: *GarbageCollectionController) void { if (this.disabled) return; - var vm = this.bunVM().global.vm(); + var vm = this.bunVM().jsc; vm.collectAsync(); this.gc_last_heap_size = vm.blockBytesAllocated(); } @@ -616,7 +616,7 @@ pub const EventLoop = struct { pub fn tickWhilePaused(this: *EventLoop, done: *bool) void { while (!done.*) { - this.virtual_machine.event_loop_handle.?.tick(); + this.virtual_machine.event_loop_handle.?.tick(this.virtual_machine.jsc); } } extern fn JSC__JSGlobalObject__drainMicrotasks(*JSC.JSGlobalObject) void; @@ -982,8 +982,9 @@ pub const EventLoop = struct { } if (loop.num_polls > 0 or loop.active > 0) { - loop.tick(); this.processGCTimer(); + loop.tick(ctx.jsc); + ctx.onAfterEventLoop(); // this.afterUSocketsTick(); } @@ -1006,8 +1007,8 @@ pub const EventLoop = struct { } if (loop.num_polls > 0 or loop.active > 0) { - loop.tickWithTimeout(timeoutMs); this.processGCTimer(); + loop.tickWithTimeout(timeoutMs, ctx.jsc); ctx.onAfterEventLoop(); // this.afterUSocketsTick(); } @@ -1031,8 +1032,8 @@ pub const EventLoop = struct { } } - loop.tick(); this.processGCTimer(); + loop.tick(ctx.jsc); ctx.onAfterEventLoop(); this.tickConcurrent(); this.tick(); @@ -1054,8 +1055,8 @@ pub const EventLoop = struct { } if (loop.active > 0) { - loop.tick(); this.processGCTimer(); + loop.tick(ctx.jsc); ctx.onAfterEventLoop(); // this.afterUSocketsTick(); } @@ -1072,7 +1073,7 @@ pub const EventLoop = struct { this.processGCTimer(); var global = ctx.global; - var global_vm = global.vm(); + var global_vm = ctx.jsc; while (true) { while (this.tickWithCount() > 0) : (this.global.handleRejectedPromises()) { this.tickConcurrent(); @@ -1093,12 +1094,12 @@ pub const EventLoop = struct { } pub fn waitForPromise(this: *EventLoop, promise: JSC.AnyPromise) void { - switch (promise.status(this.global.vm())) { + switch (promise.status(this.virtual_machine.jsc)) { JSC.JSPromise.Status.Pending => { - while (promise.status(this.global.vm()) == .Pending) { + while (promise.status(this.virtual_machine.jsc) == .Pending) { this.tick(); - if (promise.status(this.global.vm()) == .Pending) { + if (promise.status(this.virtual_machine.jsc) == .Pending) { this.autoTick(); } } @@ -1110,16 +1111,16 @@ pub const EventLoop = struct { // TODO: this implementation is terrible // we should not be checking the millitimestamp every time pub fn waitForPromiseWithTimeout(this: *EventLoop, promise: JSC.AnyPromise, timeout: u32) bool { - return switch (promise.status(this.global.vm())) { + return switch (promise.status(this.virtual_machine.jsc)) { JSC.JSPromise.Status.Pending => { if (timeout == 0) { return false; } var start_time = std.time.milliTimestamp(); - while (promise.status(this.global.vm()) == .Pending) { + while (promise.status(this.virtual_machine.jsc) == .Pending) { this.tick(); - if (promise.status(this.global.vm()) == .Pending) { + if (promise.status(this.virtual_machine.jsc) == .Pending) { const remaining = std.time.milliTimestamp() - start_time; if (remaining >= timeout) { return false; @@ -1159,7 +1160,7 @@ pub const EventLoop = struct { this.virtual_machine.event_loop_handle = actual; this.virtual_machine.gc_controller.init(this.virtual_machine); // _ = actual.addPostHandler(*JSC.EventLoop, this, JSC.EventLoop.afterUSocketsTick); - // _ = actual.addPreHandler(*JSC.VM, this.virtual_machine.global.vm(), JSC.VM.drainMicrotasks); + // _ = actual.addPreHandler(*JSC.VM, this.virtual_machine.jsc, JSC.VM.drainMicrotasks); } } @@ -1238,7 +1239,7 @@ pub const MiniEventLoop = struct { while (!isDone(context)) { if (this.tickConcurrentWithCount() == 0 and this.tasks.count == 0) { this.loop.num_polls += 1; - this.loop.tick(); + this.loop.tick(null); this.loop.num_polls -= 1; } @@ -1329,7 +1330,7 @@ pub const AnyEventLoop = union(enum) { // var concurrent = bun.default_allocator.create(ConcurrentTask) catch unreachable; // _ = concurrent.from(JSC.Task.init(&@field(ctx, field))); // concurrent.auto_delete = true; - // this.jsc.enqueueTaskConcurrent(concurrent); + // this.virtual_machine.jsc.enqueueTaskConcurrent(concurrent); }, .mini => { this.mini.enqueueTaskConcurrent(Context, ParentContext, ctx, Callback, field); diff --git a/src/deps/uws.zig b/src/deps/uws.zig index b8016a6b7..a565bd47a 100644 --- a/src/deps/uws.zig +++ b/src/deps/uws.zig @@ -835,12 +835,12 @@ pub const Loop = extern struct { return us_wakeup_loop(this); } - pub fn tick(this: *Loop) void { - us_loop_run_bun_tick(this, 0); + pub fn tick(this: *Loop, ctx: ?*anyopaque) void { + us_loop_run_bun_tick(this, 0, ctx); } - pub fn tickWithTimeout(this: *Loop, timeoutMs: i64) void { - us_loop_run_bun_tick(this, timeoutMs); + pub fn tickWithTimeout(this: *Loop, timeoutMs: i64, ctx: ?*anyopaque) void { + us_loop_run_bun_tick(this, timeoutMs, ctx); } pub fn nextTick(this: *Loop, comptime UserType: type, user_data: UserType, comptime deferCallback: fn (ctx: UserType) void) void { @@ -902,7 +902,7 @@ pub const Loop = extern struct { extern fn us_loop_free(loop: ?*Loop) void; extern fn us_loop_ext(loop: ?*Loop) ?*anyopaque; extern fn us_loop_run(loop: ?*Loop) void; - extern fn us_loop_run_bun_tick(loop: ?*Loop, timouetMs: i64) void; + extern fn us_loop_run_bun_tick(loop: ?*Loop, timouetMs: i64, ?*anyopaque) void; extern fn us_wakeup_loop(loop: ?*Loop) void; extern fn us_loop_integrate(loop: ?*Loop) void; extern fn us_loop_iteration_number(loop: ?*Loop) c_longlong; |