Merge pull request #802 from lightpanda-io/endless_loop_fix_and_dot_slash_stitch
Some checks failed
e2e-test / zig build release (push) Has been cancelled
e2e-test / puppeteer-perf (push) Has been cancelled
e2e-test / demo-scripts (push) Has been cancelled
e2e-test / cdp-and-hyperfine-bench (push) Has been cancelled
e2e-test / perf-fmt (push) Has been cancelled
zig-test / zig build dev (push) Has been cancelled
zig-test / browser fetch (push) Has been cancelled
zig-test / zig test (push) Has been cancelled
zig-test / perf-fmt (push) Has been cancelled
nightly build / build-linux-x86_64 (push) Has been cancelled
nightly build / build-linux-aarch64 (push) Has been cancelled
nightly build / build-macos-aarch64 (push) Has been cancelled
nightly build / build-macos-x86_64 (push) Has been cancelled
wpt / web platform tests json output (push) Has been cancelled
wpt / perf-fmt (push) Has been cancelled

Don't allow timeouts to be registered when shutting down
This commit is contained in:
Karl Seguin
2025-06-24 08:57:59 +08:00
committed by GitHub
6 changed files with 46 additions and 21 deletions

View File

@@ -144,12 +144,12 @@ pub const Page = struct {
return self.fetchData("module", src);
}
pub fn wait(self: *Page) !void {
pub fn wait(self: *Page, wait_ns: usize) !void {
var try_catch: Env.TryCatch = undefined;
try_catch.init(self.main_context);
defer try_catch.deinit();
try self.session.browser.app.loop.run();
try self.session.browser.app.loop.run(wait_ns);
if (try_catch.hasCaught() == false) {
log.debug(.browser, "page wait complete", .{});

View File

@@ -126,7 +126,7 @@ fn run(alloc: Allocator) !void {
},
};
try page.wait();
try page.wait(std.time.ns_per_s * 3);
// dump
if (opts.dump) {

View File

@@ -157,7 +157,7 @@ fn run(arena: Allocator, test_file: []const u8, loader: *FileLoader, err_out: *?
var try_catch: Env.TryCatch = undefined;
try_catch.init(runner.page.main_context);
defer try_catch.deinit();
try runner.page.loop.run();
try runner.page.loop.run(std.time.ns_per_ms * 200);
if (try_catch.hasCaught()) {
err_out.* = (try try_catch.err(arena)) orelse "unknwon error";

View File

@@ -23,6 +23,8 @@ const MemoryPool = std.heap.MemoryPool;
const log = @import("../log.zig");
pub const IO = @import("tigerbeetle-io").IO;
const RUN_DURATION = 10 * std.time.ns_per_ms;
// SingleThreaded I/O Loop based on Tigerbeetle io_uring loop.
// On Linux it's using io_uring.
// On MacOS and Windows it's using kqueue/IOCP with a ring design.
@@ -82,7 +84,7 @@ pub const Loop = struct {
// run tail events. We do run the tail events to ensure all the
// contexts are correcly free.
while (self.pending_network_count != 0 or self.pending_timeout_count != 0) {
self.io.run_for_ns(std.time.ns_per_ms * 10) catch |err| {
self.io.run_for_ns(RUN_DURATION) catch |err| {
log.err(.loop, "deinit", .{ .err = err });
break;
};
@@ -102,12 +104,16 @@ pub const Loop = struct {
// Stops when there is no more I/O events registered on the loop.
// Note that I/O events callbacks might register more I/O events
// on the go when they are executed (ie. nested I/O events).
pub fn run(self: *Self) !void {
pub fn run(self: *Self, wait_ns: usize) !void {
// stop repeating / interval timeouts from re-registering
self.stopping = true;
defer self.stopping = false;
while (self.pending_network_count != 0 or self.pending_timeout_count != 0) {
const max_iterations = wait_ns / (RUN_DURATION);
for (0..max_iterations) |_| {
if (self.pending_network_count == 0 and self.pending_timeout_count == 0) {
break;
}
self.io.run_for_ns(std.time.ns_per_ms * 10) catch |err| {
log.err(.loop, "deinit", .{ .err = err });
break;
@@ -187,6 +193,11 @@ pub const Loop = struct {
}
pub fn timeout(self: *Self, nanoseconds: u63, callback_node: ?*CallbackNode) !usize {
if (self.stopping and nanoseconds > std.time.ns_per_ms * 500) {
// we're trying to shutdown, we probably don't want to wait for a new
// long timeout
return 0;
}
const completion = try self.alloc.create(Completion);
errdefer self.alloc.destroy(completion);
completion.* = undefined;

View File

@@ -435,7 +435,7 @@ pub const JsRunner = struct {
}
return err;
};
try self.page.loop.run();
try self.page.loop.run(std.time.ns_per_ms * 200);
@import("root").js_runner_duration += std.time.Instant.since(try std.time.Instant.now(), start);
if (case.@"1") |expected| {

View File

@@ -110,7 +110,13 @@ pub const URL = struct {
}
return src;
}
if (src.len == 0) {
var normalized_src = if (std.mem.startsWith(u8, src, "/")) src[1..] else src;
while (std.mem.startsWith(u8, normalized_src, "./")) {
normalized_src = normalized_src[2..];
}
if (normalized_src.len == 0) {
if (opts.alloc == .always) {
return allocator.dupe(u8, base);
}
@@ -125,8 +131,6 @@ pub const URL = struct {
}
};
const normalized_src = if (src[0] == '/') src[1..] else src;
if (std.mem.lastIndexOfScalar(u8, base[protocol_end..], '/')) |index| {
const last_slash_pos = index + protocol_end;
if (last_slash_pos == base.len - 1) {
@@ -216,41 +220,41 @@ test "URL: resolve size" {
test "URL: Stitching Base & Src URLs (Basic)" {
const allocator = testing.allocator;
const base = "https://www.google.com/xyz/abc/123";
const base = "https://lightpanda.io/xyz/abc/123";
const src = "something.js";
const result = try URL.stitch(allocator, src, base, .{});
defer allocator.free(result);
try testing.expectString("https://www.google.com/xyz/abc/something.js", result);
try testing.expectString("https://lightpanda.io/xyz/abc/something.js", result);
}
test "URL: Stitching Base & Src URLs (Just Ending Slash)" {
const allocator = testing.allocator;
const base = "https://www.google.com/";
const base = "https://lightpanda.io/";
const src = "something.js";
const result = try URL.stitch(allocator, src, base, .{});
defer allocator.free(result);
try testing.expectString("https://www.google.com/something.js", result);
try testing.expectString("https://lightpanda.io/something.js", result);
}
test "URL: Stitching Base & Src URLs with leading slash" {
const allocator = testing.allocator;
const base = "https://www.google.com/";
const base = "https://lightpanda.io/";
const src = "/something.js";
const result = try URL.stitch(allocator, src, base, .{});
defer allocator.free(result);
try testing.expectString("https://www.google.com/something.js", result);
try testing.expectString("https://lightpanda.io/something.js", result);
}
test "URL: Stitching Base & Src URLs (No Ending Slash)" {
const allocator = testing.allocator;
const base = "https://www.google.com";
const base = "https://lightpanda.io";
const src = "something.js";
const result = try URL.stitch(allocator, src, base, .{});
defer allocator.free(result);
try testing.expectString("https://www.google.com/something.js", result);
try testing.expectString("https://lightpanda.io/something.js", result);
}
test "URL: Stiching Base & Src URLs (Both Local)" {
@@ -275,11 +279,21 @@ test "URL: Stiching src as full path" {
test "URL: Stitching Base & Src URLs (empty src)" {
const allocator = testing.allocator;
const base = "https://www.google.com/xyz/abc/123";
const base = "https://lightpanda.io/xyz/abc/123";
const src = "";
const result = try URL.stitch(allocator, src, base, .{});
defer allocator.free(result);
try testing.expectString("https://www.google.com/xyz/abc/123", result);
try testing.expectString("https://lightpanda.io/xyz/abc/123", result);
}
test "URL: Stitching dotslash" {
const allocator = testing.allocator;
const base = "https://lightpanda.io/hello/";
const src = "./something.js";
const result = try URL.stitch(allocator, src, base, .{});
defer allocator.free(result);
try testing.expectString("https://lightpanda.io/hello/something.js", result);
}
test "URL: concatQueryString" {