diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/allocators/mimalloc.zig | 2 | ||||
-rw-r--r-- | src/javascript/jsc/api/server.zig | 201 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/bindings.cpp | 13 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 8 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/response.zig | 9 | ||||
-rw-r--r-- | src/mimalloc_arena.zig | 8 | ||||
-rw-r--r-- | src/thread_pool.zig | 28 |
7 files changed, 185 insertions, 84 deletions
diff --git a/src/allocators/mimalloc.zig b/src/allocators/mimalloc.zig index b8badec99..f671b4553 100644 --- a/src/allocators/mimalloc.zig +++ b/src/allocators/mimalloc.zig @@ -63,7 +63,7 @@ pub extern fn mi_heap_new() ?*mi_heap_t; pub extern fn mi_heap_delete(heap: ?*mi_heap_t) void; pub extern fn mi_heap_destroy(heap: ?*mi_heap_t) void; pub extern fn mi_heap_set_default(heap: ?*mi_heap_t) ?*mi_heap_t; -pub extern fn mi_heap_get_default() ?*mi_heap_t; +pub extern fn mi_heap_get_default() *mi_heap_t; pub extern fn mi_heap_get_backing() *mi_heap_t; pub extern fn mi_heap_collect(heap: ?*mi_heap_t, force: bool) void; pub extern fn mi_heap_malloc(heap: ?*mi_heap_t, size: usize) ?*anyopaque; diff --git a/src/javascript/jsc/api/server.zig b/src/javascript/jsc/api/server.zig index 361be14f0..2ebd30ee1 100644 --- a/src/javascript/jsc/api/server.zig +++ b/src/javascript/jsc/api/server.zig @@ -47,7 +47,6 @@ const FetchEvent = WebCore.FetchEvent; const js = @import("../../../jsc.zig").C; const JSC = @import("../../../jsc.zig"); const JSError = @import("../base.zig").JSError; -const d = @import("../base.zig").d; const MarkedArrayBuffer = @import("../base.zig").MarkedArrayBuffer; const getAllocator = @import("../base.zig").getAllocator; const JSValue = @import("../../../jsc.zig").JSValue; @@ -82,6 +81,7 @@ const Fallback = Runtime.Fallback; const MimeType = HTTP.MimeType; const Blob = JSC.WebCore.Blob; const BoringSSL = @import("boringssl"); +const Arena = @import("../../../mimalloc_arena.zig").Arena; const SendfileContext = struct { fd: i32, socket_fd: i32 = 0, @@ -431,13 +431,20 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp return struct { const RequestContext = @This(); const App = uws.NewApp(ssl_enabled); + pub threadlocal var pool: ?*RequestContext.RequestContextStackAllocator = null; + pub threadlocal var pool_allocator: std.mem.Allocator = undefined; server: *ThisServer, resp: *App.Response, + /// thread-local default heap allocator + /// this prevents an extra pthread_getspecific() call which shows up in profiling + allocator: std.mem.Allocator, req: *uws.Request, url: string, method: HTTP.Method, aborted: bool = false, + + has_marked_complete: bool = false, response_jsvalue: JSC.JSValue = JSC.JSValue.zero, response_ptr: ?*JSC.WebCore.Response = null, blob: JSC.WebCore.Blob = JSC.WebCore.Blob{}, @@ -454,13 +461,75 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp /// When the response body is a temporary value response_buf_owned: std.ArrayListUnmanaged(u8) = .{}, - pub const RequestContextStackAllocator = std.heap.StackFallbackAllocator(@sizeOf(RequestContext) * 2048 + 4096); + // Pre-allocate up to 2048 requests + // use a bitset to track which ones are used + pub const RequestContextStackAllocator = struct { + buf: [2048]RequestContext = undefined, + unused: Set = undefined, + fallback_allocator: std.mem.Allocator = undefined, + + pub const Set = std.bit_set.ArrayBitSet(usize, 2048); + + pub fn get(this: *@This()) std.mem.Allocator { + this.unused = Set.initFull(); + return std.mem.Allocator.init(this, alloc, resize, free); + } + + fn alloc(self: *@This(), a: usize, b: u29, c: u29, d: usize) ![]u8 { + if (self.unused.findFirstSet()) |i| { + self.unused.unset(i); + return std.mem.asBytes(&self.buf[i]); + } + + return try self.fallback_allocator.rawAlloc(a, b, c, d); + } + + fn resize( + _: *@This(), + _: []u8, + _: u29, + _: usize, + _: u29, + _: usize, + ) ?usize { + unreachable; + } + + fn sliceContainsSlice(container: []u8, slice: []u8) bool { + return @ptrToInt(slice.ptr) >= @ptrToInt(container.ptr) and + (@ptrToInt(slice.ptr) + slice.len) <= (@ptrToInt(container.ptr) + container.len); + } + + fn free( + self: *@This(), + buf: []u8, + buf_align: u29, + return_address: usize, + ) void { + _ = buf_align; + _ = return_address; + const bytes = std.mem.asBytes(&self.buf); + if (sliceContainsSlice(bytes, buf)) { + const index = if (bytes[0..buf.len].ptr != buf.ptr) + (@ptrToInt(buf.ptr) - @ptrToInt(bytes)) / @sizeOf(RequestContext) + else + @as(usize, 0); + + if (comptime Environment.allow_assert) { + std.debug.assert(@intToPtr(*RequestContext, @ptrToInt(buf.ptr)) == &self.buf[index]); + std.debug.assert(!self.unused.isSet(index)); + } + + self.unused.set(index); + } else { + self.fallback_allocator.rawFree(buf, buf_align, return_address); + } + } + }; // TODO: support builtin compression const can_sendfile = !ssl_enabled; - pub threadlocal var pool: *RequestContextStackAllocator = undefined; - pub fn setAbortHandler(this: *RequestContext) void { if (this.has_abort_handler) return; this.has_abort_handler = true; @@ -497,6 +566,9 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp ctx.renderMissing(); return; }; + ctx.response_jsvalue = value; + JSC.C.JSValueProtect(ctx.server.globalThis.ref(), value.asObjectRef()); + ctx.render(response); } @@ -551,7 +623,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.resp.writeStatus("500 Internal Server Error"); this.resp.writeHeader("content-type", MimeType.html.value); - var allocator = bun.default_allocator; + const allocator = this.allocator; var fallback_container = allocator.create(Api.FallbackMessageContainer) catch unreachable; defer allocator.destroy(fallback_container); @@ -596,6 +668,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp pub fn onWritableResponseBuffer(this: *RequestContext, write_offset: c_ulong, resp: *App.Response) callconv(.C) bool { if (this.aborted) { + this.finalize(); return false; } return this.sendWritableBytes(this.response_buf_owned.items, write_offset, resp); @@ -603,10 +676,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp pub fn create(this: *RequestContext, server: *ThisServer, req: *uws.Request, resp: *App.Response) void { this.* = .{ + .allocator = server.allocator, .resp = resp, .req = req, // this memory is owned by the Request object - .url = strings.append(bun.default_allocator, server.base_url_string_for_joining, req.url()) catch + .url = strings.append(this.allocator, server.base_url_string_for_joining, req.url()) catch @panic("Out of memory while joining the URL path?"), .method = HTTP.Method.which(req.method()) orelse .GET, .server = server, @@ -616,13 +690,19 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp pub fn onAbort(this: *RequestContext, _: *App.Response) void { this.aborted = true; this.finalizeWithoutDeinit(); + this.markComplete(); + } + + pub fn markComplete(this: *RequestContext) void { + if (!this.has_marked_complete) this.server.onRequestComplete(); + this.has_marked_complete = true; } // This function may be called multiple times // so it's important that we can safely do that pub fn finalizeWithoutDeinit(this: *RequestContext) void { this.blob.detach(); - this.request_body_buf.clearAndFree(bun.default_allocator); + this.request_body_buf.clearAndFree(this.allocator); if (!this.response_jsvalue.isEmpty()) { this.server.response_objects_pool.push(this.server.globalThis, this.response_jsvalue); @@ -656,14 +736,13 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp this.response_headers = null; } - this.response_buf_owned.clearAndFree(bun.default_allocator); + this.response_buf_owned.clearAndFree(this.allocator); } pub fn finalize(this: *RequestContext) void { var server = this.server; this.finalizeWithoutDeinit(); - std.debug.assert(server.pending_requests > 0); + this.markComplete(); server.request_pool_allocator.destroy(this); - server.onRequestComplete(); } fn writeHeaders( @@ -767,8 +846,10 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } pub fn onWritableBytes(this: *RequestContext, write_offset: c_ulong, resp: *App.Response) callconv(.C) bool { - if (this.aborted) + if (this.aborted) { + this.finalize(); return false; + } var bytes = this.blob.sharedView(); return this.sendWritableBytes(bytes, write_offset, resp); @@ -794,12 +875,12 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } fn onPrepareSendfile(this: *RequestContext, fd: i32, size: Blob.SizeType, err: ?JSC.SystemError, globalThis: *JSGlobalObject) void { - if (this.aborted) { - this.finalize(); - return; - } - if (err) |system_error| { + if (this.aborted) { + this.finalize(); + return; + } + if (system_error.errno == @enumToInt(std.os.E.NOENT)) { this.runErrorHandlerWithStatusCode(system_error.toErrorInstance(globalThis), 404); } else { @@ -819,6 +900,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp }; if (this.aborted) { + _ = JSC.Node.Syscall.close(fd); this.finalize(); return; } @@ -849,11 +931,13 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } pub fn doSendfile(this: *RequestContext, blob: Blob) void { - if (this.has_sendfile_ctx) return; if (this.aborted) { this.finalize(); return; } + + if (this.has_sendfile_ctx) return; + this.has_sendfile_ctx = true; this.setAbortHandler(); @@ -865,6 +949,11 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } pub fn onReadFile(this: *RequestContext, result: Blob.Store.ReadFile.ResultType) void { + if (this.aborted) { + this.finalize(); + return; + } + if (result == .err) { this.runErrorHandler(result.err.toErrorInstance(this.server.globalThis)); return; @@ -886,21 +975,25 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } pub fn doRenderWithBody(this: *RequestContext, value: *JSC.WebCore.Body.Value) void { - if (this.aborted) { - this.finalize(); - return; - } - switch (value.*) { .Error => { const err = value.Error; _ = value.use(); + if (this.aborted) { + this.finalize(); + return; + } this.runErrorHandler(err); return; }, .Blob => { this.blob = value.use(); + if (this.aborted) { + this.finalize(); + return; + } + if (this.blob.needsToReadFile()) { this.req.setYield(false); this.setAbortHandler(); @@ -932,6 +1025,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp pub fn doRender(this: *RequestContext) void { if (this.aborted) { + this.finalize(); return; } var response = this.response_ptr.?; @@ -968,7 +1062,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp ) void { if (this.resp.hasResponded()) return; - var exception_list: std.ArrayList(Api.JsException) = std.ArrayList(Api.JsException).init(bun.default_allocator); + var exception_list: std.ArrayList(Api.JsException) = std.ArrayList(Api.JsException).init(this.allocator); defer exception_list.deinit(); if (!this.server.config.onError.isEmpty() and !this.has_called_error_handler) { this.has_called_error_handler = true; @@ -1094,14 +1188,17 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } pub fn resolveRequestBody(this: *RequestContext) void { - if (this.aborted) + if (this.aborted) { + this.finalize(); return; + } + if (JSC.JSValue.fromRef(this.request_js_object).as(Request)) |req| { - var bytes = this.request_body_buf.toOwnedSlice(bun.default_allocator); + var bytes = this.request_body_buf.toOwnedSlice(this.allocator); var old = req.body; req.body = .{ .Blob = if (bytes.len > 0) - Blob.init(bytes, bun.default_allocator, this.server.globalThis) + Blob.init(bytes, this.allocator, this.server.globalThis) else Blob.initEmpty(this.server.globalThis), }; @@ -1113,12 +1210,12 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp pub fn onBodyChunk(this: *RequestContext, _: *App.Response, chunk: []const u8, last: bool) void { if (this.aborted) return; - this.request_body_buf.appendSlice(bun.default_allocator, chunk) catch @panic("Out of memory while allocating request body"); + this.request_body_buf.appendSlice(this.allocator, chunk) catch @panic("Out of memory while allocating request body"); if (last) { if (JSC.JSValue.fromRef(this.request_js_object).as(Request) != null) { uws.Loop.get().?.nextTick(*RequestContext, this, resolveRequestBody); } else { - this.request_body_buf.deinit(bun.default_allocator); + this.request_body_buf.deinit(this.allocator); this.request_body_buf = .{}; } } @@ -1154,7 +1251,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp return; } - this.request_body_buf.ensureTotalCapacityPrecise(bun.default_allocator, len) catch @panic("Out of memory while allocating request body buffer"); + this.request_body_buf.ensureTotalCapacityPrecise(this.allocator, len) catch @panic("Out of memory while allocating request body buffer"); } this.setAbortHandler(); @@ -1189,6 +1286,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { request_pool_allocator: std.mem.Allocator = undefined, has_js_deinited: bool = false, listen_callback: JSC.AnyTask = undefined, + allocator: std.mem.Allocator, pub const Class = JSC.NewClass( ThisServer, @@ -1261,8 +1359,6 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { } pub fn stop(this: *ThisServer) void { - this.next_tick_pending = true; - if (this.listener) |listener| { listener.close(); this.listener = null; @@ -1284,7 +1380,8 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { } this.app.destroy(); - bun.default_allocator.destroy(this); + const allocator = this.allocator; + allocator.destroy(this); } pub fn init(config: ServerConfig, globalThis: *JSGlobalObject) *ThisServer { @@ -1294,9 +1391,19 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { .config = config, .base_url_string_for_joining = strings.trim(config.base_url.href, "/"), .vm = JSC.VirtualMachine.vm, + .allocator = Arena.getThreadlocalDefault(), }; - RequestContext.pool = bun.default_allocator.create(RequestContext.RequestContextStackAllocator) catch @panic("Out of memory!"); - server.request_pool_allocator = RequestContext.pool.get(); + if (RequestContext.pool == null) { + RequestContext.pool = server.allocator.create(RequestContext.RequestContextStackAllocator) catch @panic("Out of memory!"); + RequestContext.pool.?.* = .{ + .fallback_allocator = server.allocator, + }; + server.request_pool_allocator = RequestContext.pool.?.get(); + RequestContext.pool_allocator = server.request_pool_allocator; + } else { + server.request_pool_allocator = RequestContext.pool_allocator; + } + return server; } @@ -1386,7 +1493,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { this.pending_requests += 1; defer this.pending_requests -= 1; req.setYield(false); - var stack_fallback = std.heap.stackFallback(8096, bun.default_allocator); + var stack_fallback = std.heap.stackFallback(8096, this.allocator); var allocator = stack_fallback.get(); var buffer_writer = js_printer.BufferWriter.init(allocator) catch unreachable; @@ -1431,7 +1538,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { if (strings.indexOfChar(url, ':')) |colon| { url = url[0..colon]; } - editor.open(ctx.path, url, line, column, bun.default_allocator) catch Output.prettyErrorln("Failed to open editor", .{}); + editor.open(ctx.path, url, line, column, this.allocator) catch Output.prettyErrorln("Failed to open editor", .{}); } else { resp.writeStatus("500 Missing Editor :("); resp.end("Please set your editor in bunfig.toml", false); @@ -1446,7 +1553,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { var ctx = this.request_pool_allocator.create(RequestContext) catch @panic("ran out of memory"); ctx.create(this, req, resp); - var request_object = bun.default_allocator.create(JSC.WebCore.Request) catch unreachable; + var request_object = this.allocator.create(JSC.WebCore.Request) catch unreachable; request_object.* = .{ .url = JSC.ZigString.init(ctx.url), .method = ctx.method, @@ -1464,32 +1571,34 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { var args = [_]JSC.C.JSValueRef{JSC.WebCore.Request.Class.make(this.globalThis.ref(), request_object)}; ctx.request_js_object = args[0]; JSC.C.JSValueProtect(this.globalThis.ref(), args[0]); - ctx.response_jsvalue = JSC.C.JSObjectCallAsFunctionReturnValue(this.globalThis.ref(), this.config.onRequest.asObjectRef(), this.thisObject.asObjectRef(), 1, &args); + const response_value = JSC.C.JSObjectCallAsFunctionReturnValue(this.globalThis.ref(), this.config.onRequest.asObjectRef(), this.thisObject.asObjectRef(), 1, &args); if (ctx.aborted) { ctx.finalize(); return; } - if (ctx.response_jsvalue.isEmptyOrUndefinedOrNull() and !ctx.resp.hasResponded()) { + if (response_value.isEmptyOrUndefinedOrNull() and !ctx.resp.hasResponded()) { ctx.renderMissing(); return; } - if (ctx.response_jsvalue.isError() or ctx.response_jsvalue.isAggregateError(this.globalThis) or ctx.response_jsvalue.isException(this.globalThis.vm())) { - ctx.runErrorHandler(ctx.response_jsvalue); + if (response_value.isError() or response_value.isAggregateError(this.globalThis) or response_value.isException(this.globalThis.vm())) { + ctx.runErrorHandler(response_value); return; } - JSC.C.JSValueProtect(this.globalThis.ref(), ctx.response_jsvalue.asObjectRef()); - if (ctx.response_jsvalue.as(JSC.WebCore.Response)) |response| { + if (response_value.as(JSC.WebCore.Response)) |response| { + JSC.C.JSValueProtect(this.globalThis.ref(), response_value.asObjectRef()); + ctx.response_jsvalue = response_value; + ctx.render(response); return; } var wait_for_promise = false; - if (ctx.response_jsvalue.asPromise()) |promise| { + if (response_value.asPromise()) |promise| { // If we immediately have the value available, we can skip the extra event loop tick switch (promise.status(vm.global.vm())) { .Pending => {}, @@ -1505,7 +1614,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { wait_for_promise = true; // I don't think this case should happen // But I'm uncertain - } else if (ctx.response_jsvalue.asInternalPromise()) |promise| { + } else if (response_value.asInternalPromise()) |promise| { switch (promise.status(vm.global.vm())) { .Pending => {}, .Fulfilled => { @@ -1522,7 +1631,7 @@ pub fn NewServer(comptime ssl_enabled_: bool, comptime debug_mode_: bool) type { if (wait_for_promise) { ctx.setAbortHandler(); - ctx.response_jsvalue.then( + response_value.then( this.globalThis, RequestContext, ctx, diff --git a/src/javascript/jsc/bindings/bindings.cpp b/src/javascript/jsc/bindings/bindings.cpp index a6fd5c95f..16900c191 100644 --- a/src/javascript/jsc/bindings/bindings.cpp +++ b/src/javascript/jsc/bindings/bindings.cpp @@ -508,9 +508,14 @@ JSC__JSPromise* JSC__JSPromise__create(JSC__JSGlobalObject* arg0) void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObject, void* ctx, void (*ArgFn3)(JSC__JSGlobalObject* arg0, void* arg1, JSC__JSValue arg2, size_t arg3), void (*ArgFn4)(JSC__JSGlobalObject* arg0, void* arg1, JSC__JSValue arg2, size_t arg3)) { + globalObject->vm().drainMicrotasks(); + auto* cell = JSC::JSValue::decode(JSValue0).asCell(); + JSC::Strong<JSC::Unknown> promiseValue = { globalObject->vm(), cell }; + JSC::JSNativeStdFunction* resolverFunction = JSC::JSNativeStdFunction::create( - globalObject->vm(), globalObject, 1, String(), [ctx, ArgFn3](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue { + globalObject->vm(), globalObject, 1, String(), [&promiseValue, ctx, ArgFn3](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue { auto argCount = static_cast<uint16_t>(callFrame->argumentCount()); + WTF::Vector<JSC::EncodedJSValue, 16> arguments; arguments.reserveInitialCapacity(argCount); if (argCount) { @@ -520,11 +525,12 @@ void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObjec } ArgFn3(globalObject, ctx, reinterpret_cast<JSC__JSValue>(arguments.data()), argCount); + return JSC::JSValue::encode(JSC::jsUndefined()); }); JSC::JSNativeStdFunction* rejecterFunction = JSC::JSNativeStdFunction::create( globalObject->vm(), globalObject, 1, String(), - [ctx, ArgFn4](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue { + [&promiseValue, ctx, ArgFn4](JSC::JSGlobalObject* globalObject, JSC::CallFrame* callFrame) -> JSC::EncodedJSValue { auto argCount = static_cast<uint16_t>(callFrame->argumentCount()); WTF::Vector<JSC::EncodedJSValue, 16> arguments; arguments.reserveInitialCapacity(argCount); @@ -535,11 +541,10 @@ void JSC__JSValue___then(JSC__JSValue JSValue0, JSC__JSGlobalObject* globalObjec } ArgFn4(globalObject, ctx, reinterpret_cast<JSC__JSValue>(arguments.data()), argCount); + return JSC::JSValue::encode(JSC::jsUndefined()); }); - globalObject->vm().drainMicrotasks(); - auto* cell = JSC::JSValue::decode(JSValue0).asCell(); if (JSC::JSPromise* promise = JSC::jsDynamicCast<JSC::JSPromise*>(globalObject->vm(), cell)) { promise->performPromiseThen(globalObject, resolverFunction, rejecterFunction, JSC::jsUndefined()); } else if (JSC::JSInternalPromise* promise = JSC::jsDynamicCast<JSC::JSInternalPromise*>(globalObject->vm(), cell)) { diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index 8073ad879..890a74554 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -635,12 +635,8 @@ pub const VirtualMachine = struct { this.tasks.ensureUnusedCapacity(add) catch unreachable; { - var writable = std.mem.sliceAsBytes(this.tasks.writableSlice(0)); - const readable = std.mem.sliceAsBytes(this.concurrent_tasks.readableSlice(0)); - @memcpy(writable.ptr, readable.ptr, @minimum(writable.len, readable.len)); - this.tasks.count += add; - this.concurrent_tasks.head = 0; - this.concurrent_tasks.count = 0; + this.tasks.writeAssumeCapacity(this.concurrent_tasks.readableSlice(0)); + this.concurrent_tasks.discard(this.concurrent_tasks.count); } _ = this.pending_tasks_count.fetchAdd(add, .Monotonic); diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig index 7043769fa..b92d0882f 100644 --- a/src/javascript/jsc/webcore/response.zig +++ b/src/javascript/jsc/webcore/response.zig @@ -1756,13 +1756,13 @@ pub const Blob = struct { const system_error_ = this.system_error; var store = this.store; - bun.default_allocator.destroy(this); if (system_error_) |err| { cb(cb_ctx, -1, 0, err, globalThis); } else { cb(cb_ctx, fd, _size, null, globalThis); } store.deref(); + bun.default_allocator.destroy(this); } fn _runAsync(this: *OpenAndStatFile) void { @@ -1770,12 +1770,15 @@ pub const Blob = struct { if (this.file_store.pathlike == .fd) { this.opened_fd = this.file_store.pathlike.fd; } - const fd = - if (this.opened_fd == null_fd) + const fd = if (this.opened_fd == null_fd) this.getFd() catch return else this.opened_fd; + this.doWithFd(fd); + } + + pub fn doWithFd(this: *OpenAndStatFile, fd: JSC.Node.FileDescriptor) void { const stat: std.os.Stat = switch (JSC.Node.Syscall.fstat(fd)) { .result => |result| result, .err => |err| { diff --git a/src/mimalloc_arena.zig b/src/mimalloc_arena.zig index 70ce813a4..8b131c84a 100644 --- a/src/mimalloc_arena.zig +++ b/src/mimalloc_arena.zig @@ -11,6 +11,14 @@ const assert = std.debug.assert; pub const Arena = struct { heap: ?*mimalloc.mi_heap_t = null, + /// Internally, mimalloc calls mi_heap_get_default() + /// to get the default heap. + /// It uses pthread_getspecific to do that. + /// We can save those extra calls if we just do it once in here + pub fn getThreadlocalDefault() Allocator { + return Allocator{ .ptr = mimalloc.mi_heap_get_default(), .vtable = &c_allocator_vtable }; + } + pub fn backingAllocator(this: Arena) Allocator { var arena = Arena{ .heap = this.heap.?.backing() }; return arena.allocator(); diff --git a/src/thread_pool.zig b/src/thread_pool.zig index 704d4783e..9608c1bca 100644 --- a/src/thread_pool.zig +++ b/src/thread_pool.zig @@ -213,7 +213,7 @@ fn _wait(self: *ThreadPool, _is_waking: bool, comptime sleep_on_idle: bool) erro var is_idle = false; var is_waking = _is_waking; var sync = @bitCast(Sync, self.sync.load(.Monotonic)); - var idle_network_ticks: u32 = 0; + var checked_count: usize = 0; while (true) { if (sync.state == .shutdown) return error.Shutdown; @@ -275,13 +275,7 @@ fn _wait(self: *ThreadPool, _is_waking: bool, comptime sleep_on_idle: bool) erro const HTTP = @import("http"); io.tick() catch {}; - const end_count = HTTP.AsyncHTTP.active_requests_count.loadUnchecked(); - - if (end_count > 0) { - if (comptime sleep_on_idle) { - idle_network_ticks = 0; - } - + if (HTTP.AsyncHTTP.active_requests_count.loadUnchecked() > 0) { var remaining_ticks: isize = 5; while (remaining_ticks > 0 and HTTP.AsyncHTTP.active_requests_count.loadUnchecked() > HTTP.AsyncHTTP.max_simultaneous_requests) : (remaining_ticks -= 1) { @@ -290,26 +284,12 @@ fn _wait(self: *ThreadPool, _is_waking: bool, comptime sleep_on_idle: bool) erro } } - const idle = HTTP.AsyncHTTP.active_requests_count.loadUnchecked() == 0; - if (sleep_on_idle and io.hasNoWork()) { - if (comptime @hasField(AsyncIO, "pending_count")) { + if (checked_count > 99999) { HTTP.cleanup(false); self.idle_event.waitFor(comptime std.time.ns_per_s * 60); } else { - idle_network_ticks += @as(u32, @boolToInt(idle)); - - // If it's been roughly 2ms since the last network request, go to sleep! - // this is 4ms because run_for_ns runs for 10 microseconds - // 10 microseconds * 400 == 4ms - if (idle_network_ticks > 400) { - idle_network_ticks = 0; - // force(true) causes an assertion failure - // judging from reading mimalloc's code, - // it should only be used when the thread is about to shutdown - HTTP.cleanup(false); - self.idle_event.wait(); - } + checked_count += 1; } } |