diff --git a/src/browser/page.zig b/src/browser/page.zig index 5bbc6b59..2a34b2da 100644 --- a/src/browser/page.zig +++ b/src/browser/page.zig @@ -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", .{}); diff --git a/src/main.zig b/src/main.zig index c95f7a6d..ef68d139 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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) { diff --git a/src/main_wpt.zig b/src/main_wpt.zig index 90bcafba..cc3e013d 100644 --- a/src/main_wpt.zig +++ b/src/main_wpt.zig @@ -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"; diff --git a/src/runtime/loop.zig b/src/runtime/loop.zig index 6e022e44..71cd7f8a 100644 --- a/src/runtime/loop.zig +++ b/src/runtime/loop.zig @@ -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; diff --git a/src/testing.zig b/src/testing.zig index 75bb3a95..843240bb 100644 --- a/src/testing.zig +++ b/src/testing.zig @@ -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| { diff --git a/src/url.zig b/src/url.zig index 6244bf59..fa2dad8a 100644 --- a/src/url.zig +++ b/src/url.zig @@ -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" {