diff options
author | 2022-02-01 20:47:35 -0800 | |
---|---|---|
committer | 2022-02-01 20:47:35 -0800 | |
commit | 213960a04a22bed4d0ffb3cdc9e20439bd1dcc10 (patch) | |
tree | bbbc7d983496fc6ff8a4c0136bfbaaceb0f30eb9 | |
parent | 170e58a99db61536c345ce2dd41693aea57cd359 (diff) | |
download | bun-213960a04a22bed4d0ffb3cdc9e20439bd1dcc10.tar.gz bun-213960a04a22bed4d0ffb3cdc9e20439bd1dcc10.tar.zst bun-213960a04a22bed4d0ffb3cdc9e20439bd1dcc10.zip |
Limit the number of pooled objects
-rw-r--r-- | src/http/async_bio.zig | 13 | ||||
-rw-r--r-- | src/http/async_message.zig | 2 | ||||
-rw-r--r-- | src/http/async_socket.zig | 9 | ||||
-rw-r--r-- | src/http_client_async.zig | 7 | ||||
-rw-r--r-- | src/install/npm.zig | 2 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/exports.zig | 1 | ||||
-rw-r--r-- | src/javascript/jsc/test/jest.zig | 2 | ||||
-rw-r--r-- | src/javascript/jsc/webcore/response.zig | 4 | ||||
-rw-r--r-- | src/js_ast.zig | 1 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 2 | ||||
-rw-r--r-- | src/mimalloc_arena.zig | 4 | ||||
-rw-r--r-- | src/pool.zig | 28 |
12 files changed, 61 insertions, 14 deletions
diff --git a/src/http/async_bio.zig b/src/http/async_bio.zig index 7ac9bc92d..765c841e0 100644 --- a/src/http/async_bio.zig +++ b/src/http/async_bio.zig @@ -22,7 +22,7 @@ const Packet = struct { min: u32 = 0, owned_slice: []u8 = &[_]u8{}, - pub const Pool = ObjectPool(Packet, null, false); + pub const Pool = ObjectPool(Packet, null, false, 32); }; bio: *boring.BIO = undefined, @@ -34,7 +34,7 @@ pending_reads: u32 = 0, pending_sends: u32 = 0, recv_buffer: ?*BufferPool.Node = null, - +big_buffer: std.ArrayListUnmanaged(u8) = .{}, send_buffer: ?*BufferPool.Node = null, write_error: c_int = 0, @@ -231,10 +231,14 @@ pub fn scheduleSocketRead(this: *AsyncBIO, min: u32) void { this.recv_buffer = BufferPool.get(getAllocator()); } + this.scheduleSocketReadBuf(min, this.readBuf()); +} + +pub fn scheduleSocketReadBuf(this: *AsyncBIO, min: u32, buf: []u8) void { var packet = Packet.Pool.get(getAllocator()); packet.data.min = @truncate(u32, min); - AsyncIO.global.recv(*AsyncBIO, this, doSocketRead, &packet.data.completion, this.socket_fd, this.readBuf()); + AsyncIO.global.recv(*AsyncBIO, this, doSocketRead, &packet.data.completion, this.socket_fd, buf); } pub fn scheduleSocketWrite(this: *AsyncBIO, buf: []const u8) void { @@ -318,9 +322,6 @@ pub const Bio = struct { return -1; } - const remaining_in_send_buffer = buffer_pool_len - this.bio_write_offset; - const total_remaining = remaining_in_send_buffer - @minimum(remaining_in_send_buffer, len); - if (this.send_buffer == null) { this.send_buffer = BufferPool.get(getAllocator()); } diff --git a/src/http/async_message.zig b/src/http/async_message.zig index d68c58cb0..5fce3488b 100644 --- a/src/http/async_message.zig +++ b/src/http/async_message.zig @@ -4,7 +4,7 @@ const AsyncIO = @import("io"); pub const buffer_pool_len = std.math.maxInt(u16) - 64; pub const BufferPoolBytes = [buffer_pool_len]u8; -pub const BufferPool = ObjectPool(BufferPoolBytes, null, false); +pub const BufferPool = ObjectPool(BufferPoolBytes, null, false, 4); const AsyncMessage = @This(); diff --git a/src/http/async_socket.zig b/src/http/async_socket.zig index e5274df76..062a645de 100644 --- a/src/http/async_socket.zig +++ b/src/http/async_socket.zig @@ -603,6 +603,15 @@ pub const SSL = struct { this.completed_connect = true; this.next_handshake_state = .none; this.doPeek(); + if (extremely_verbose) { + const version = std.mem.span(boring.SSL_get_version(this.ssl)); + var hostname = std.mem.span(std.mem.sliceTo(&this.hostname, 0)); + Output.prettyErrorln("[{s}] Handshake complete.\n[{s}] TLS Version: {s}", .{ + hostname, + hostname, + version, + }); + } } fn doPeek(this: *SSL) void { diff --git a/src/http_client_async.zig b/src/http_client_async.zig index ada77b8c4..d81d0bf4e 100644 --- a/src/http_client_async.zig +++ b/src/http_client_async.zig @@ -28,7 +28,7 @@ const AsyncMessage = @import("./http/async_message.zig"); const AsyncBIO = @import("./http/async_bio.zig"); const AsyncSocket = @import("./http/async_socket.zig"); const ZlibPool = @import("./http/zlib.zig"); -const URLBufferPool = ObjectPool([4096]u8, null, false); +const URLBufferPool = ObjectPool([4096]u8, null, false, 10); // This becomes Arena.allocator pub var default_allocator: std.mem.Allocator = undefined; @@ -53,6 +53,10 @@ pub inline fn getAllocator() std.mem.Allocator { return default_allocator; } +pub inline fn cleanup(force: bool) void { + default_arena.gc(force); +} + pub const Headers = @import("./http/headers.zig"); pub const SOCKET_FLAGS: u32 = if (Environment.isLinux) @@ -558,6 +562,7 @@ pub fn sendAsync(this: *HTTPClient, body: []const u8, body_out_str: *MutableStri pub fn send(this: *HTTPClient, body: []const u8, body_out_str: *MutableString) !picohttp.Response { defer if (@enumToInt(this.stage) > @enumToInt(Stage.pending)) this.socket.deinit(); + // this prevents stack overflow redirect: while (this.remaining_redirect_count >= -1) { if (@enumToInt(this.stage) > @enumToInt(Stage.pending)) this.socket.deinit(); diff --git a/src/install/npm.zig b/src/install/npm.zig index 3f3f8596a..61e2b064c 100644 --- a/src/install/npm.zig +++ b/src/install/npm.zig @@ -38,7 +38,7 @@ pub const Registry = struct { token: string = "", auth: string = "", - pub const BodyPool = ObjectPool(MutableString, MutableString.init2048, true); + pub const BodyPool = ObjectPool(MutableString, MutableString.init2048, true, 8); pub const Scope = struct { name: string = "", diff --git a/src/javascript/jsc/bindings/exports.zig b/src/javascript/jsc/bindings/exports.zig index 9feb6497d..26ba4cd3c 100644 --- a/src/javascript/jsc/bindings/exports.zig +++ b/src/javascript/jsc/bindings/exports.zig @@ -958,6 +958,7 @@ pub const ZigConsoleClient = struct { } }.init, true, + 16, ); }; diff --git a/src/javascript/jsc/test/jest.zig b/src/javascript/jsc/test/jest.zig index 0c7392623..ea7d092b4 100644 --- a/src/javascript/jsc/test/jest.zig +++ b/src/javascript/jsc/test/jest.zig @@ -784,7 +784,7 @@ pub const DescribeScope = struct { this.tests.deinit(getAllocator(ctx)); } - const ScopeStack = ObjectPool(std.ArrayListUnmanaged(*DescribeScope), null, true); + const ScopeStack = ObjectPool(std.ArrayListUnmanaged(*DescribeScope), null, true, 16); // pub fn runBeforeAll(this: *DescribeScope, ctx: js.JSContextRef, exception: js.ExceptionRef) bool { // var scopes = ScopeStack.get(default_allocator); diff --git a/src/javascript/jsc/webcore/response.zig b/src/javascript/jsc/webcore/response.zig index 00b78f41c..1d5281626 100644 --- a/src/javascript/jsc/webcore/response.zig +++ b/src/javascript/jsc/webcore/response.zig @@ -463,8 +463,8 @@ pub const Fetch = struct { reject: js.JSObjectRef = null, context: FetchTaskletContext = undefined, - const Pool = ObjectPool(FetchTasklet, init, true); - const BodyPool = ObjectPool(MutableString, MutableString.init2048, true); + const Pool = ObjectPool(FetchTasklet, init, true, 32); + const BodyPool = ObjectPool(MutableString, MutableString.init2048, true, 8); pub const FetchTaskletContext = struct { tasklet: *FetchTasklet, }; diff --git a/src/js_ast.zig b/src/js_ast.zig index b1b2e26f4..1a8b6dd35 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -3851,6 +3851,7 @@ pub const SymbolPool = ObjectPool( } }.init, true, + 4, ); pub const Ast = struct { diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index bb95d9934..58e7f5a2a 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -1755,7 +1755,7 @@ const StringVoidMap = struct { Pool.release(node); } - pub const Pool = ObjectPool(StringVoidMap, init, true); + pub const Pool = ObjectPool(StringVoidMap, init, true, 32); pub const Node = Pool.Node; }; const RefCtx = @import("../ast/base.zig").RefCtx; diff --git a/src/mimalloc_arena.zig b/src/mimalloc_arena.zig index 9fa0b586e..db7a6c368 100644 --- a/src/mimalloc_arena.zig +++ b/src/mimalloc_arena.zig @@ -27,6 +27,10 @@ pub const Arena = struct { return Arena{ .heap = mimalloc.mi_heap_new() orelse return error.OutOfMemory }; } + pub fn gc(this: Arena, force: bool) void { + mimalloc.mi_heap_collect(this.heap, force); + } + const MI_MAX_ALIGN_SIZE = 16; inline fn mi_malloc_satisfies_alignment(alignment: usize, size: usize) bool { return (alignment == @sizeOf(*anyopaque) or (alignment == MI_MAX_ALIGN_SIZE and size > (MI_MAX_ALIGN_SIZE / 2))); diff --git a/src/pool.zig b/src/pool.zig index 36a3d9d0a..084fa9dd4 100644 --- a/src/pool.zig +++ b/src/pool.zig @@ -7,6 +7,7 @@ fn SinglyLinkedList(comptime T: type, comptime Parent: type) type { /// Node inside the linked list wrapping the actual data. pub const Node = struct { next: ?*Node = null, + allocator: std.mem.Allocator, data: T, pub const Data = T; @@ -106,20 +107,30 @@ fn SinglyLinkedList(comptime T: type, comptime Parent: type) type { }; } -pub fn ObjectPool(comptime Type: type, comptime Init: (?fn (allocator: std.mem.Allocator) anyerror!Type), comptime threadsafe: bool) type { +const log_allocations = true; + +pub fn ObjectPool( + comptime Type: type, + comptime Init: (?fn (allocator: std.mem.Allocator) anyerror!Type), + comptime threadsafe: bool, + comptime max_count: comptime_int, +) type { return struct { const Pool = @This(); const LinkedList = SinglyLinkedList(Type, Pool); pub const Node = LinkedList.Node; + const MaxCountInt = std.math.IntFittingRange(0, max_count); const Data = if (threadsafe) struct { pub threadlocal var list: LinkedList = undefined; pub threadlocal var loaded: bool = false; + pub threadlocal var count: MaxCountInt = 0; } else struct { pub var list: LinkedList = undefined; pub var loaded: bool = false; + pub var count: MaxCountInt = 0; }; const data = Data; @@ -128,12 +139,16 @@ pub fn ObjectPool(comptime Type: type, comptime Init: (?fn (allocator: std.mem.A if (data.loaded) { if (data.list.popFirst()) |node| { if (comptime std.meta.trait.isContainer(Type) and @hasDecl(Type, "reset")) node.data.reset(); + if (comptime max_count > 0) data.count -|= 1; return node; } } + if (comptime log_allocations) std.io.getStdErr().writeAll(comptime std.fmt.comptimePrint("Allocate {s} - {d} bytes\n", .{ @typeName(Type), @sizeOf(Type) })) catch {}; + var new_node = allocator.create(LinkedList.Node) catch unreachable; new_node.* = LinkedList.Node{ + .allocator = allocator, .data = if (comptime Init) |init_| (init_( allocator, @@ -146,6 +161,17 @@ pub fn ObjectPool(comptime Type: type, comptime Init: (?fn (allocator: std.mem.A } pub fn release(node: *LinkedList.Node) void { + if (comptime max_count > 0) { + if (data.count >= max_count) { + if (comptime log_allocations) std.io.getStdErr().writeAll(comptime std.fmt.comptimePrint("Free {s} - {d} bytes\n", .{ @typeName(Type), @sizeOf(Type) })) catch {}; + if (comptime std.meta.trait.isContainer(Type) and @hasDecl(Type, "deinit")) node.data.deinit(); + node.allocator.destroy(node); + return; + } + } + + if (comptime max_count > 0) data.count +|= 1; + if (data.loaded) { data.list.prepend(node); return; |