| ... | ... | @@ -3,7 +3,6 @@ const Threaded = @This(); |
| 3 | 3 | const builtin = @import("builtin"); |
| 4 | 4 | const native_os = builtin.os.tag; |
| 5 | 5 | const is_windows = native_os == .windows; |
| 6 | | const is_musl = native_os == .linux and builtin.link_libc and builtin.abi.isMusl(); |
| 7 | 6 | const windows = std.os.windows; |
| 8 | 7 | const ws2_32 = std.os.windows.ws2_32; |
| 9 | 8 | const is_debug = builtin.mode == .Debug; |
| ... | ... | @@ -63,12 +62,7 @@ pid: Pid = .unknown, |
| 63 | 62 | /// Unfortunately, trying again until the cancellation request is acknowledged |
| 64 | 63 | /// has been observed to be relatively slow, and usually strong cancellation |
| 65 | 64 | /// guarantees are not needed, so this defaults to off. |
| 66 | | /// |
| 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, |
| 65 | robust_cancel: RobustCancel = .disabled, |
| 72 | 66 | |
| 73 | 67 | wsa: if (is_windows) Wsa else struct {} = .{}, |
| 74 | 68 | |
| ... | ... | @@ -76,9 +70,7 @@ have_signal_handler: bool, |
| 76 | 70 | old_sig_io: if (have_sig_io) posix.Sigaction else void, |
| 77 | 71 | old_sig_pipe: if (have_sig_pipe) posix.Sigaction else void, |
| 78 | 72 | |
| 79 | | pub const RobustCancel = if (is_musl) enum { |
| 80 | | enabled, |
| 81 | | } else if (std.Thread.use_pthreads or native_os == .linux) enum { |
| 73 | pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum { |
| 82 | 74 | enabled, |
| 83 | 75 | disabled, |
| 84 | 76 | } else enum { |
| ... | ... | @@ -113,15 +105,10 @@ const Thread = struct { |
| 113 | 105 | .acknowledged, |
| 114 | 106 | .acq_rel, |
| 115 | 107 | .acquire, |
| 116 | | ) orelse { |
| 117 | | if (is_musl) assert(std.c.pthread_setcancelstate(.DISABLE, null) == .SUCCESS); |
| 118 | | return error.Canceled; |
| 119 | | }) { |
| 108 | ) orelse return error.Canceled) { |
| 120 | 109 | .requested => unreachable, |
| 121 | 110 | .acknowledged => unreachable, |
| 122 | | .none, _ => { |
| 123 | | if (is_musl) assert(std.c.pthread_setcancelstate(.MASKED, null) == .SUCCESS); |
| 124 | | }, |
| 111 | .none, _ => {}, |
| 125 | 112 | } |
| 126 | 113 | } |
| 127 | 114 | |
| ... | ... | @@ -139,7 +126,6 @@ const Thread = struct { |
| 139 | 126 | .none => unreachable, |
| 140 | 127 | .requested => { |
| 141 | 128 | @atomicStore(CancelStatus, &closure.cancel_status, .acknowledged, .release); |
| 142 | | if (is_musl) assert(std.c.pthread_setcancelstate(.DISABLE, null) == .SUCCESS); |
| 143 | 129 | return error.Canceled; |
| 144 | 130 | }, |
| 145 | 131 | .acknowledged => return, |
| ... | ... | @@ -149,7 +135,7 @@ const Thread = struct { |
| 149 | 135 | |
| 150 | 136 | fn endSyscall(thread: *Thread) void { |
| 151 | 137 | const closure = thread.current_closure orelse return; |
| 152 | | const prev = @cmpxchgStrong( |
| 138 | _ = @cmpxchgStrong( |
| 153 | 139 | CancelStatus, |
| 154 | 140 | &closure.cancel_status, |
| 155 | 141 | .fromSignaleeId(thread.signal_id), |
| ... | ... | @@ -157,11 +143,6 @@ const Thread = struct { |
| 157 | 143 | .acq_rel, |
| 158 | 144 | .acquire, |
| 159 | 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 | 148 | fn currentSignalId() SignaleeId { |
| ... | ... | @@ -233,13 +214,6 @@ const Closure = struct { |
| 233 | 214 | .none, .acknowledged, .requested => return, |
| 234 | 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 | 217 | // The task will enter a blocking syscall before checking for cancellation again. |
| 244 | 218 | // We can send a signal to interrupt the syscall, but if it arrives before |
| 245 | 219 | // the syscall instruction, it will be missed. Therefore, this code tries |
| ... | ... | @@ -328,7 +302,7 @@ pub fn init( |
| 328 | 302 | .mask = posix.sigemptyset(), |
| 329 | 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 | 306 | if (have_sig_pipe) posix.sigaction(.PIPE, &act, &t.old_sig_pipe); |
| 333 | 307 | t.have_signal_handler = true; |
| 334 | 308 | } |
| ... | ... | @@ -366,7 +340,7 @@ pub fn deinit(t: *Threaded) void { |
| 366 | 340 | if (ws2_32.WSACleanup() != 0) recoverableOsBugDetected(); |
| 367 | 341 | } |
| 368 | 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 | 344 | if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null); |
| 371 | 345 | } |
| 372 | 346 | t.* = undefined; |
| ... | ... | @@ -397,15 +371,6 @@ fn worker(t: *Threaded) void { |
| 397 | 371 | while (true) { |
| 398 | 372 | while (t.run_queue.popFirst()) |closure_node| { |
| 399 | 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 | 374 | const closure: *Closure = @fieldParentPtr("node", closure_node); |
| 410 | 375 | closure.start(closure, t); |
| 411 | 376 | t.mutex.lock(); |
| ... | ... | @@ -3460,8 +3425,7 @@ fn nowWasi(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp { |
| 3460 | 3425 | const sleep = switch (native_os) { |
| 3461 | 3426 | .windows => sleepWindows, |
| 3462 | 3427 | .wasi => sleepWasi, |
| 3463 | | // Since we use musl's pthread_cancel, it's important that all the syscalls go through libc. |
| 3464 | | .linux => if (is_musl) sleepPosix else sleepLinux, |
| 3428 | .linux => sleepLinux, |
| 3465 | 3429 | else => sleepPosix, |
| 3466 | 3430 | }; |
| 3467 | 3431 | |