aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-02-01 20:47:35 -0800
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-02-01 20:47:35 -0800
commit213960a04a22bed4d0ffb3cdc9e20439bd1dcc10 (patch)
treebbbc7d983496fc6ff8a4c0136bfbaaceb0f30eb9
parent170e58a99db61536c345ce2dd41693aea57cd359 (diff)
downloadbun-213960a04a22bed4d0ffb3cdc9e20439bd1dcc10.tar.gz
bun-213960a04a22bed4d0ffb3cdc9e20439bd1dcc10.tar.zst
bun-213960a04a22bed4d0ffb3cdc9e20439bd1dcc10.zip
Limit the number of pooled objects
-rw-r--r--src/http/async_bio.zig13
-rw-r--r--src/http/async_message.zig2
-rw-r--r--src/http/async_socket.zig9
-rw-r--r--src/http_client_async.zig7
-rw-r--r--src/install/npm.zig2
-rw-r--r--src/javascript/jsc/bindings/exports.zig1
-rw-r--r--src/javascript/jsc/test/jest.zig2
-rw-r--r--src/javascript/jsc/webcore/response.zig4
-rw-r--r--src/js_ast.zig1
-rw-r--r--src/js_parser/js_parser.zig2
-rw-r--r--src/mimalloc_arena.zig4
-rw-r--r--src/pool.zig28
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;