Skip to content

Commit 477a5e5

Browse files
authored
Merge pull request #1088 from lightpanda-io/nonblocking_dynamic_imports
nonblocking dynamic imports
2 parents 2a15122 + 2aa4b03 commit 477a5e5

File tree

6 files changed

+376
-76
lines changed

6 files changed

+376
-76
lines changed

src/browser/ScriptManager.zig

Lines changed: 112 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ client: *Http.Client,
6767
allocator: Allocator,
6868
buffer_pool: BufferPool,
6969
script_pool: std.heap.MemoryPool(PendingScript),
70+
async_module_pool: std.heap.MemoryPool(AsyncModule),
7071

7172
const OrderList = std.DoublyLinkedList;
7273

@@ -85,13 +86,15 @@ pub fn init(browser: *Browser, page: *Page) ScriptManager {
8586
.static_scripts_done = false,
8687
.buffer_pool = BufferPool.init(allocator, 5),
8788
.script_pool = std.heap.MemoryPool(PendingScript).init(allocator),
89+
.async_module_pool = std.heap.MemoryPool(AsyncModule).init(allocator),
8890
};
8991
}
9092

9193
pub fn deinit(self: *ScriptManager) void {
9294
self.reset();
9395
self.buffer_pool.deinit();
9496
self.script_pool.deinit();
97+
self.async_module_pool.deinit();
9598
}
9699

