From 1a4a3608c82fa847138a936e6063c278a0b0f31b Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Thu, 28 Aug 2025 10:58:35 +0200 Subject: [PATCH 1/2] exit the browser on SIGINT signal --- src/main.zig | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main.zig b/src/main.zig index bd20ba721..85079bbbc 100644 --- a/src/main.zig +++ b/src/main.zig @@ -41,6 +41,21 @@ pub fn main() !void { if (gpa.detectLeaks()) std.posix.exit(1); }; + // Exit the program on SIGINT signal. When running the browser in a Docker + // container, sending a CTRL-C (SIGINT) signal is catched but doesn't exit + // the program. Here we force exiting on SIGINT. + std.posix.sigaction(std.posix.SIG.INT, &std.posix.Sigaction{ + .handler = .{ + .handler = struct { + pub fn handler(_: c_int) callconv(.c) void { + std.posix.exit(0); + } + }.handler, + }, + .mask = std.posix.empty_sigset, + .flags = 0, + }, null); + run(alloc) catch |err| { // If explicit filters were set, they won't be valid anymore because // the args_arena is gone. We need to set it to something that's not From 4c7b7b1e60ffc58feb1514fdb67c9233792044ce Mon Sep 17 00:00:00 2001 From: Pierre Tachoire Date: Thu, 28 Aug 2025 12:26:59 +0200 Subject: [PATCH 2/2] handle graceful shutdown --- src/main.zig | 50 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/main.zig b/src/main.zig index 85079bbbc..3e6af90fd 100644 --- a/src/main.zig +++ b/src/main.zig @@ -30,6 +30,9 @@ const Browser = @import("browser/browser.zig").Browser; const build_config = @import("build_config"); const parser = @import("browser/netsurf.zig"); +var _app: ?*App = null; +var _server: ?Server = null; + pub fn main() !void { // allocator // - in Debug mode we use the General Purpose Allocator to detect memory leaks @@ -41,29 +44,42 @@ pub fn main() !void { if (gpa.detectLeaks()) std.posix.exit(1); }; - // Exit the program on SIGINT signal. When running the browser in a Docker - // container, sending a CTRL-C (SIGINT) signal is catched but doesn't exit - // the program. Here we force exiting on SIGINT. - std.posix.sigaction(std.posix.SIG.INT, &std.posix.Sigaction{ + run(alloc) catch |err| { + // If explicit filters were set, they won't be valid anymore because + // the args_arena is gone. We need to set it to something that's not + // invalid. (We should just move the args_arena up to main) + log.opts.filter_scopes = &.{}; + log.fatal(.app, "exit", .{ .err = err }); + std.posix.exit(1); + }; +} + +// Handle app shutdown gracefuly on signals. +fn shutdown() void { + const sigaction: std.posix.Sigaction = .{ .handler = .{ .handler = struct { pub fn handler(_: c_int) callconv(.c) void { + // Shutdown service gracefuly. + if (_server) |server| { + server.deinit(); + } + if (_app) |app| { + app.deinit(); + } std.posix.exit(0); } }.handler, }, .mask = std.posix.empty_sigset, .flags = 0, - }, null); - - run(alloc) catch |err| { - // If explicit filters were set, they won't be valid anymore because - // the args_arena is gone. We need to set it to something that's not - // invalid. (We should just move the args_arena up to main) - log.opts.filter_scopes = &.{}; - log.fatal(.app, "exit", .{ .err = err }); - std.posix.exit(1); }; + // Exit the program on SIGINT signal. When running the browser in a Docker + // container, sending a CTRL-C (SIGINT) signal is catched but doesn't exit + // the program. Here we force exiting on SIGINT. + std.posix.sigaction(std.posix.SIG.INT, &sigaction, null); + std.posix.sigaction(std.posix.SIG.TERM, &sigaction, null); + std.posix.sigaction(std.posix.SIG.QUIT, &sigaction, null); } fn run(alloc: Allocator) !void { @@ -96,7 +112,8 @@ fn run(alloc: Allocator) !void { const platform = try Platform.init(); defer platform.deinit(); - var app = try App.init(alloc, .{ + // _app is global to handle graceful shutdown. + _app = try App.init(alloc, .{ .run_mode = args.mode, .platform = &platform, .http_proxy = args.httpProxy(), @@ -107,6 +124,7 @@ fn run(alloc: Allocator) !void { .http_max_host_open = args.httpMaxHostOpen(), .http_max_concurrent = args.httpMaxConcurrent(), }); + const app = _app.?; defer app.deinit(); app.telemetry.record(.{ .run = {} }); @@ -118,7 +136,9 @@ fn run(alloc: Allocator) !void { return args.printUsageAndExit(false); }; - var server = try Server.init(app, address); + // _server is global to handle graceful shutdown. + _server = try Server.init(app, address); + const server = &_server.?; defer server.deinit(); server.run(address, opts.timeout) catch |err| {