authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-12-30 20:43:53+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-03 15:45:10+00:00
log2c395e326f2db4e2389af729b88bd93e76e4d27c
treed005a7ec304236a88d310f3013e032d817211270
parentf27134d671450a2aac3d48c82ccfbc5a947849f3
signaturelock-open Commit is signed but in an unrecognized format.

std.Io.Threaded: always use robust cancelation

As of this branch, the performance impact of robust cancelation is now negligible (and in fact entirely unmeasurable in almost all cases), so there is no good reason to not enable it in all cases. The performance issues before were primarily down to a typo in the robust cancelation logic which resulted in every canceled syscall potentially being sent hundreds of signals in quick succession, because the delay between signals started out at 1ns instead of 1us!

1 files changed, 2 insertions(+), 18 deletions(-)

lib/std/Io/Threaded.zig+2-18
......@@ -39,7 +39,6 @@ cpu_count_error: ?std.Thread.CpuCountError,
3939busy_count: usize = 0,
4040worker_threads: std.atomic.Value(?*Thread),
4141pid: Pid = .unknown,
42robust_cancel: RobustCancel,
4342
4443wsa: if (is_windows) Wsa else struct {} = .{},
4544
......@@ -105,8 +104,6 @@ pub const Environ = struct {
105104 };
106105};
107106
108pub const RobustCancel = enum { enabled, disabled };
109
110107pub const Pid = if (native_os == .linux) enum(posix.pid_t) {
111108 unknown = 0,
112109 _,
......@@ -315,7 +312,7 @@ const Group = struct {
315312 var need_signal: bool = !skip_signals and g.cancelThreads(t);
316313 var timeout_ns: u64 = 1 << 10;
317314 while (true) {
318 need_signal = need_signal and g.signalAllCanceledSyscalls(t) and t.robust_cancel == .enabled;
315 need_signal = need_signal and g.signalAllCanceledSyscalls(t);
319316 Thread.futexWaitUncancelable(&num_completed.raw, 0, if (need_signal) timeout_ns else null);
320317 switch (num_completed.load(.acquire)) { // acquire task results
321318 0 => {},
......@@ -469,7 +466,7 @@ const Future = struct {
469466 var need_signal: bool = thread != null and thread.?.cancelAwaitable(.fromFuture(future));
470467 var timeout_ns: u64 = 1 << 10;
471468 while (true) {
472 need_signal = need_signal and thread.?.signalCanceledSyscall(t, .fromFuture(future)) and t.robust_cancel == .enabled;
469 need_signal = need_signal and thread.?.signalCanceledSyscall(t, .fromFuture(future));
473470 Thread.futexWaitUncancelable(&num_completed.raw, 0, if (need_signal) timeout_ns else null);
474471 switch (num_completed.load(.acquire)) { // acquire task results
475472 0 => {},
......@@ -1112,17 +1109,6 @@ pub const InitOptions = struct {
11121109 /// concurrent tasks. After this number, calls to `Io.concurrent` return
11131110 /// `error.ConcurrencyUnavailable`.
11141111 concurrent_limit: Io.Limit = .unlimited,
1115 /// When a cancel request is made, blocking syscalls can be unblocked by
1116 /// issuing a signal. However, if the signal arrives after the check and before
1117 /// the syscall instruction, it is missed.
1118 ///
1119 /// This option solves the race condition by retrying the signal delivery
1120 /// until it is acknowledged, with an exponential backoff.
1121 ///
1122 /// Unfortunately, trying again until the cancellation request is acknowledged
1123 /// has been observed to be relatively slow, and usually strong cancellation
1124 /// guarantees are not needed, so this defaults to off.
1125 robust_cancel: RobustCancel = .disabled,
11261112 /// Affects the following operations:
11271113 /// * `processExecutablePath` on OpenBSD and Haiku.
11281114 argv0: Argv0 = .{},
......@@ -1160,7 +1146,6 @@ pub fn init(
11601146 .have_signal_handler = false,
11611147 .argv0 = options.argv0,
11621148 .environ = options.environ,
1163 .robust_cancel = options.robust_cancel,
11641149 .worker_threads = .init(null),
11651150 };
11661151
......@@ -1195,7 +1180,6 @@ pub const init_single_threaded: Threaded = .{
11951180 .old_sig_io = undefined,
11961181 .old_sig_pipe = undefined,
11971182 .have_signal_handler = false,
1198 .robust_cancel = .disabled,
11991183 .argv0 = .{},
12001184 .environ = .{},
12011185 .worker_threads = .init(null),