97100
pub fn reset(self: *ScriptManager) void {
@@ -257,7 +260,7 @@ pub fn addFromElement(self: *ScriptManager, element: *parser.Element) !void {
257260
// Unlike external modules which can only ever be executed after releasing an
258261
// http handle, these are executed without there necessarily being a free handle.
259262
// Thus, Http/Client.zig maintains a dedicated handle for these calls.
260-
pub fn blockingGet(self: *ScriptManager, url: [:0]const u8) !BlockingResult {
263+
pub fn blockingGet(self: *ScriptManager, url: [:0]const u8) !GetResult {
261264
std.debug.assert(self.is_blocking == false);
262265

263266
self.is_blocking = true;
@@ -303,6 +306,34 @@ pub fn blockingGet(self: *ScriptManager, url: [:0]const u8) !BlockingResult {
303306
}
304307
}
305308

309+
pub fn getAsyncModule(self: *ScriptManager, url: [:0]const u8, cb: AsyncModule.Callback, cb_data: *anyopaque) !void {
310+
const async = try self.async_module_pool.create();
311+
errdefer self.async_module_pool.destroy(async);
312+
313+
async.* = .{
314+
.cb = cb,
315+
.manager = self,
316+
.cb_data = cb_data,
317+
};
318+
319+
var headers = try self.client.newHeaders();
320+
try self.page.requestCookie(.{}).headersForRequest(self.page.arena, url, &headers);
321+
322+
try self.client.request(.{
323+
.url = url,
324+
.method = .GET,
325+
.headers = headers,
326+
.cookie_jar = self.page.cookie_jar,
327+
.ctx = async,
328+
.resource_type = .script,
329+
.start_callback = if (log.enabled(.http, .debug)) AsyncModule.startCallback else null,
330+
.header_callback = AsyncModule.headerCallback,
331+
.data_callback = AsyncModule.dataCallback,
332+
.done_callback = AsyncModule.doneCallback,
333+
.error_callback = AsyncModule.errorCallback,
334+
});
335+
}
336+
306337
pub fn staticScriptsDone(self: *ScriptManager) void {
307338
std.debug.assert(self.static_scripts_done == false);
308339
self.static_scripts_done = true;
@@ -595,7 +626,7 @@ const Script = struct {
595626
.javascript => _ = js_context.eval(content, url) catch break :blk false,
596627
.module => {
597628
// We don't care about waiting for the evaluation here.
598-
_ = js_context.module(content, url, cacheable) catch break :blk false;
629+
js_context.module(false, content, url, cacheable) catch break :blk false;
599630
},
600631
}
601632
break :blk true;
@@ -761,7 +792,7 @@ const Blocking = struct {
761792
const State = union(enum) {
762793
running: void,
763794
err: anyerror,
764-
done: BlockingResult,
795+
done: GetResult,
765796
};
766797

767798
fn startCallback(transfer: *Http.Transfer) !void {
@@ -815,19 +846,93 @@ const Blocking = struct {
815846
fn errorCallback(ctx: *anyopaque, err: anyerror) void {
816847
var self: *Blocking = @ptrCast(@alignCast(ctx));
817848
self.state = .{ .err = err };
818-
self.buffer_pool.release(self.buffer);
849+
if (self.buffer.items.len > 0) {
850+
self.buffer_pool.release(self.buffer);
851+
}
852+
}
853+
};
854+
855+
pub const AsyncModule = struct {
856+
cb: Callback,
857+
cb_data: *anyopaque,
858+
manager: *ScriptManager,
859+
buffer: std.ArrayListUnmanaged(u8) = .{},
860+
861+
pub const Callback = *const fn (ptr: *anyopaque, result: anyerror!GetResult) void;
862+
863+
fn startCallback(transfer: *Http.Transfer) !void {
864+
log.debug(.http, "script fetch start", .{ .req = transfer, .async = true });
865+
}
866+
867+
fn headerCallback(transfer: *Http.Transfer) !void {
868+
const header = &transfer.response_header.?;
869+
log.debug(.http, "script header", .{
870+
.req = transfer,
871+
.async = true,
872+
.status = header.status,
873+
.content_type = header.contentType(),
874+
});
875+
876+
if (header.status != 200) {
877+
return error.InvalidStatusCode;
878+
}
879+
880+
var self: *AsyncModule = @ptrCast(@alignCast(transfer.ctx));
881+
self.buffer = self.manager.buffer_pool.get();
882+
}
883+
884+
fn dataCallback(transfer: *Http.Transfer, data: []const u8) !void {
885+
// too verbose
886+
// log.debug(.http, "script data chunk", .{
887+
// .req = transfer,
888+
// .blocking = true,
889+
// });
890+
891+
var self: *AsyncModule = @ptrCast(@alignCast(transfer.ctx));
892+
self.buffer.appendSlice(self.manager.allocator, data) catch |err| {
893+
log.err(.http, "SM.dataCallback", .{
894+
.err = err,
895+
.len = data.len,
896+
.ascyn = true,
897+
.transfer = transfer,
898+
});
899+
return err;
900+
};
901+
}
902+
903+
fn doneCallback(ctx: *anyopaque) !void {
904+
var self: *AsyncModule = @ptrCast(@alignCast(ctx));
905+
defer self.manager.async_module_pool.destroy(self);
906+
self.cb(self.cb_data, .{
907+
.buffer = self.buffer,
908+
.buffer_pool = &self.manager.buffer_pool,
909+
});
910+
}
911+
912+
fn errorCallback(ctx: *anyopaque, err: anyerror) void {
913+
var self: *AsyncModule = @ptrCast(@alignCast(ctx));
914+
915+
if (err != error.Abort) {
916+
self.cb(self.cb_data, err);
917+
}
918+
919+
if (self.buffer.items.len > 0) {
920+
self.manager.buffer_pool.release(self.buffer);
921+
}
922+
923+
self.manager.async_module_pool.destroy(self);
819924
}
820925
};
821926

822-
pub const BlockingResult = struct {
927+
pub const GetResult = struct {
823928
buffer: std.ArrayListUnmanaged(u8),
824929
buffer_pool: *BufferPool,
825930

826-
pub fn deinit(self: *BlockingResult) void {
931+
pub fn deinit(self: *GetResult) void {
827932
self.buffer_pool.release(self.buffer);
828933
}
829934

830-
pub fn src(self: *const BlockingResult) []const u8 {
935+
pub fn src(self: *const GetResult) []const u8 {
831936
return self.buffer.items;
832937
}
833938
};

src/browser/mimalloc.zig

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ pub fn getRSS() i64 {
6262
const data = writer.written();
6363
const index = std.mem.indexOf(u8, data, "rss: ") orelse return -1;
6464
const sep = std.mem.indexOfScalarPos(u8, data, index + 5, ' ') orelse return -2;
65-
const value = std.fmt.parseFloat(f64, data[index+5..sep]) catch return -3;
66-
const unit = data[sep+1..];
65+
const value = std.fmt.parseFloat(f64, data[index + 5 .. sep]) catch return -3;
66+
const unit = data[sep + 1 ..];
6767
if (std.mem.startsWith(u8, unit, "KiB,")) {
6868
return @as(i64, @intFromFloat(value)) * 1024;
6969
}

src/browser/page.zig

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,14 @@ pub const Page = struct {
255255
try Node.prepend(head, &[_]Node.NodeOrText{.{ .node = parser.elementToNode(base) }});
256256
}
257257

258-
pub fn fetchModuleSource(ctx: *anyopaque, src: [:0]const u8) !ScriptManager.BlockingResult {
258+
pub fn fetchModuleSource(ctx: *anyopaque, url: [:0]const u8) !ScriptManager.GetResult {
259259
const self: *Page = @ptrCast(@alignCast(ctx));
260-
return self.script_manager.blockingGet(src);
260+
return self.script_manager.blockingGet(url);
261+
}
262+
263+
pub fn fetchAsyncModuleSource(ctx: *anyopaque, url: [:0]const u8, cb: ScriptManager.AsyncModule.Callback, cb_data: *anyopaque) !void {
264+
const self: *Page = @ptrCast(@alignCast(ctx));
265+
return self.script_manager.getAsyncModule(url, cb, cb_data);
261266
}
262267

263268
pub fn wait(self: *Page, wait_ms: i32) Session.WaitResult {

0 commit comments

Comments
 (0)