Skip to content

Commit 31cdf15

Browse files
committed
Fix build run-mode validation
1 parent 8866195 commit 31cdf15

3 files changed

Lines changed: 68 additions & 0 deletions

File tree

src/root/server_routes.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ fn wsConnectionThreadMain(state: anytype, stream: std.net.Stream, connection_id:
3333
defer {
3434
state.state_mutex.lock();
3535
state.unregisterWsConnectionLocked(connection_id);
36+
state.noteWsDisconnectLocked("websocket-disconnected");
3637
state.state_mutex.unlock();
3738
stream.close();
3839
state.emitDiagnostic("websocket.disconnected", .websocket, .info, "WebSocket client disconnected");

src/root/tests.zig

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,61 @@ test "window_closing lifecycle message is ignored in web-url mode without tracke
777777
try std.testing.expect(!should_close);
778778
}
779779

780+
test "websocket disconnect schedules close in browser-window mode when last client is gone" {
781+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
782+
defer _ = gpa.deinit();
783+
784+
var app = try App.init(gpa.allocator(), .{
785+
.launch_policy = .{ .first = .browser_window, .second = null, .third = null },
786+
});
787+
defer app.deinit();
788+
789+
var win = try app.newWindow(.{ .title = "WsDisconnectCloseBrowserWindow" });
790+
try win.showHtml("<html><body>ws-disconnect-close-browser-window</body></html>");
791+
try app.run();
792+
793+
win.state().state_mutex.lock();
794+
win.state().runtime_render_state.active_surface = .browser_window;
795+
win.state().noteWsDisconnectLocked("test-disconnect");
796+
const pending_after_disconnect = win.state().lifecycle_close_pending;
797+
const close_immediate = win.state().close_requested.load(.acquire);
798+
win.state().state_mutex.unlock();
799+
800+
try std.testing.expect(pending_after_disconnect);
801+
try std.testing.expect(!close_immediate);
802+
803+
win.state().state_mutex.lock();
804+
win.state().lifecycle_close_deadline_ms = std.time.milliTimestamp() - 1;
805+
win.state().reconcileChildExit(gpa.allocator());
806+
const should_close = win.state().close_requested.load(.acquire);
807+
win.state().state_mutex.unlock();
808+
try std.testing.expect(should_close);
809+
}
810+
811+
test "websocket disconnect does not close backend in web-url mode" {
812+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
813+
defer _ = gpa.deinit();
814+
815+
var app = try App.init(gpa.allocator(), .{
816+
.launch_policy = .{ .first = .web_url, .second = null, .third = null },
817+
});
818+
defer app.deinit();
819+
820+
var win = try app.newWindow(.{ .title = "WsDisconnectNoCloseWebUrl" });
821+
try win.showHtml("<html><body>ws-disconnect-no-close-web-url</body></html>");
822+
try app.run();
823+
824+
win.state().state_mutex.lock();
825+
win.state().runtime_render_state.active_surface = .web_url;
826+
win.state().noteWsDisconnectLocked("test-disconnect");
827+
const pending = win.state().lifecycle_close_pending;
828+
const should_close = win.state().close_requested.load(.acquire);
829+
win.state().state_mutex.unlock();
830+
831+
try std.testing.expect(!pending);
832+
try std.testing.expect(!should_close);
833+
}
834+
780835
test "non-linked tracked browser pid death detaches without close in web mode" {
781836
if (builtin.os.tag != .linux) return error.SkipZigTest;
782837

src/root/window_state.zig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,18 @@ pub const WindowState = struct {
906906
}
907907
}
908908

909+
pub fn noteWsDisconnectLocked(self: *WindowState, reason: []const u8) void {
910+
// Browser-window lifecycle should terminate backend when the window is
911+
// gone, even when explicit lifecycle "window_closing" is not delivered
912+
// (for example some browser/window-manager close paths).
913+
// Keep grace semantics so refresh/reload reconnect can cancel shutdown.
914+
if (self.runtime_render_state.active_surface != .browser_window) return;
915+
if (self.close_requested.load(.acquire)) return;
916+
if (self.ws_connections.items.len != 0) return;
917+
self.rpc_state.logf(.debug, "[webui.lifecycle] scheduling close from ws disconnect reason={s}\n", .{reason});
918+
self.scheduleLifecycleCloseLocked();
919+
}
920+
909921
pub fn closeWsConnectionLocked(self: *WindowState, connection_id: usize) void {
910922
for (self.ws_connections.items, 0..) |entry, idx| {
911923
if (entry.connection_id != connection_id) continue;

0 commit comments

Comments
 (0)