mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-30 15:41:48 +00:00
Optimize memory usage
The two bigger changes here are: 1- The http_client has been moved from the Session to the Browser, allowing its connection pool to be re-used across multiple sessions 2- The browser now has a page_arena which is used for all page-level allocation and which can be re-used between pages (currently retains 1MB of memory). Previously, pages uses an arena that was tied to the lifetime of the page, thus it could not be re-used. Using the Bench allocator for zig-js-runtime, allocated bytes went from 1347037879 to 834932438 (in a RUNS=1000 of puppeteer demo). Various other changes to try to simplify the API and remove the possibility of invalid states. For example, session.newPage() now includes the logic for page.start() so that there should now never be a page that wasn't started.
This commit is contained in:
@@ -55,13 +55,15 @@ pub fn CDPT(comptime TypeProvider: type) type {
|
||||
allocator: Allocator,
|
||||
|
||||
// The active browser
|
||||
browser: ?Browser = null,
|
||||
browser: Browser,
|
||||
|
||||
target_id_gen: TargetIdGen = .{},
|
||||
session_id_gen: SessionIdGen = .{},
|
||||
browser_context_id_gen: BrowserContextIdGen = .{},
|
||||
|
||||
browser_context: ?BrowserContext(Self),
|
||||
browser_context: ?*BrowserContext(Self),
|
||||
|
||||
browser_context_pool: std.heap.MemoryPool(BrowserContext(Self)),
|
||||
|
||||
// Re-used arena for processing a message. We're assuming that we're getting
|
||||
// 1 message at a time.
|
||||
@@ -77,15 +79,19 @@ pub fn CDPT(comptime TypeProvider: type) type {
|
||||
.client = client,
|
||||
.allocator = allocator,
|
||||
.browser_context = null,
|
||||
.browser = Browser.init(allocator, loop),
|
||||
.message_arena = std.heap.ArenaAllocator.init(allocator),
|
||||
.browser_context_pool = std.heap.MemoryPool(BrowserContext(Self)).init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (self.browser_context) |*bc| {
|
||||
if (self.browser_context) |bc| {
|
||||
bc.deinit();
|
||||
}
|
||||
self.browser.deinit();
|
||||
self.message_arena.deinit();
|
||||
self.browser_context_pool.deinit();
|
||||
}
|
||||
|
||||
pub fn handleMessage(self: *Self, msg: []const u8) bool {
|
||||
@@ -119,7 +125,7 @@ pub fn CDPT(comptime TypeProvider: type) type {
|
||||
.cdp = self,
|
||||
.arena = arena,
|
||||
.sender = sender,
|
||||
.browser_context = if (self.browser_context) |*bc| bc else null,
|
||||
.browser_context = if (self.browser_context) |bc| bc else null,
|
||||
};
|
||||
|
||||
// See dispatchStartupCommand for more info on this.
|
||||
@@ -213,7 +219,7 @@ pub fn CDPT(comptime TypeProvider: type) type {
|
||||
}
|
||||
|
||||
fn isValidSessionId(self: *const Self, input_session_id: []const u8) bool {
|
||||
const browser_context = &(self.browser_context orelse return false);
|
||||
const browser_context = self.browser_context orelse return false;
|
||||
const session_id = browser_context.session_id orelse return false;
|
||||
return std.mem.eql(u8, session_id, input_session_id);
|
||||
}
|
||||
@@ -224,20 +230,21 @@ pub fn CDPT(comptime TypeProvider: type) type {
|
||||
}
|
||||
const browser_context_id = self.browser_context_id_gen.next();
|
||||
|
||||
// is this safe?
|
||||
self.browser_context = undefined;
|
||||
errdefer self.browser_context = null;
|
||||
try BrowserContext(Self).init(&self.browser_context.?, browser_context_id, self);
|
||||
const browser_context = try self.browser_context_pool.create();
|
||||
errdefer self.browser_context_pool.destroy(browser_context);
|
||||
|
||||
try BrowserContext(Self).init(browser_context, browser_context_id, self);
|
||||
self.browser_context = browser_context;
|
||||
return browser_context_id;
|
||||
}
|
||||
|
||||
pub fn disposeBrowserContext(self: *Self, browser_context_id: []const u8) bool {
|
||||
const bc = &(self.browser_context orelse return false);
|
||||
const bc = self.browser_context orelse return false;
|
||||
if (std.mem.eql(u8, bc.id, browser_context_id) == false) {
|
||||
return false;
|
||||
}
|
||||
bc.deinit();
|
||||
self.browser_context_pool.destroy(bc);
|
||||
self.browser_context = null;
|
||||
return true;
|
||||
}
|
||||
@@ -257,7 +264,6 @@ pub fn BrowserContext(comptime CDP_T: type) type {
|
||||
id: []const u8,
|
||||
cdp: *CDP_T,
|
||||
|
||||
browser: CDP_T.Browser,
|
||||
// Represents the browser session. There is no equivalent in CDP. For
|
||||
// all intents and purpose, from CDP's point of view our Browser and
|
||||
// our Session more or less maps to a BrowserContext. THIS HAS ZERO
|
||||
@@ -294,22 +300,17 @@ pub fn BrowserContext(comptime CDP_T: type) type {
|
||||
self.* = .{
|
||||
.id = id,
|
||||
.cdp = cdp,
|
||||
.browser = undefined,
|
||||
.session = undefined,
|
||||
.target_id = null,
|
||||
.session_id = null,
|
||||
.url = URL_BASE,
|
||||
.security_origin = URL_BASE,
|
||||
.secure_context_type = "Secure", // TODO = enum
|
||||
.loader_id = LOADER_ID,
|
||||
.session = try cdp.browser.newSession(self),
|
||||
.page_life_cycle_events = false, // TODO; Target based value
|
||||
.node_list = dom.NodeList.init(cdp.allocator),
|
||||
.node_search_list = dom.NodeSearchList.init(cdp.allocator),
|
||||
};
|
||||
|
||||
self.browser = CDP_T.Browser.init(cdp.allocator, cdp.loop);
|
||||
errdefer self.browser.deinit();
|
||||
self.session = try self.browser.newSession(self);
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
@@ -318,7 +319,6 @@ pub fn BrowserContext(comptime CDP_T: type) type {
|
||||
s.deinit();
|
||||
}
|
||||
self.node_search_list.deinit();
|
||||
self.browser.deinit();
|
||||
}
|
||||
|
||||
pub fn reset(self: *Self) void {
|
||||
@@ -447,7 +447,7 @@ pub fn Command(comptime CDP_T: type, comptime Sender: type) type {
|
||||
|
||||
pub fn createBrowserContext(self: *Self) !*BrowserContext(CDP_T) {
|
||||
_ = try self.cdp.createBrowserContext();
|
||||
self.browser_context = &self.cdp.browser_context.?;
|
||||
self.browser_context = self.cdp.browser_context.?;
|
||||
return self.browser_context.?;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,15 +116,8 @@ fn createTarget(cmd: anytype) !void {
|
||||
// if target_id is null, we should never have a session_id
|
||||
std.debug.assert(bc.session_id == null);
|
||||
|
||||
const page = try bc.session.createPage();
|
||||
const target_id = cmd.cdp.target_id_gen.next();
|
||||
|
||||
// change CDP state
|
||||
bc.url = "about:blank";
|
||||
bc.security_origin = "://";
|
||||
bc.secure_context_type = "InsecureScheme";
|
||||
bc.loader_id = LOADER_ID;
|
||||
|
||||
// start the js env
|
||||
const aux_data = try std.fmt.allocPrint(
|
||||
cmd.arena,
|
||||
@@ -132,7 +125,13 @@ fn createTarget(cmd: anytype) !void {
|
||||
"{{\"isDefault\":true,\"type\":\"default\",\"frameId\":\"{s}\"}}",
|
||||
.{target_id},
|
||||
);
|
||||
try page.start(aux_data);
|
||||
_ = try bc.session.createPage(aux_data);
|
||||
|
||||
// change CDP state
|
||||
bc.url = "about:blank";
|
||||
bc.security_origin = "://";
|
||||
bc.secure_context_type = "InsecureScheme";
|
||||
bc.loader_id = LOADER_ID;
|
||||
|
||||
// send targetCreated event
|
||||
// TODO: should this only be sent when Target.setDiscoverTargets
|
||||
@@ -213,7 +212,7 @@ fn closeTarget(cmd: anytype) !void {
|
||||
bc.session_id = null;
|
||||
}
|
||||
|
||||
bc.session.currentPage().?.end();
|
||||
bc.session.removePage();
|
||||
bc.target_id = null;
|
||||
}
|
||||
|
||||
@@ -508,7 +507,7 @@ test "cdp.target: closeTarget" {
|
||||
}
|
||||
|
||||
// pretend we createdTarget first
|
||||
_ = try bc.session.createPage();
|
||||
_ = try bc.session.createPage(null);
|
||||
bc.target_id = "TID-A";
|
||||
{
|
||||
try testing.expectError(error.UnknownTargetId, ctx.processMessage(.{ .id = 10, .method = "Target.closeTarget", .params = .{ .targetId = "TID-8" } }));
|
||||
@@ -539,7 +538,7 @@ test "cdp.target: attachToTarget" {
|
||||
}
|
||||
|
||||
// pretend we createdTarget first
|
||||
_ = try bc.session.createPage();
|
||||
_ = try bc.session.createPage(null);
|
||||
bc.target_id = "TID-B";
|
||||
{
|
||||
try testing.expectError(error.UnknownTargetId, ctx.processMessage(.{ .id = 10, .method = "Target.attachToTarget", .params = .{ .targetId = "TID-8" } }));
|
||||
@@ -583,7 +582,7 @@ test "cdp.target: getTargetInfo" {
|
||||
}
|
||||
|
||||
// pretend we createdTarget first
|
||||
_ = try bc.session.createPage();
|
||||
_ = try bc.session.createPage(null);
|
||||
bc.target_id = "TID-A";
|
||||
{
|
||||
try testing.expectError(error.UnknownTargetId, ctx.processMessage(.{ .id = 10, .method = "Target.getTargetInfo", .params = .{ .targetId = "TID-8" } }));
|
||||
|
||||
@@ -56,17 +56,21 @@ const Session = struct {
|
||||
return &(self.page orelse return null);
|
||||
}
|
||||
|
||||
pub fn createPage(self: *Session) !*Page {
|
||||
pub fn createPage(self: *Session, aux_data: ?[]const u8) !*Page {
|
||||
if (self.page != null) {
|
||||
return error.MockBrowserPageAlreadyExists;
|
||||
}
|
||||
self.page = .{
|
||||
.session = self,
|
||||
.allocator = self.allocator,
|
||||
.aux_data = try self.allocator.dupe(u8, aux_data orelse ""),
|
||||
};
|
||||
return &self.page.?;
|
||||
}
|
||||
|
||||
pub fn removePage(self: *Session) void {
|
||||
self.page = null;
|
||||
}
|
||||
|
||||
pub fn callInspector(self: *Session, msg: []const u8) void {
|
||||
_ = self;
|
||||
_ = msg;
|
||||
@@ -75,7 +79,6 @@ const Session = struct {
|
||||
|
||||
const Page = struct {
|
||||
session: *Session,
|
||||
allocator: Allocator,
|
||||
aux_data: []const u8 = "",
|
||||
doc: ?*parser.Document = null,
|
||||
|
||||
@@ -84,14 +87,6 @@ const Page = struct {
|
||||
_ = url;
|
||||
_ = aux_data;
|
||||
}
|
||||
|
||||
pub fn start(self: *Page, aux_data: []const u8) !void {
|
||||
self.aux_data = try self.allocator.dupe(u8, aux_data);
|
||||
}
|
||||
|
||||
pub fn end(self: *Page) void {
|
||||
self.session.page = null;
|
||||
}
|
||||
};
|
||||
|
||||
const Client = struct {
|
||||
@@ -152,13 +147,15 @@ const TestContext = struct {
|
||||
};
|
||||
pub fn loadBrowserContext(self: *TestContext, opts: BrowserContextOpts) !*main.BrowserContext(TestCDP) {
|
||||
var c = self.cdp();
|
||||
if (c.browser_context) |*bc| {
|
||||
c.browser.session = null;
|
||||
|
||||
if (c.browser_context) |bc| {
|
||||
bc.deinit();
|
||||
c.browser_context = null;
|
||||
}
|
||||
|
||||
_ = try c.createBrowserContext();
|
||||
var bc = &c.browser_context.?;
|
||||
var bc = c.browser_context.?;
|
||||
|
||||
if (opts.id) |id| {
|
||||
bc.id = id;
|
||||
|
||||
Reference in New Issue
Block a user