| ... | @@ -3,7 +3,6 @@ const Threaded = @This(); | ... | @@ -3,7 +3,6 @@ const Threaded = @This(); |
| 3 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| 4 | const native_os = builtin.os.tag; | 4 | const native_os = builtin.os.tag; |
| 5 | const is_windows = native_os == .windows; | 5 | const is_windows = native_os == .windows; |
| 6 | const is_musl = native_os == .linux and builtin.link_libc and builtin.abi.isMusl(); | | |
| 7 | const windows = std.os.windows; | 6 | const windows = std.os.windows; |
| 8 | const ws2_32 = std.os.windows.ws2_32; | 7 | const ws2_32 = std.os.windows.ws2_32; |
| 9 | const is_debug = builtin.mode == .Debug; | 8 | const is_debug = builtin.mode == .Debug; |
| ... | @@ -63,12 +62,7 @@ pid: Pid = .unknown, | ... | @@ -63,12 +62,7 @@ pid: Pid = .unknown, |
| 63 | /// Unfortunately, trying again until the cancellation request is acknowledged | 62 | /// Unfortunately, trying again until the cancellation request is acknowledged |
| 64 | /// has been observed to be relatively slow, and usually strong cancellation | 63 | /// has been observed to be relatively slow, and usually strong cancellation |
| 65 | /// guarantees are not needed, so this defaults to off. | 64 | /// guarantees are not needed, so this defaults to off. |
| 66 | /// | 65 | robust_cancel: RobustCancel = .disabled, |
| 67 | /// Musl libc does not have this problem because of a clever, undocumented | | |
| 68 | /// extension related to pthread_cancel, which this code integrates with. | | |
| 69 | /// When compiling with no libc, `Threaded` does not yet implement the | | |
| 70 | /// equivalent trick (tracked by https://codeberg.org/ziglang/zig/issues/30049). | | |
| 71 | robust_cancel: RobustCancel = if (is_musl) .enabled else .disabled, | | |
| 72 | | 66 | |
| 73 | wsa: if (is_windows) Wsa else struct {} = .{}, | 67 | wsa: if (is_windows) Wsa else struct {} = .{}, |
| 74 | | 68 | |
| ... | @@ -76,9 +70,7 @@ have_signal_handler: bool, | ... | @@ -76,9 +70,7 @@ have_signal_handler: bool, |
| 76 | old_sig_io: if (have_sig_io) posix.Sigaction else void, | 70 | old_sig_io: if (have_sig_io) posix.Sigaction else void, |
| 77 | old_sig_pipe: if (have_sig_pipe) posix.Sigaction else void, | 71 | old_sig_pipe: if (have_sig_pipe) posix.Sigaction else void, |
| 78 | | 72 | |
| 79 | pub const RobustCancel = if (is_musl) enum { | 73 | pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum { |
| 80 | enabled, | | |
| 81 | } else if (std.Thread.use_pthreads or native_os == .linux) enum { | | |
| 82 | enabled, | 74 | enabled, |
| 83 | disabled, | 75 | disabled, |
| 84 | } else enum { | 76 | } else enum { |
| ... | @@ -113,15 +105,10 @@ const Thread = struct { | ... | @@ -113,15 +105,10 @@ const Thread = struct { |
| 113 | .acknowledged, | 105 | .acknowledged, |
| 114 | .acq_rel, | 106 | .acq_rel, |
| 115 | .acquire, | 107 | .acquire, |
| 116 | ) orelse { | 108 | ) orelse return error.Canceled) { |
| 117 | if (is_musl) assert(std.c.pthread_setcancelstate(.DISABLE, null) == .SUCCESS); | | |
| 118 | return error.Canceled; | | |
| 119 | }) { | | |
| 120 | .requested => unreachable, | 109 | .requested => unreachable, |
| 121 | .acknowledged => unreachable, | 110 | .acknowledged => unreachable, |
| 122 | .none, _ => { | 111 | .none, _ => {}, |
| 123 | if (is_musl) assert(std.c.pthread_setcancelstate(.MASKED, null) == .SUCCESS); | | |
| 124 | }, | | |
| 125 | } | 112 | } |
| 126 | } | 113 | } |
| 127 | | 114 | |
| ... | @@ -139,7 +126,6 @@ const Thread = struct { | ... | @@ -139,7 +126,6 @@ const Thread = struct { |
| 139 | .none => unreachable, | 126 | .none => unreachable, |
| 140 | .requested => { | 127 | .requested => { |
| 141 | @atomicStore(CancelStatus, &closure.cancel_status, .acknowledged, .release); | 128 | @atomicStore(CancelStatus, &closure.cancel_status, .acknowledged, .release); |
| 142 | if (is_musl) assert(std.c.pthread_setcancelstate(.DISABLE, null) == .SUCCESS); | | |
| 143 | return error.Canceled; | 129 | return error.Canceled; |
| 144 | }, | 130 | }, |
| 145 | .acknowledged => return, | 131 | .acknowledged => return, |
| ... | @@ -149,7 +135,7 @@ const Thread = struct { | ... | @@ -149,7 +135,7 @@ const Thread = struct { |
| 149 | | 135 | |
| 150 | fn endSyscall(thread: *Thread) void { | 136 | fn endSyscall(thread: *Thread) void { |
| 151 | const closure = thread.current_closure orelse return; | 137 | const closure = thread.current_closure orelse return; |
| 152 | const prev = @cmpxchgStrong( | 138 | _ = @cmpxchgStrong( |
| 153 | CancelStatus, | 139 | CancelStatus, |
| 154 | &closure.cancel_status, | 140 | &closure.cancel_status, |
| 155 | .fromSignaleeId(thread.signal_id), | 141 | .fromSignaleeId(thread.signal_id), |
| ... | @@ -157,11 +143,6 @@ const Thread = struct { | ... | @@ -157,11 +143,6 @@ const Thread = struct { |
| 157 | .acq_rel, | 143 | .acq_rel, |
| 158 | .acquire, | 144 | .acquire, |
| 159 | ) orelse return; | 145 | ) orelse return; |
| 160 | if (is_musl and prev == .requested) { | | |
| 161 | // They called pthread_cancel, but we want to disarm it since | | |
| 162 | // the next call to beginSyscall will notice requested status. | | |
| 163 | assert(std.c.pthread_setcancelstate(.DISABLE, null) == .SUCCESS); | | |
| 164 | } | | |
| 165 | } | 146 | } |
| 166 | | 147 | |
| 167 | fn currentSignalId() SignaleeId { | 148 | fn currentSignalId() SignaleeId { |
| ... | @@ -233,13 +214,6 @@ const Closure = struct { | ... | @@ -233,13 +214,6 @@ const Closure = struct { |
| 233 | .none, .acknowledged, .requested => return, | 214 | .none, .acknowledged, .requested => return, |
| 234 | .signal_id => |signal_id| signal_id, | 215 | .signal_id => |signal_id| signal_id, |
| 235 | }; | 216 | }; |
| 236 | // Musl has an undocumented extension that makes pthread_cancel have the useful, desired | | |
| 237 | // behavior of causing the next syscall to return ECANCELED. | | |
| 238 | if (is_musl) { | | |
| 239 | _ = std.c.pthread_cancel(signal_id); | | |
| 240 | return; | | |
| 241 | } | | |
| 242 | | | |
| 243 | // The task will enter a blocking syscall before checking for cancellation again. | 217 | // The task will enter a blocking syscall before checking for cancellation again. |
| 244 | // We can send a signal to interrupt the syscall, but if it arrives before | 218 | // We can send a signal to interrupt the syscall, but if it arrives before |
| 245 | // the syscall instruction, it will be missed. Therefore, this code tries | 219 | // the syscall instruction, it will be missed. Therefore, this code tries |
| ... | @@ -328,7 +302,7 @@ pub fn init( | ... | @@ -328,7 +302,7 @@ pub fn init( |
| 328 | .mask = posix.sigemptyset(), | 302 | .mask = posix.sigemptyset(), |
| 329 | .flags = 0, | 303 | .flags = 0, |
| 330 | }; | 304 | }; |
| 331 | if (!is_musl and have_sig_io) posix.sigaction(.IO, &act, &t.old_sig_io); | 305 | if (have_sig_io) posix.sigaction(.IO, &act, &t.old_sig_io); |
| 332 | if (have_sig_pipe) posix.sigaction(.PIPE, &act, &t.old_sig_pipe); | 306 | if (have_sig_pipe) posix.sigaction(.PIPE, &act, &t.old_sig_pipe); |
| 333 | t.have_signal_handler = true; | 307 | t.have_signal_handler = true; |
| 334 | } | 308 | } |
| ... | @@ -366,7 +340,7 @@ pub fn deinit(t: *Threaded) void { | ... | @@ -366,7 +340,7 @@ pub fn deinit(t: *Threaded) void { |
| 366 | if (ws2_32.WSACleanup() != 0) recoverableOsBugDetected(); | 340 | if (ws2_32.WSACleanup() != 0) recoverableOsBugDetected(); |
| 367 | } | 341 | } |
| 368 | if (posix.Sigaction != void and t.have_signal_handler) { | 342 | if (posix.Sigaction != void and t.have_signal_handler) { |
| 369 | if (!is_musl and have_sig_io) posix.sigaction(.IO, &t.old_sig_io, null); | 343 | if (have_sig_io) posix.sigaction(.IO, &t.old_sig_io, null); |
| 370 | if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null); | 344 | if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null); |
| 371 | } | 345 | } |
| 372 | t.* = undefined; | 346 | t.* = undefined; |
| ... | @@ -397,15 +371,6 @@ fn worker(t: *Threaded) void { | ... | @@ -397,15 +371,6 @@ fn worker(t: *Threaded) void { |
| 397 | while (true) { | 371 | while (true) { |
| 398 | while (t.run_queue.popFirst()) |closure_node| { | 372 | while (t.run_queue.popFirst()) |closure_node| { |
| 399 | t.mutex.unlock(); | 373 | t.mutex.unlock(); |
| 400 | | | |
| 401 | // Musl has an undocumented extension that makes pthread_cancel have the useful, desired | | |
| 402 | // behavior of causing the next syscall to return ECANCELED. | | |
| 403 | // | | |
| 404 | // The call to `requestCancel` and this can race, leading to | | |
| 405 | // ECANCELED being returned for a syscall in an unrelated task, | | |
| 406 | // which is why EINTR and ECANCELED are both handled with a check. | | |
| 407 | if (is_musl) assert(std.c.pthread_setcancelstate(.MASKED, null) == .SUCCESS); | | |
| 408 | | | |
| 409 | const closure: *Closure = @fieldParentPtr("node", closure_node); | 374 | const closure: *Closure = @fieldParentPtr("node", closure_node); |
| 410 | closure.start(closure, t); | 375 | closure.start(closure, t); |
| 411 | t.mutex.lock(); | 376 | t.mutex.lock(); |
| ... | @@ -3460,8 +3425,7 @@ fn nowWasi(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp { | ... | @@ -3460,8 +3425,7 @@ fn nowWasi(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp { |
| 3460 | const sleep = switch (native_os) { | 3425 | const sleep = switch (native_os) { |
| 3461 | .windows => sleepWindows, | 3426 | .windows => sleepWindows, |
| 3462 | .wasi => sleepWasi, | 3427 | .wasi => sleepWasi, |
| 3463 | // Since we use musl's pthread_cancel, it's important that all the syscalls go through libc. | 3428 | .linux => sleepLinux, |
| 3464 | .linux => if (is_musl) sleepPosix else sleepLinux, | | |
| 3465 | else => sleepPosix, | 3429 | else => sleepPosix, |
| 3466 | }; | 3430 | }; |
| 3467 | | 3431 | |