diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 109b467c41..168395d335 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -26,8 +26,13 @@ threads: std.ArrayListUnmanaged(std.Thread), stack_size: usize, cpu_count: std.Thread.CpuCountError!usize, concurrent_count: usize, + wsa: if (is_windows) Wsa else struct {} = .{}, +have_signal_handler: bool, +old_sig_io: if (have_sig_io) posix.Sigaction else void, +old_sig_pipe: if (have_sig_pipe) posix.Sigaction else void, + threadlocal var current_closure: ?*Closure = null; const max_iovecs_len = 8; @@ -104,23 +109,46 @@ pub fn init( .stack_size = std.Thread.SpawnConfig.default_stack_size, .cpu_count = std.Thread.getCpuCount(), .concurrent_count = 0, + .old_sig_io = undefined, + .old_sig_pipe = undefined, + .have_signal_handler = false, }; + if (t.cpu_count) |n| { t.threads.ensureTotalCapacityPrecise(gpa, n - 1) catch {}; } else |_| {} + + if (posix.Sigaction != void) { + // This causes sending `posix.SIG.IO` to thread to interrupt blocking + // syscalls, returning `posix.E.INTR`. + const act: posix.Sigaction = .{ + .handler = .{ .handler = doNothingSignalHandler }, + .mask = posix.sigemptyset(), + .flags = 0, + }; + if (have_sig_io) posix.sigaction(.IO, &act, &t.old_sig_io); + if (have_sig_pipe) posix.sigaction(.PIPE, &act, &t.old_sig_pipe); + t.have_signal_handler = true; + } + return t; } /// Statically initialize such that calls to `Io.VTable.concurrent` will fail /// with `error.ConcurrencyUnavailable`. /// -/// When initialized this way, `deinit` is safe, but unnecessary to call. +/// When initialized this way: +/// * cancel requests have no effect. +/// * `deinit` is safe, but unnecessary to call. pub const init_single_threaded: Threaded = .{ .allocator = .failing, .threads = .empty, .stack_size = std.Thread.SpawnConfig.default_stack_size, .cpu_count = 1, .concurrent_count = 0, + .old_sig_io = undefined, + .old_sig_pipe = undefined, + .have_signal_handler = false, }; pub fn deinit(t: *Threaded) void { @@ -130,6 +158,10 @@ pub fn deinit(t: *Threaded) void { if (is_windows and t.wsa.status == .initialized) { if (ws2_32.WSACleanup() != 0) recoverableOsBugDetected(); } + if (posix.Sigaction != void and t.have_signal_handler) { + if (have_sig_io) posix.sigaction(.IO, &t.old_sig_io, null); + if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null); + } t.* = undefined; } @@ -338,6 +370,8 @@ const have_preadv = switch (native_os) { .windows, .haiku, .serenity => false, // 💩💩💩 else => true, }; +const have_sig_io = posix.SIG != void and @hasField(posix.SIG, "IO"); +const have_sig_pipe = posix.SIG != void and @hasField(posix.SIG, "PIPE"); const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat; const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat; @@ -6115,6 +6149,8 @@ fn initializeWsa(t: *Threaded) error{NetworkDown}!void { return error.NetworkDown; } +fn doNothingSignalHandler(_: posix.SIG) callconv(.c) void {} + test { _ = @import("Threaded/test.zig"); } diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 7952727e9a..6faf6bbe72 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -55,6 +55,7 @@ else switch (native_os) { pub const mode_t = u0; pub const ino_t = void; pub const IFNAMESIZE = {}; + pub const SIG = void; }, }; diff --git a/lib/std/start.zig b/lib/std/start.zig index 79e2d93134..a563912bbc 100644 --- a/lib/std/start.zig +++ b/lib/std/start.zig @@ -651,7 +651,6 @@ inline fn callMainWithArgs(argc: usize, argv: [*][*:0]u8, envp: [][*:0]u8) u8 { std.os.argv = argv[0..argc]; std.os.environ = envp; - maybeIgnoreSignals(); std.debug.maybeEnableSegfaultHandler(); return callMain(); @@ -756,23 +755,3 @@ pub fn call_wWinMain() std.os.windows.INT { // second parameter hPrevInstance, MSDN: "This parameter is always NULL" return root.wWinMain(hInstance, null, lpCmdLine, nCmdShow); } - -fn maybeIgnoreSignals() void { - const posix = std.posix; - if (posix.Sigaction == void) return; - const act: posix.Sigaction = .{ - // Set handler to a noop function instead of `IGN` to prevent - // leaking signal disposition to a child process. - .handler = .{ .handler = noopSigHandler }, - .mask = posix.sigemptyset(), - .flags = 0, - }; - - if (@hasField(posix.SIG, "IO") and !std.options.keep_sig_io) - posix.sigaction(.IO, &act, null); - - if (@hasField(posix.SIG, "PIPE") and !std.options.keep_sig_pipe) - posix.sigaction(.PIPE, &act, null); -} - -fn noopSigHandler(_: std.posix.SIG) callconv(.c) void {} diff --git a/lib/std/std.zig b/lib/std/std.zig index ff1976ae04..1b8142ce4c 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -144,21 +144,6 @@ pub const Options = struct { crypto_fork_safety: bool = true, - keep_sig_io: bool = false, - - /// By default Zig disables SIGPIPE by setting a "no-op" handler for it. Set this option - /// to `true` to prevent that. - /// - /// Note that we use a "no-op" handler instead of SIG_IGN because it will not be inherited by - /// any child process. - /// - /// SIGPIPE is triggered when a process attempts to write to a broken pipe. By default, SIGPIPE - /// will terminate the process instead of exiting. It doesn't trigger the panic handler so in many - /// cases it's unclear why the process was terminated. By capturing SIGPIPE instead, functions that - /// write to broken pipes will return the EPIPE error (error.BrokenPipe) and the program can handle - /// it like any other error. - keep_sig_pipe: bool = false, - /// By default, std.http.Client will support HTTPS connections. Set this option to `true` to /// disable TLS support. ///