diff --git a/lib/std/Io.zig b/lib/std/Io.zig index aa860abb36..eb854e3eca 100644 --- a/lib/std/Io.zig +++ b/lib/std/Io.zig @@ -620,11 +620,6 @@ pub const VTable = struct { result: []u8, result_alignment: std.mem.Alignment, ) void, - /// Returns whether the current thread of execution is known to have - /// been requested to cancel. - /// - /// Thread-safe. - cancelRequested: *const fn (?*anyopaque) bool, /// When this function returns, implementation guarantees that `start` has /// either already been called, or a unit of concurrency has been assigned diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index b5d40b5b01..0e17cc01d0 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -50,6 +50,19 @@ cpu_count_error: ?std.Thread.CpuCountError, /// available count, subtract this from either `async_limit` or /// `concurrent_limit`. busy_count: usize = 0, +main_thread: Thread, +pid: Pid = .unknown, +/// When a cancel request is made, blocking syscalls can be unblocked by +/// issuing a signal. However, if the signal arrives after the check and before +/// the syscall instruction, it is missed. +/// +/// This option solves the race condition by retrying the signal delivery +/// until it is acknowledged, with an exponential backoff. +/// +/// Unfortunately, trying again until the cancellation request is acknowledged +/// has been observed to be relatively slow, and usually strong cancellation +/// guarantees are not needed, so this defaults to off. +robust_cancel: RobustCancel = .disabled, wsa: if (is_windows) Wsa else struct {} = .{}, @@ -57,7 +70,92 @@ 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; +pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum { + enabled, + disabled, +} else enum { + disabled, +}; + +pub const Pid = if (native_os == .linux) enum(posix.pid_t) { + unknown = 0, + _, +} else enum(u0) { unknown = 0 }; + +const Thread = struct { + /// The value that needs to be passed to pthread_kill or tgkill in order to + /// send a signal. + signal_id: SignaleeId, + current_closure: ?*Closure = null, + + const SignaleeId = if (std.Thread.use_pthreads) std.c.pthread_t else std.Thread.Id; + + threadlocal var current: ?*Thread = null; + + fn getCurrent(t: *Threaded) *Thread { + return current orelse return &t.main_thread; + } + + fn checkCancel(thread: *Thread) error{Canceled}!void { + const closure = thread.current_closure orelse return; + switch (@cmpxchgStrong( + CancelStatus, + &closure.cancel_status, + .requested, + .acknowledged, + .acq_rel, + .acquire, + ) orelse return error.Canceled) { + .requested => unreachable, + .acknowledged => unreachable, + .none, _ => {}, + } + } + + fn beginSyscall(thread: *Thread) error{Canceled}!void { + const closure = thread.current_closure orelse return; + + switch (@cmpxchgStrong( + CancelStatus, + &closure.cancel_status, + .none, + .fromSignaleeId(thread.signal_id), + .acq_rel, + .acquire, + ) orelse return) { + .none => unreachable, + .requested => { + @atomicStore(CancelStatus, &closure.cancel_status, .acknowledged, .release); + return error.Canceled; + }, + .acknowledged => return, + _ => unreachable, + } + } + + fn endSyscall(thread: *Thread) void { + const closure = thread.current_closure orelse return; + _ = @cmpxchgStrong( + CancelStatus, + &closure.cancel_status, + .fromSignaleeId(thread.signal_id), + .none, + .acq_rel, + .acquire, + ) orelse return; + } + + fn endSyscallCanceled(thread: *Thread) Io.Cancelable { + if (thread.current_closure) |closure| { + @atomicStore(CancelStatus, &closure.cancel_status, .acknowledged, .release); + } + return error.Canceled; + } + + fn currentSignalId() SignaleeId { + return if (std.Thread.use_pthreads) std.c.pthread_self() else std.Thread.getCurrentId(); + } +}; const max_iovecs_len = 8; const splat_buffer_size = 64; @@ -66,48 +164,110 @@ comptime { if (@TypeOf(posix.IOV_MAX) != void) assert(max_iovecs_len <= posix.IOV_MAX); } -const CancelId = enum(usize) { +const CancelStatus = enum(usize) { + /// Cancellation has neither been requested, nor checked. The async + /// operation will check status before entering a blocking syscall. + /// This is also the status used for uninteruptible tasks. none = 0, - canceling = std.math.maxInt(usize), + /// Cancellation has been requested and the status will be checked before + /// entering a blocking syscall. + requested = std.math.maxInt(usize) - 1, + /// Cancellation has been acknowledged and is in progress. Signals should + /// not be sent. + acknowledged = std.math.maxInt(usize), + /// Stores a `Thread.SignaleeId` and indicates that sending a signal to this thread + /// is needed in order to cancel. This state is set before going into + /// a blocking operation that needs to get unblocked via signal. _, - const ThreadId = if (std.Thread.use_pthreads) std.c.pthread_t else std.Thread.Id; + const Unpacked = union(enum) { + none, + requested, + acknowledged, + signal_id: Thread.SignaleeId, + }; - fn currentThread() CancelId { - if (std.Thread.use_pthreads) { - return @enumFromInt(@intFromPtr(std.c.pthread_self())); - } else { - return @enumFromInt(std.Thread.getCurrentId()); - } + fn unpack(cs: CancelStatus) Unpacked { + return switch (cs) { + .none => .none, + .requested => .requested, + .acknowledged => .acknowledged, + _ => |signal_id| .{ + .signal_id = if (std.Thread.use_pthreads) + @ptrFromInt(@intFromEnum(signal_id)) + else + @truncate(@intFromEnum(signal_id)), + }, + }; } - fn toThreadId(cancel_id: CancelId) ThreadId { - if (std.Thread.use_pthreads) { - return @ptrFromInt(@intFromEnum(cancel_id)); - } else { - return @intCast(@intFromEnum(cancel_id)); - } + fn fromSignaleeId(signal_id: Thread.SignaleeId) CancelStatus { + return if (std.Thread.use_pthreads) + @enumFromInt(@intFromPtr(signal_id)) + else + @enumFromInt(signal_id); } }; const Closure = struct { start: Start, node: std.SinglyLinkedList.Node = .{}, - cancel_tid: CancelId, + cancel_status: CancelStatus, - const Start = *const fn (*Closure) void; + const Start = *const fn (*Closure, *Threaded) void; - fn requestCancel(closure: *Closure) void { - switch (@atomicRmw(CancelId, &closure.cancel_tid, .Xchg, .canceling, .acq_rel)) { - .none, .canceling => {}, - else => |tid| { - if (std.Thread.use_pthreads) { - const rc = std.c.pthread_kill(tid.toThreadId(), .IO); - if (is_debug) assert(rc == 0); - } else if (native_os == .linux) { - _ = std.os.linux.tgkill(std.os.linux.getpid(), @bitCast(tid.toThreadId()), .IO); - } - }, + fn requestCancel(closure: *Closure, t: *Threaded) void { + var signal_id = switch (@atomicRmw(CancelStatus, &closure.cancel_status, .Xchg, .requested, .monotonic).unpack()) { + .none, .acknowledged, .requested => return, + .signal_id => |signal_id| signal_id, + }; + // The task will enter a blocking syscall before checking for cancellation again. + // We can send a signal to interrupt the syscall, but if it arrives before + // the syscall instruction, it will be missed. Therefore, this code tries + // again until the cancellation request is acknowledged. + + // 1 << 10 ns is about 1 microsecond, approximately syscall overhead. + // 1 << 20 ns is about 1 millisecond. + // 1 << 30 ns is about 1 second. + // + // On a heavily loaded Linux 6.17.5, I observed a maximum of 20 + // attempts not acknowledged before the timeout (including exponential + // backoff) was sufficient, despite the heavy load. + const max_attempts = 22; + + for (0..max_attempts) |attempt_index| { + if (std.Thread.use_pthreads) { + if (std.c.pthread_kill(signal_id, .IO) != 0) return; + } else if (native_os == .linux) { + const pid: posix.pid_t = p: { + const cached_pid = @atomicLoad(Pid, &t.pid, .monotonic); + if (cached_pid != .unknown) break :p @intFromEnum(cached_pid); + const pid = std.os.linux.getpid(); + @atomicStore(Pid, &t.pid, @enumFromInt(pid), .monotonic); + break :p pid; + }; + if (std.os.linux.tgkill(pid, @bitCast(signal_id), .IO) != 0) return; + } else { + return; + } + + if (t.robust_cancel != .enabled) return; + + var timespec: posix.timespec = .{ + .sec = 0, + .nsec = @as(isize, 1) << @intCast(attempt_index), + }; + if (native_os == .linux) { + _ = std.os.linux.clock_nanosleep(posix.CLOCK.MONOTONIC, .{ .ABSTIME = false }, ×pec, ×pec); + } else { + _ = posix.system.nanosleep(×pec, ×pec); + } + + switch (@atomicRmw(CancelStatus, &closure.cancel_status, .Xchg, .requested, .monotonic).unpack()) { + .requested => continue, // Retry needed in case other thread hasn't yet entered the syscall. + .none, .acknowledged => return, + .signal_id => |new_signal_id| signal_id = new_signal_id, + } } } }; @@ -136,6 +296,9 @@ pub fn init( .old_sig_io = undefined, .old_sig_pipe = undefined, .have_signal_handler = false, + .main_thread = .{ + .signal_id = Thread.currentSignalId(), + }, }; if (posix.Sigaction != void) { @@ -169,6 +332,7 @@ pub const init_single_threaded: Threaded = .{ .old_sig_io = undefined, .old_sig_pipe = undefined, .have_signal_handler = false, + .main_thread = .{ .signal_id = undefined }, }; pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void { @@ -201,6 +365,11 @@ fn join(t: *Threaded) void { } fn worker(t: *Threaded) void { + var thread: Thread = .{ + .signal_id = Thread.currentSignalId(), + }; + Thread.current = &thread; + defer t.wait_group.finish(); t.mutex.lock(); @@ -210,7 +379,7 @@ fn worker(t: *Threaded) void { while (t.run_queue.popFirst()) |closure_node| { t.mutex.unlock(); const closure: *Closure = @fieldParentPtr("node", closure_node); - closure.start(closure); + closure.start(closure, t); t.mutex.lock(); t.busy_count -= 1; } @@ -227,7 +396,6 @@ pub fn io(t: *Threaded) Io { .concurrent = concurrent, .await = await, .cancel = cancel, - .cancelRequested = cancelRequested, .select = select, .groupAsync = groupAsync, @@ -324,7 +492,6 @@ pub fn ioBasic(t: *Threaded) Io { .concurrent = concurrent, .await = await, .cancel = cancel, - .cancelRequested = cancelRequested, .select = select, .groupAsync = groupAsync, @@ -418,24 +585,12 @@ const AsyncClosure = struct { const done_reset_event: *ResetEvent = @ptrFromInt(@alignOf(ResetEvent)); - fn start(closure: *Closure) void { + fn start(closure: *Closure, t: *Threaded) void { const ac: *AsyncClosure = @alignCast(@fieldParentPtr("closure", closure)); - const tid: CancelId = .currentThread(); - if (@cmpxchgStrong(CancelId, &closure.cancel_tid, .none, tid, .acq_rel, .acquire)) |cancel_tid| { - assert(cancel_tid == .canceling); - // Even though we already know the task is canceled, we must still - // run the closure in order to make the return value valid and in - // case there are side effects. - } - current_closure = closure; + const current_thread = Thread.getCurrent(t); + current_thread.current_closure = closure; ac.func(ac.contextPointer(), ac.resultPointer()); - current_closure = null; - - // In case a cancel happens after successful task completion, prevents - // signal from being delivered to the thread in `requestCancel`. - if (@cmpxchgStrong(CancelId, &closure.cancel_tid, tid, .none, .acq_rel, .acquire)) |cancel_tid| { - assert(cancel_tid == .canceling); - } + current_thread.current_closure = null; if (@atomicRmw(?*ResetEvent, &ac.select_condition, .Xchg, done_reset_event, .release)) |select_reset| { assert(select_reset != done_reset_event); @@ -476,7 +631,7 @@ const AsyncClosure = struct { const actual_result_offset = actual_result_addr - @intFromPtr(ac); ac.* = .{ .closure = .{ - .cancel_tid = .none, + .cancel_status = .none, .start = start, }, .func = func, @@ -493,7 +648,7 @@ const AsyncClosure = struct { fn waitAndDeinit(ac: *AsyncClosure, t: *Threaded, result: []u8) void { ac.reset_event.wait(t) catch |err| switch (err) { error.Canceled => { - ac.closure.requestCancel(); + ac.closure.requestCancel(t); ac.reset_event.waitUncancelable(); }, }; @@ -604,7 +759,6 @@ fn concurrent( const GroupClosure = struct { closure: Closure, - t: *Threaded, group: *Io.Group, /// Points to sibling `GroupClosure`. Used for walking the group to cancel all. node: std.SinglyLinkedList.Node, @@ -612,26 +766,15 @@ const GroupClosure = struct { context_alignment: Alignment, alloc_len: usize, - fn start(closure: *Closure) void { + fn start(closure: *Closure, t: *Threaded) void { const gc: *GroupClosure = @alignCast(@fieldParentPtr("closure", closure)); - const tid: CancelId = .currentThread(); + const current_thread = Thread.getCurrent(t); const group = gc.group; const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state); const reset_event: *ResetEvent = @ptrCast(&group.context); - if (@cmpxchgStrong(CancelId, &closure.cancel_tid, .none, tid, .acq_rel, .acquire)) |cancel_tid| { - assert(cancel_tid == .canceling); - // Even though we already know the task is canceled, we must still - // run the closure in case there are side effects. - } - current_closure = closure; + current_thread.current_closure = closure; gc.func(group, gc.contextPointer()); - current_closure = null; - - // In case a cancel happens after successful task completion, prevents - // signal from being delivered to the thread in `requestCancel`. - if (@cmpxchgStrong(CancelId, &closure.cancel_tid, tid, .none, .acq_rel, .acquire)) |cancel_tid| { - assert(cancel_tid == .canceling); - } + current_thread.current_closure = null; const prev_state = group_state.fetchSub(sync_one_pending, .acq_rel); assert((prev_state / sync_one_pending) > 0); @@ -647,7 +790,6 @@ const GroupClosure = struct { /// Does not initialize the `node` field. fn init( gpa: Allocator, - t: *Threaded, group: *Io.Group, context: []const u8, context_alignment: Alignment, @@ -662,10 +804,9 @@ const GroupClosure = struct { gc.* = .{ .closure = .{ - .cancel_tid = .none, + .cancel_status = .none, .start = start, }, - .t = t, .group = group, .node = undefined, .func = func, @@ -696,7 +837,7 @@ fn groupAsync( if (builtin.single_threaded) return start(group, context.ptr); const gpa = t.allocator; - const gc = GroupClosure.init(gpa, t, group, context, context_alignment, start) catch + const gc = GroupClosure.init(gpa, group, context, context_alignment, start) catch return start(group, context.ptr); t.mutex.lock(); @@ -752,7 +893,7 @@ fn groupConcurrent( const t: *Threaded = @ptrCast(@alignCast(userdata)); const gpa = t.allocator; - const gc = GroupClosure.init(gpa, t, group, context, context_alignment, start) catch + const gc = GroupClosure.init(gpa, group, context, context_alignment, start) catch return error.ConcurrencyUnavailable; t.mutex.lock(); @@ -806,7 +947,7 @@ fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void { var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token)); while (true) { const gc: *GroupClosure = @fieldParentPtr("node", node); - gc.closure.requestCancel(); + gc.closure.requestCancel(t); node = node.next orelse break; } reset_event.waitUncancelable(); @@ -832,7 +973,7 @@ fn groupCancel(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void var node: *std.SinglyLinkedList.Node = @ptrCast(@alignCast(token)); while (true) { const gc: *GroupClosure = @fieldParentPtr("node", node); - gc.closure.requestCancel(); + gc.closure.requestCancel(t); node = node.next orelse break; } } @@ -875,30 +1016,20 @@ fn cancel( _ = result_alignment; const t: *Threaded = @ptrCast(@alignCast(userdata)); const ac: *AsyncClosure = @ptrCast(@alignCast(any_future)); - ac.closure.requestCancel(); + ac.closure.requestCancel(t); ac.waitAndDeinit(t, result); } -fn cancelRequested(userdata: ?*anyopaque) bool { - const t: *Threaded = @ptrCast(@alignCast(userdata)); - _ = t; - const closure = current_closure orelse return false; - return @atomicLoad(CancelId, &closure.cancel_tid, .acquire) == .canceling; -} - -fn checkCancel(t: *Threaded) error{Canceled}!void { - if (cancelRequested(t)) return error.Canceled; -} - fn mutexLock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) Io.Cancelable!void { if (builtin.single_threaded) unreachable; // Interface should have prevented this. if (native_os == .netbsd) @panic("TODO"); const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); if (prev_state == .contended) { - try futexWait(t, @ptrCast(&mutex.state), @intFromEnum(Io.Mutex.State.contended)); + try futexWait(current_thread, @ptrCast(&mutex.state), @intFromEnum(Io.Mutex.State.contended)); } while (@atomicRmw(Io.Mutex.State, &mutex.state, .Xchg, .contended, .acquire) != .unlocked) { - try futexWait(t, @ptrCast(&mutex.state), @intFromEnum(Io.Mutex.State.contended)); + try futexWait(current_thread, @ptrCast(&mutex.state), @intFromEnum(Io.Mutex.State.contended)); } } @@ -960,6 +1091,7 @@ fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) I if (builtin.single_threaded) unreachable; // Deadlock. if (native_os == .netbsd) @panic("TODO"); const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const t_io = ioBasic(t); comptime assert(@TypeOf(cond.state) == u64); const ints: *[2]std.atomic.Value(u32) = @ptrCast(&cond.state); @@ -988,7 +1120,7 @@ fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) I defer mutex.lockUncancelable(t_io); while (true) { - try futexWait(t, cond_epoch, epoch); + try futexWait(current_thread, cond_epoch, epoch); epoch = cond_epoch.load(.acquire); state = cond_state.load(.monotonic); @@ -1074,35 +1206,46 @@ const dirMake = switch (native_os) { fn dirMakePosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); var path_buffer: [posix.PATH_MAX]u8 = undefined; const sub_path_posix = try pathToPosix(sub_path, &path_buffer); + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, mode))) { - .SUCCESS => return, - .INTR => continue, - .CANCELED => return error.Canceled, - - .ACCES => return error.AccessDenied, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .PERM => return error.PermissionDenied, - .DQUOT => return error.DiskQuota, - .EXIST => return error.PathAlreadyExists, - .FAULT => |err| return errnoBug(err), - .LOOP => return error.SymLinkLoop, - .MLINK => return error.LinkQuotaExceeded, - .NAMETOOLONG => return error.NameTooLong, - .NOENT => return error.FileNotFound, - .NOMEM => return error.SystemResources, - .NOSPC => return error.NoSpaceLeft, - .NOTDIR => return error.NotDir, - .ROFS => return error.ReadOnlyFileSystem, - // dragonfly: when dir_fd is unlinked from filesystem - .NOTCONN => return error.FileNotFound, - .ILSEQ => return error.BadPathName, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ACCES => return error.AccessDenied, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .PERM => return error.PermissionDenied, + .DQUOT => return error.DiskQuota, + .EXIST => return error.PathAlreadyExists, + .FAULT => |err| return errnoBug(err), + .LOOP => return error.SymLinkLoop, + .MLINK => return error.LinkQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NOENT => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .NOSPC => return error.NoSpaceLeft, + .NOTDIR => return error.NotDir, + .ROFS => return error.ReadOnlyFileSystem, + // dragonfly: when dir_fd is unlinked from filesystem + .NOTCONN => return error.FileNotFound, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -1110,37 +1253,49 @@ fn dirMakePosix(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: fn dirMakeWasi(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void { if (builtin.link_libc) return dirMakePosix(userdata, dir, sub_path, mode); const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (std.os.wasi.path_create_directory(dir.handle, sub_path.ptr, sub_path.len)) { - .SUCCESS => return, - .INTR => continue, - .CANCELED => return error.Canceled, - - .ACCES => return error.AccessDenied, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .PERM => return error.PermissionDenied, - .DQUOT => return error.DiskQuota, - .EXIST => return error.PathAlreadyExists, - .FAULT => |err| return errnoBug(err), - .LOOP => return error.SymLinkLoop, - .MLINK => return error.LinkQuotaExceeded, - .NAMETOOLONG => return error.NameTooLong, - .NOENT => return error.FileNotFound, - .NOMEM => return error.SystemResources, - .NOSPC => return error.NoSpaceLeft, - .NOTDIR => return error.NotDir, - .ROFS => return error.ReadOnlyFileSystem, - .NOTCAPABLE => return error.AccessDenied, - .ILSEQ => return error.BadPathName, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ACCES => return error.AccessDenied, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .PERM => return error.PermissionDenied, + .DQUOT => return error.DiskQuota, + .EXIST => return error.PathAlreadyExists, + .FAULT => |err| return errnoBug(err), + .LOOP => return error.SymLinkLoop, + .MLINK => return error.LinkQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NOENT => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .NOSPC => return error.NoSpaceLeft, + .NOTDIR => return error.NotDir, + .ROFS => return error.ReadOnlyFileSystem, + .NOTCAPABLE => return error.AccessDenied, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } fn dirMakeWindows(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8, mode: Io.Dir.Mode) Io.Dir.MakeError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); - try t.checkCancel(); + const current_thread = Thread.getCurrent(t); + try current_thread.checkCancel(); const sub_path_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path); _ = mode; @@ -1213,6 +1368,7 @@ fn dirMakeOpenPathWindows( options: Io.Dir.OpenOptions, ) Io.Dir.MakeOpenPathError!Io.Dir { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const w = windows; const access_mask = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE | @@ -1226,7 +1382,7 @@ fn dirMakeOpenPathWindows( }; while (true) { - try t.checkCancel(); + try current_thread.checkCancel(); const sub_path_w_array = try w.sliceToPrefixedFileW(dir.handle, component.path); const sub_path_w = sub_path_w_array.span(); @@ -1328,8 +1484,7 @@ fn dirMakeOpenPathWasi( fn dirStat(userdata: ?*anyopaque, dir: Io.Dir) Io.Dir.StatError!Io.Dir.Stat { const t: *Threaded = @ptrCast(@alignCast(userdata)); - try t.checkCancel(); - + _ = t; _ = dir; @panic("TODO implement dirStat"); } @@ -1348,6 +1503,7 @@ fn dirStatPathLinux( options: Io.Dir.StatPathOptions, ) Io.Dir.StatPathError!Io.File.Stat { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const linux = std.os.linux; var path_buffer: [posix.PATH_MAX]u8 = undefined; @@ -1356,8 +1512,8 @@ fn dirStatPathLinux( const flags: u32 = linux.AT.NO_AUTOMOUNT | @as(u32, if (!options.follow_symlinks) linux.AT.SYMLINK_NOFOLLOW else 0); + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); var statx = std.mem.zeroes(linux.Statx); const rc = linux.statx( dir.handle, @@ -1367,20 +1523,30 @@ fn dirStatPathLinux( &statx, ); switch (linux.errno(rc)) { - .SUCCESS => return statFromLinux(&statx), - .INTR => continue, - .CANCELED => return error.Canceled, - - .ACCES => return error.AccessDenied, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .FAULT => |err| return errnoBug(err), - .INVAL => |err| return errnoBug(err), - .LOOP => return error.SymLinkLoop, - .NAMETOOLONG => |err| return errnoBug(err), // Handled by pathToPosix() above. - .NOENT => return error.FileNotFound, - .NOTDIR => return error.NotDir, - .NOMEM => return error.SystemResources, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return statFromLinux(&statx); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ACCES => return error.AccessDenied, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .FAULT => |err| return errnoBug(err), + .INVAL => |err| return errnoBug(err), + .LOOP => return error.SymLinkLoop, + .NAMETOOLONG => |err| return errnoBug(err), // Handled by pathToPosix() above. + .NOENT => return error.FileNotFound, + .NOTDIR => return error.NotDir, + .NOMEM => return error.SystemResources, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -1392,32 +1558,43 @@ fn dirStatPathPosix( options: Io.Dir.StatPathOptions, ) Io.Dir.StatPathError!Io.File.Stat { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); var path_buffer: [posix.PATH_MAX]u8 = undefined; const sub_path_posix = try pathToPosix(sub_path, &path_buffer); const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); var stat = std.mem.zeroes(posix.Stat); switch (posix.errno(fstatat_sym(dir.handle, sub_path_posix, &stat, flags))) { - .SUCCESS => return statFromPosix(&stat), - .INTR => continue, - .CANCELED => return error.Canceled, - - .INVAL => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .NOMEM => return error.SystemResources, - .ACCES => return error.AccessDenied, - .PERM => return error.PermissionDenied, - .FAULT => |err| return errnoBug(err), - .NAMETOOLONG => return error.NameTooLong, - .LOOP => return error.SymLinkLoop, - .NOENT => return error.FileNotFound, - .NOTDIR => return error.FileNotFound, - .ILSEQ => return error.BadPathName, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return statFromPosix(&stat); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .INVAL => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .NOMEM => return error.SystemResources, + .ACCES => return error.AccessDenied, + .PERM => return error.PermissionDenied, + .FAULT => |err| return errnoBug(err), + .NAMETOOLONG => return error.NameTooLong, + .LOOP => return error.SymLinkLoop, + .NOENT => return error.FileNotFound, + .NOTDIR => return error.FileNotFound, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -1444,29 +1621,40 @@ fn dirStatPathWasi( ) Io.Dir.StatPathError!Io.File.Stat { if (builtin.link_libc) return dirStatPathPosix(userdata, dir, sub_path, options); const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const wasi = std.os.wasi; const flags: wasi.lookupflags_t = .{ .SYMLINK_FOLLOW = options.follow_symlinks, }; var stat: wasi.filestat_t = undefined; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) { - .SUCCESS => return statFromWasi(&stat), - .INTR => continue, - .CANCELED => return error.Canceled, - - .INVAL => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .NOMEM => return error.SystemResources, - .ACCES => return error.AccessDenied, - .FAULT => |err| return errnoBug(err), - .NAMETOOLONG => return error.NameTooLong, - .NOENT => return error.FileNotFound, - .NOTDIR => return error.FileNotFound, - .NOTCAPABLE => return error.AccessDenied, - .ILSEQ => return error.BadPathName, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return statFromWasi(&stat); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .INVAL => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .NOMEM => return error.SystemResources, + .ACCES => return error.AccessDenied, + .FAULT => |err| return errnoBug(err), + .NAMETOOLONG => return error.NameTooLong, + .NOENT => return error.FileNotFound, + .NOTDIR => return error.FileNotFound, + .NOTCAPABLE => return error.AccessDenied, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -1480,31 +1668,44 @@ const fileStat = switch (native_os) { fn fileStatPosix(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); if (posix.Stat == void) return error.Streaming; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); var stat = std.mem.zeroes(posix.Stat); switch (posix.errno(fstat_sym(file.handle, &stat))) { - .SUCCESS => return statFromPosix(&stat), - .INTR => continue, - .CANCELED => return error.Canceled, - - .INVAL => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .NOMEM => return error.SystemResources, - .ACCES => return error.AccessDenied, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return statFromPosix(&stat); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .INVAL => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .NOMEM => return error.SystemResources, + .ACCES => return error.AccessDenied, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } fn fileStatLinux(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const linux = std.os.linux; + + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); var statx = std.mem.zeroes(linux.Statx); const rc = linux.statx( file.handle, @@ -1514,27 +1715,38 @@ fn fileStatLinux(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File &statx, ); switch (linux.errno(rc)) { - .SUCCESS => return statFromLinux(&statx), - .INTR => continue, - .CANCELED => return error.Canceled, - - .ACCES => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .FAULT => |err| return errnoBug(err), - .INVAL => |err| return errnoBug(err), - .LOOP => |err| return errnoBug(err), - .NAMETOOLONG => |err| return errnoBug(err), - .NOENT => |err| return errnoBug(err), - .NOMEM => return error.SystemResources, - .NOTDIR => |err| return errnoBug(err), - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return statFromLinux(&statx); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ACCES => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .FAULT => |err| return errnoBug(err), + .INVAL => |err| return errnoBug(err), + .LOOP => |err| return errnoBug(err), + .NAMETOOLONG => |err| return errnoBug(err), + .NOENT => |err| return errnoBug(err), + .NOMEM => return error.SystemResources, + .NOTDIR => |err| return errnoBug(err), + else => |err| return posix.unexpectedErrno(err), + } + }, } } } fn fileStatWindows(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat { const t: *Threaded = @ptrCast(@alignCast(userdata)); - try t.checkCancel(); + const current_thread = Thread.getCurrent(t); + try current_thread.checkCancel(); var io_status_block: windows.IO_STATUS_BLOCK = undefined; var info: windows.FILE_ALL_INFORMATION = undefined; @@ -1581,21 +1793,34 @@ fn fileStatWindows(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.Fi fn fileStatWasi(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat { if (builtin.link_libc) return fileStatPosix(userdata, file); + const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); + + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); var stat: std.os.wasi.filestat_t = undefined; switch (std.os.wasi.fd_filestat_get(file.handle, &stat)) { - .SUCCESS => return statFromWasi(&stat), - .INTR => continue, - .CANCELED => return error.Canceled, - - .INVAL => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .NOMEM => return error.SystemResources, - .ACCES => return error.AccessDenied, - .NOTCAPABLE => return error.AccessDenied, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return statFromWasi(&stat); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .INVAL => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .NOMEM => return error.SystemResources, + .ACCES => return error.AccessDenied, + .NOTCAPABLE => return error.AccessDenied, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -1613,6 +1838,7 @@ fn dirAccessPosix( options: Io.Dir.AccessOptions, ) Io.Dir.AccessError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); var path_buffer: [posix.PATH_MAX]u8 = undefined; const sub_path_posix = try pathToPosix(sub_path, &path_buffer); @@ -1624,27 +1850,37 @@ fn dirAccessPosix( @as(u32, if (options.write) posix.W_OK else 0) | @as(u32, if (options.execute) posix.X_OK else 0); + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (posix.errno(posix.system.faccessat(dir.handle, sub_path_posix, mode, flags))) { - .SUCCESS => return, - .INTR => continue, - .CANCELED => return error.Canceled, - - .ACCES => return error.AccessDenied, - .PERM => return error.PermissionDenied, - .ROFS => return error.ReadOnlyFileSystem, - .LOOP => return error.SymLinkLoop, - .TXTBSY => return error.FileBusy, - .NOTDIR => return error.FileNotFound, - .NOENT => return error.FileNotFound, - .NAMETOOLONG => return error.NameTooLong, - .INVAL => |err| return errnoBug(err), - .FAULT => |err| return errnoBug(err), - .IO => return error.InputOutput, - .NOMEM => return error.SystemResources, - .ILSEQ => return error.BadPathName, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ACCES => return error.AccessDenied, + .PERM => return error.PermissionDenied, + .ROFS => return error.ReadOnlyFileSystem, + .LOOP => return error.SymLinkLoop, + .TXTBSY => return error.FileBusy, + .NOTDIR => return error.FileNotFound, + .NOENT => return error.FileNotFound, + .NAMETOOLONG => return error.NameTooLong, + .INVAL => |err| return errnoBug(err), + .FAULT => |err| return errnoBug(err), + .IO => return error.InputOutput, + .NOMEM => return error.SystemResources, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -1657,29 +1893,41 @@ fn dirAccessWasi( ) Io.Dir.AccessError!void { if (builtin.link_libc) return dirAccessPosix(userdata, dir, sub_path, options); const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const wasi = std.os.wasi; const flags: wasi.lookupflags_t = .{ .SYMLINK_FOLLOW = options.follow_symlinks, }; var stat: wasi.filestat_t = undefined; - while (true) { - try t.checkCancel(); - switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) { - .SUCCESS => break, - .INTR => continue, - .CANCELED => return error.Canceled, - .INVAL => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .NOMEM => return error.SystemResources, - .ACCES => return error.AccessDenied, - .FAULT => |err| return errnoBug(err), - .NAMETOOLONG => return error.NameTooLong, - .NOENT => return error.FileNotFound, - .NOTDIR => return error.FileNotFound, - .NOTCAPABLE => return error.AccessDenied, - .ILSEQ => return error.BadPathName, - else => |err| return posix.unexpectedErrno(err), + try current_thread.beginSyscall(); + while (true) { + switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) { + .SUCCESS => { + current_thread.endSyscall(); + break; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .INVAL => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .NOMEM => return error.SystemResources, + .ACCES => return error.AccessDenied, + .FAULT => |err| return errnoBug(err), + .NAMETOOLONG => return error.NameTooLong, + .NOENT => return error.FileNotFound, + .NOTDIR => return error.FileNotFound, + .NOTCAPABLE => return error.AccessDenied, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + } + }, } } @@ -1717,7 +1965,8 @@ fn dirAccessWindows( options: Io.Dir.AccessOptions, ) Io.Dir.AccessError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); - try t.checkCancel(); + const current_thread = Thread.getCurrent(t); + try current_thread.checkCancel(); _ = options; // TODO @@ -1768,6 +2017,7 @@ fn dirCreateFilePosix( flags: Io.File.CreateFlags, ) Io.File.OpenError!Io.File { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); var path_buffer: [posix.PATH_MAX]u8 = undefined; const sub_path_posix = try pathToPosix(sub_path, &path_buffer); @@ -1796,40 +2046,50 @@ fn dirCreateFilePosix( }, }; + try current_thread.beginSyscall(); const fd: posix.fd_t = while (true) { - try t.checkCancel(); const rc = openat_sym(dir.handle, sub_path_posix, os_flags, flags.mode); switch (posix.errno(rc)) { - .SUCCESS => break @intCast(rc), - .INTR => continue, - .CANCELED => return error.Canceled, - - .FAULT => |err| return errnoBug(err), - .INVAL => return error.BadPathName, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .ACCES => return error.AccessDenied, - .FBIG => return error.FileTooBig, - .OVERFLOW => return error.FileTooBig, - .ISDIR => return error.IsDir, - .LOOP => return error.SymLinkLoop, - .MFILE => return error.ProcessFdQuotaExceeded, - .NAMETOOLONG => return error.NameTooLong, - .NFILE => return error.SystemFdQuotaExceeded, - .NODEV => return error.NoDevice, - .NOENT => return error.FileNotFound, - .SRCH => return error.ProcessNotFound, - .NOMEM => return error.SystemResources, - .NOSPC => return error.NoSpaceLeft, - .NOTDIR => return error.NotDir, - .PERM => return error.PermissionDenied, - .EXIST => return error.PathAlreadyExists, - .BUSY => return error.DeviceBusy, - .OPNOTSUPP => return error.FileLocksNotSupported, - .AGAIN => return error.WouldBlock, - .TXTBSY => return error.FileBusy, - .NXIO => return error.NoDevice, - .ILSEQ => return error.BadPathName, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + break @intCast(rc); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .FAULT => |err| return errnoBug(err), + .INVAL => return error.BadPathName, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .ACCES => return error.AccessDenied, + .FBIG => return error.FileTooBig, + .OVERFLOW => return error.FileTooBig, + .ISDIR => return error.IsDir, + .LOOP => return error.SymLinkLoop, + .MFILE => return error.ProcessFdQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NFILE => return error.SystemFdQuotaExceeded, + .NODEV => return error.NoDevice, + .NOENT => return error.FileNotFound, + .SRCH => return error.ProcessNotFound, + .NOMEM => return error.SystemResources, + .NOSPC => return error.NoSpaceLeft, + .NOTDIR => return error.NotDir, + .PERM => return error.PermissionDenied, + .EXIST => return error.PathAlreadyExists, + .BUSY => return error.DeviceBusy, + .OPNOTSUPP => return error.FileLocksNotSupported, + .AGAIN => return error.WouldBlock, + .TXTBSY => return error.FileBusy, + .NXIO => return error.NoDevice, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + } + }, } }; errdefer posix.close(fd); @@ -1841,42 +2101,71 @@ fn dirCreateFilePosix( .shared => posix.LOCK.SH | lock_nonblocking, .exclusive => posix.LOCK.EX | lock_nonblocking, }; - while (true) { - try t.checkCancel(); - switch (posix.errno(posix.system.flock(fd, lock_flags))) { - .SUCCESS => break, - .INTR => continue, - .CANCELED => return error.Canceled, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .INVAL => |err| return errnoBug(err), // invalid parameters - .NOLCK => return error.SystemResources, - .AGAIN => return error.WouldBlock, - .OPNOTSUPP => return error.FileLocksNotSupported, - else => |err| return posix.unexpectedErrno(err), + try current_thread.beginSyscall(); + while (true) { + switch (posix.errno(posix.system.flock(fd, lock_flags))) { + .SUCCESS => { + current_thread.endSyscall(); + break; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .INVAL => |err| return errnoBug(err), // invalid parameters + .NOLCK => return error.SystemResources, + .AGAIN => return error.WouldBlock, + .OPNOTSUPP => return error.FileLocksNotSupported, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } if (have_flock_open_flags and flags.lock_nonblocking) { + try current_thread.beginSyscall(); var fl_flags: usize = while (true) { - try t.checkCancel(); const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0)); switch (posix.errno(rc)) { - .SUCCESS => break @intCast(rc), - .INTR => continue, - .CANCELED => return error.Canceled, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + break @intCast(rc); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + else => |err| { + current_thread.endSyscall(); + return posix.unexpectedErrno(err); + }, } }; + fl_flags |= @as(usize, 1 << @bitOffsetOf(posix.O, "NONBLOCK")); + + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFL, fl_flags))) { - .SUCCESS => break, - .INTR => continue, - .CANCELED => return error.Canceled, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + break; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + else => |err| { + current_thread.endSyscall(); + return posix.unexpectedErrno(err); + }, } } } @@ -1892,7 +2181,8 @@ fn dirCreateFileWindows( ) Io.File.OpenError!Io.File { const w = windows; const t: *Threaded = @ptrCast(@alignCast(userdata)); - try t.checkCancel(); + const current_thread = Thread.getCurrent(t); + try current_thread.checkCancel(); const sub_path_w_array = try w.sliceToPrefixedFileW(dir.handle, sub_path); const sub_path_w = sub_path_w_array.span(); @@ -1939,6 +2229,7 @@ fn dirCreateFileWasi( flags: Io.File.CreateFlags, ) Io.File.OpenError!Io.File { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const wasi = std.os.wasi; const lookup_flags: wasi.lookupflags_t = .{}; const oflags: wasi.oflags_t = .{ @@ -1966,35 +2257,45 @@ fn dirCreateFileWasi( }; const inheriting: wasi.rights_t = .{}; var fd: posix.fd_t = undefined; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, inheriting, fdflags, &fd)) { - .SUCCESS => return .{ .handle = fd }, - .INTR => continue, - .CANCELED => return error.Canceled, - - .FAULT => |err| return errnoBug(err), - .INVAL => return error.BadPathName, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .ACCES => return error.AccessDenied, - .FBIG => return error.FileTooBig, - .OVERFLOW => return error.FileTooBig, - .ISDIR => return error.IsDir, - .LOOP => return error.SymLinkLoop, - .MFILE => return error.ProcessFdQuotaExceeded, - .NAMETOOLONG => return error.NameTooLong, - .NFILE => return error.SystemFdQuotaExceeded, - .NODEV => return error.NoDevice, - .NOENT => return error.FileNotFound, - .NOMEM => return error.SystemResources, - .NOSPC => return error.NoSpaceLeft, - .NOTDIR => return error.NotDir, - .PERM => return error.PermissionDenied, - .EXIST => return error.PathAlreadyExists, - .BUSY => return error.DeviceBusy, - .NOTCAPABLE => return error.AccessDenied, - .ILSEQ => return error.BadPathName, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return .{ .handle = fd }; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .FAULT => |err| return errnoBug(err), + .INVAL => return error.BadPathName, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .ACCES => return error.AccessDenied, + .FBIG => return error.FileTooBig, + .OVERFLOW => return error.FileTooBig, + .ISDIR => return error.IsDir, + .LOOP => return error.SymLinkLoop, + .MFILE => return error.ProcessFdQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NFILE => return error.SystemFdQuotaExceeded, + .NODEV => return error.NoDevice, + .NOENT => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .NOSPC => return error.NoSpaceLeft, + .NOTDIR => return error.NotDir, + .PERM => return error.PermissionDenied, + .EXIST => return error.PathAlreadyExists, + .BUSY => return error.DeviceBusy, + .NOTCAPABLE => return error.AccessDenied, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -2012,6 +2313,7 @@ fn dirOpenFilePosix( flags: Io.File.OpenFlags, ) Io.File.OpenError!Io.File { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); var path_buffer: [posix.PATH_MAX]u8 = undefined; const sub_path_posix = try pathToPosix(sub_path, &path_buffer); @@ -2048,40 +2350,50 @@ fn dirOpenFilePosix( }, }; + try current_thread.beginSyscall(); const fd: posix.fd_t = while (true) { - try t.checkCancel(); const rc = openat_sym(dir.handle, sub_path_posix, os_flags, @as(posix.mode_t, 0)); switch (posix.errno(rc)) { - .SUCCESS => break @intCast(rc), - .INTR => continue, - .CANCELED => return error.Canceled, - - .FAULT => |err| return errnoBug(err), - .INVAL => return error.BadPathName, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .ACCES => return error.AccessDenied, - .FBIG => return error.FileTooBig, - .OVERFLOW => return error.FileTooBig, - .ISDIR => return error.IsDir, - .LOOP => return error.SymLinkLoop, - .MFILE => return error.ProcessFdQuotaExceeded, - .NAMETOOLONG => return error.NameTooLong, - .NFILE => return error.SystemFdQuotaExceeded, - .NODEV => return error.NoDevice, - .NOENT => return error.FileNotFound, - .SRCH => return error.ProcessNotFound, - .NOMEM => return error.SystemResources, - .NOSPC => return error.NoSpaceLeft, - .NOTDIR => return error.NotDir, - .PERM => return error.PermissionDenied, - .EXIST => return error.PathAlreadyExists, - .BUSY => return error.DeviceBusy, - .OPNOTSUPP => return error.FileLocksNotSupported, - .AGAIN => return error.WouldBlock, - .TXTBSY => return error.FileBusy, - .NXIO => return error.NoDevice, - .ILSEQ => return error.BadPathName, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + break @intCast(rc); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .FAULT => |err| return errnoBug(err), + .INVAL => return error.BadPathName, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .ACCES => return error.AccessDenied, + .FBIG => return error.FileTooBig, + .OVERFLOW => return error.FileTooBig, + .ISDIR => return error.IsDir, + .LOOP => return error.SymLinkLoop, + .MFILE => return error.ProcessFdQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NFILE => return error.SystemFdQuotaExceeded, + .NODEV => return error.NoDevice, + .NOENT => return error.FileNotFound, + .SRCH => return error.ProcessNotFound, + .NOMEM => return error.SystemResources, + .NOSPC => return error.NoSpaceLeft, + .NOTDIR => return error.NotDir, + .PERM => return error.PermissionDenied, + .EXIST => return error.PathAlreadyExists, + .BUSY => return error.DeviceBusy, + .OPNOTSUPP => return error.FileLocksNotSupported, + .AGAIN => return error.WouldBlock, + .TXTBSY => return error.FileBusy, + .NXIO => return error.NoDevice, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + } + }, } }; errdefer posix.close(fd); @@ -2093,42 +2405,72 @@ fn dirOpenFilePosix( .shared => posix.LOCK.SH | lock_nonblocking, .exclusive => posix.LOCK.EX | lock_nonblocking, }; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (posix.errno(posix.system.flock(fd, lock_flags))) { - .SUCCESS => break, - .INTR => continue, - .CANCELED => return error.Canceled, - - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .INVAL => |err| return errnoBug(err), // invalid parameters - .NOLCK => return error.SystemResources, - .AGAIN => return error.WouldBlock, - .OPNOTSUPP => return error.FileLocksNotSupported, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + break; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .INVAL => |err| return errnoBug(err), // invalid parameters + .NOLCK => return error.SystemResources, + .AGAIN => return error.WouldBlock, + .OPNOTSUPP => return error.FileLocksNotSupported, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } if (have_flock_open_flags and flags.lock_nonblocking) { + try current_thread.beginSyscall(); var fl_flags: usize = while (true) { - try t.checkCancel(); const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0)); switch (posix.errno(rc)) { - .SUCCESS => break @intCast(rc), - .INTR => continue, - .CANCELED => return error.Canceled, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + break @intCast(rc); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |err| { + current_thread.endSyscall(); + return posix.unexpectedErrno(err); + }, } }; + fl_flags |= @as(usize, 1 << @bitOffsetOf(posix.O, "NONBLOCK")); + + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFL, fl_flags))) { - .SUCCESS => break, - .INTR => continue, - .CANCELED => return error.Canceled, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + break; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |err| { + current_thread.endSyscall(); + return posix.unexpectedErrno(err); + }, } } } @@ -2158,7 +2500,7 @@ pub fn dirOpenFileWtf16( if (std.mem.eql(u16, sub_path_w, &.{'.'})) return error.IsDir; if (std.mem.eql(u16, sub_path_w, &.{ '.', '.' })) return error.IsDir; const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong; - + const current_thread = Thread.getCurrent(t); const w = windows; var nt_name: w.UNICODE_STRING = .{ @@ -2187,7 +2529,7 @@ pub fn dirOpenFileWtf16( var attempt: u5 = 0; const handle = while (true) { - try t.checkCancel(); + try current_thread.checkCancel(); var result: w.HANDLE = undefined; const rc = w.ntdll.NtCreateFile( @@ -2281,6 +2623,7 @@ fn dirOpenFileWasi( ) Io.File.OpenError!Io.File { if (builtin.link_libc) return dirOpenFilePosix(userdata, dir, sub_path, flags); const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const wasi = std.os.wasi; var base: std.os.wasi.rights_t = .{}; // POLL_FD_READWRITE only grants extra rights if the corresponding FD_READ and/or FD_WRITE @@ -2310,33 +2653,44 @@ fn dirOpenFileWasi( const inheriting: wasi.rights_t = .{}; const fdflags: wasi.fdflags_t = .{}; var fd: posix.fd_t = undefined; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, inheriting, fdflags, &fd)) { - .SUCCESS => return .{ .handle = fd }, - .INTR => continue, - .CANCELED => return error.Canceled, - - .FAULT => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .ACCES => return error.AccessDenied, - .FBIG => return error.FileTooBig, - .OVERFLOW => return error.FileTooBig, - .ISDIR => return error.IsDir, - .LOOP => return error.SymLinkLoop, - .MFILE => return error.ProcessFdQuotaExceeded, - .NFILE => return error.SystemFdQuotaExceeded, - .NODEV => return error.NoDevice, - .NOENT => return error.FileNotFound, - .NOMEM => return error.SystemResources, - .NOTDIR => return error.NotDir, - .PERM => return error.PermissionDenied, - .BUSY => return error.DeviceBusy, - .NOTCAPABLE => return error.AccessDenied, - .NAMETOOLONG => return error.NameTooLong, - .INVAL => return error.BadPathName, - .ILSEQ => return error.BadPathName, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + errdefer posix.close(fd); + current_thread.endSyscall(); + return .{ .handle = fd }; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .FAULT => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .ACCES => return error.AccessDenied, + .FBIG => return error.FileTooBig, + .OVERFLOW => return error.FileTooBig, + .ISDIR => return error.IsDir, + .LOOP => return error.SymLinkLoop, + .MFILE => return error.ProcessFdQuotaExceeded, + .NFILE => return error.SystemFdQuotaExceeded, + .NODEV => return error.NoDevice, + .NOENT => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .NOTDIR => return error.NotDir, + .PERM => return error.PermissionDenied, + .BUSY => return error.DeviceBusy, + .NOTCAPABLE => return error.AccessDenied, + .NAMETOOLONG => return error.NameTooLong, + .INVAL => return error.BadPathName, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -2361,6 +2715,8 @@ fn dirOpenDirPosix( return dirOpenDirWindows(t, dir, sub_path_w.span(), options); } + const current_thread = Thread.getCurrent(t); + var path_buffer: [posix.PATH_MAX]u8 = undefined; const sub_path_posix = try pathToPosix(sub_path, &path_buffer); @@ -2381,31 +2737,41 @@ fn dirOpenDirPosix( if (@hasField(posix.O, "PATH") and !options.iterate) flags.PATH = true; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = openat_sym(dir.handle, sub_path_posix, flags, @as(usize, 0)); switch (posix.errno(rc)) { - .SUCCESS => return .{ .handle = @intCast(rc) }, - .INTR => continue, - .CANCELED => return error.Canceled, - - .FAULT => |err| return errnoBug(err), - .INVAL => return error.BadPathName, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .ACCES => return error.AccessDenied, - .LOOP => return error.SymLinkLoop, - .MFILE => return error.ProcessFdQuotaExceeded, - .NAMETOOLONG => return error.NameTooLong, - .NFILE => return error.SystemFdQuotaExceeded, - .NODEV => return error.NoDevice, - .NOENT => return error.FileNotFound, - .NOMEM => return error.SystemResources, - .NOTDIR => return error.NotDir, - .PERM => return error.PermissionDenied, - .BUSY => return error.DeviceBusy, - .NXIO => return error.NoDevice, - .ILSEQ => return error.BadPathName, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return .{ .handle = @intCast(rc) }; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .FAULT => |err| return errnoBug(err), + .INVAL => return error.BadPathName, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .ACCES => return error.AccessDenied, + .LOOP => return error.SymLinkLoop, + .MFILE => return error.ProcessFdQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NFILE => return error.SystemFdQuotaExceeded, + .NODEV => return error.NoDevice, + .NOENT => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .NOTDIR => return error.NotDir, + .PERM => return error.PermissionDenied, + .BUSY => return error.DeviceBusy, + .NXIO => return error.NoDevice, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -2417,34 +2783,46 @@ fn dirOpenDirHaiku( options: Io.Dir.OpenOptions, ) Io.Dir.OpenError!Io.Dir { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); var path_buffer: [posix.PATH_MAX]u8 = undefined; const sub_path_posix = try pathToPosix(sub_path, &path_buffer); _ = options; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = posix.system._kern_open_dir(dir.handle, sub_path_posix); - if (rc >= 0) return .{ .handle = rc }; + if (rc >= 0) { + current_thread.endSyscall(); + return .{ .handle = rc }; + } switch (@as(posix.E, @enumFromInt(rc))) { - .INTR => continue, - .CANCELED => return error.Canceled, - .FAULT => |err| return errnoBug(err), - .INVAL => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .ACCES => return error.AccessDenied, - .LOOP => return error.SymLinkLoop, - .MFILE => return error.ProcessFdQuotaExceeded, - .NAMETOOLONG => return error.NameTooLong, - .NFILE => return error.SystemFdQuotaExceeded, - .NODEV => return error.NoDevice, - .NOENT => return error.FileNotFound, - .NOMEM => return error.SystemResources, - .NOTDIR => return error.NotDir, - .PERM => return error.PermissionDenied, - .BUSY => return error.DeviceBusy, - else => |err| return posix.unexpectedErrno(err), + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .FAULT => |err| return errnoBug(err), + .INVAL => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .ACCES => return error.AccessDenied, + .LOOP => return error.SymLinkLoop, + .MFILE => return error.ProcessFdQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NFILE => return error.SystemFdQuotaExceeded, + .NODEV => return error.NoDevice, + .NOENT => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .NOTDIR => return error.NotDir, + .PERM => return error.PermissionDenied, + .BUSY => return error.DeviceBusy, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -2455,6 +2833,7 @@ pub fn dirOpenDirWindows( sub_path_w: [:0]const u16, options: Io.Dir.OpenOptions, ) Io.Dir.OpenError!Io.Dir { + const current_thread = Thread.getCurrent(t); const w = windows; // TODO remove some of these flags if options.access_sub_paths is false const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | @@ -2478,7 +2857,7 @@ pub fn dirOpenDirWindows( const open_reparse_point: w.DWORD = if (!options.follow_symlinks) w.FILE_OPEN_REPARSE_POINT else 0x0; var io_status_block: w.IO_STATUS_BLOCK = undefined; var result: Io.Dir = .{ .handle = undefined }; - try t.checkCancel(); + try current_thread.checkCancel(); const rc = w.ntdll.NtCreateFile( &result.handle, access_mask, @@ -2527,6 +2906,7 @@ fn dirOpenDirWasi( ) Io.Dir.OpenError!Io.Dir { if (builtin.link_libc) return dirOpenDirPosix(userdata, dir, sub_path, options); const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const wasi = std.os.wasi; var base: std.os.wasi.rights_t = .{ @@ -2556,31 +2936,40 @@ fn dirOpenDirWasi( const oflags: wasi.oflags_t = .{ .DIRECTORY = true }; const fdflags: wasi.fdflags_t = .{}; var fd: posix.fd_t = undefined; - + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, base, fdflags, &fd)) { - .SUCCESS => return .{ .handle = fd }, - .INTR => continue, - .CANCELED => return error.Canceled, - - .FAULT => |err| return errnoBug(err), - .INVAL => return error.BadPathName, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .ACCES => return error.AccessDenied, - .LOOP => return error.SymLinkLoop, - .MFILE => return error.ProcessFdQuotaExceeded, - .NAMETOOLONG => return error.NameTooLong, - .NFILE => return error.SystemFdQuotaExceeded, - .NODEV => return error.NoDevice, - .NOENT => return error.FileNotFound, - .NOMEM => return error.SystemResources, - .NOTDIR => return error.NotDir, - .PERM => return error.PermissionDenied, - .BUSY => return error.DeviceBusy, - .NOTCAPABLE => return error.AccessDenied, - .ILSEQ => return error.BadPathName, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return .{ .handle = fd }; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .FAULT => |err| return errnoBug(err), + .INVAL => return error.BadPathName, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .ACCES => return error.AccessDenied, + .LOOP => return error.SymLinkLoop, + .MFILE => return error.ProcessFdQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NFILE => return error.SystemFdQuotaExceeded, + .NODEV => return error.NoDevice, + .NOENT => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .NOTDIR => return error.NotDir, + .PERM => return error.PermissionDenied, + .BUSY => return error.DeviceBusy, + .NOTCAPABLE => return error.AccessDenied, + .ILSEQ => return error.BadPathName, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -2598,6 +2987,7 @@ const fileReadStreaming = switch (native_os) { fn fileReadStreamingPosix(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File.Reader.Error!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined; var i: usize = 0; @@ -2611,59 +3001,82 @@ fn fileReadStreamingPosix(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io const dest = iovecs_buffer[0..i]; assert(dest[0].len > 0); - if (native_os == .wasi and !builtin.link_libc) while (true) { - try t.checkCancel(); - var nread: usize = undefined; - switch (std.os.wasi.fd_read(file.handle, dest.ptr, dest.len, &nread)) { - .SUCCESS => return nread, - .INTR => continue, - .CANCELED => return error.Canceled, - - .INVAL => |err| return errnoBug(err), - .FAULT => |err| return errnoBug(err), - .BADF => return error.NotOpenForReading, // File operation on directory. - .IO => return error.InputOutput, - .ISDIR => return error.IsDir, - .NOBUFS => return error.SystemResources, - .NOMEM => return error.SystemResources, - .NOTCONN => return error.SocketUnconnected, - .CONNRESET => return error.ConnectionResetByPeer, - .TIMEDOUT => return error.Timeout, - .NOTCAPABLE => return error.AccessDenied, - else => |err| return posix.unexpectedErrno(err), + if (native_os == .wasi and !builtin.link_libc) { + try current_thread.beginSyscall(); + while (true) { + var nread: usize = undefined; + switch (std.os.wasi.fd_read(file.handle, dest.ptr, dest.len, &nread)) { + .SUCCESS => { + current_thread.endSyscall(); + return nread; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .INVAL => |err| return errnoBug(err), + .FAULT => |err| return errnoBug(err), + .BADF => return error.NotOpenForReading, // File operation on directory. + .IO => return error.InputOutput, + .ISDIR => return error.IsDir, + .NOBUFS => return error.SystemResources, + .NOMEM => return error.SystemResources, + .NOTCONN => return error.SocketUnconnected, + .CONNRESET => return error.ConnectionResetByPeer, + .TIMEDOUT => return error.Timeout, + .NOTCAPABLE => return error.AccessDenied, + else => |err| return posix.unexpectedErrno(err), + } + }, + } } - }; + } + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = posix.system.readv(file.handle, dest.ptr, @intCast(dest.len)); switch (posix.errno(rc)) { - .SUCCESS => return @intCast(rc), - .INTR => continue, - .CANCELED => return error.Canceled, - - .INVAL => |err| return errnoBug(err), - .FAULT => |err| return errnoBug(err), - .SRCH => return error.ProcessNotFound, - .AGAIN => return error.WouldBlock, - .BADF => |err| { - if (native_os == .wasi) return error.NotOpenForReading; // File operation on directory. - return errnoBug(err); // File descriptor used after closed. + .SUCCESS => { + current_thread.endSyscall(); + return @intCast(rc); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .INVAL => |err| return errnoBug(err), + .FAULT => |err| return errnoBug(err), + .SRCH => return error.ProcessNotFound, + .AGAIN => return error.WouldBlock, + .BADF => |err| { + if (native_os == .wasi) return error.NotOpenForReading; // File operation on directory. + return errnoBug(err); // File descriptor used after closed. + }, + .IO => return error.InputOutput, + .ISDIR => return error.IsDir, + .NOBUFS => return error.SystemResources, + .NOMEM => return error.SystemResources, + .NOTCONN => return error.SocketUnconnected, + .CONNRESET => return error.ConnectionResetByPeer, + .TIMEDOUT => return error.Timeout, + else => |err| return posix.unexpectedErrno(err), + } }, - .IO => return error.InputOutput, - .ISDIR => return error.IsDir, - .NOBUFS => return error.SystemResources, - .NOMEM => return error.SystemResources, - .NOTCONN => return error.SocketUnconnected, - .CONNRESET => return error.ConnectionResetByPeer, - .TIMEDOUT => return error.Timeout, - else => |err| return posix.unexpectedErrno(err), } } } fn fileReadStreamingWindows(userdata: ?*anyopaque, file: Io.File, data: [][]u8) Io.File.Reader.Error!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const DWORD = windows.DWORD; var index: usize = 0; @@ -2672,7 +3085,7 @@ fn fileReadStreamingWindows(userdata: ?*anyopaque, file: Io.File, data: [][]u8) const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len); while (true) { - try t.checkCancel(); + try current_thread.checkCancel(); var n: DWORD = undefined; if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, null) != 0) return n; @@ -2692,6 +3105,7 @@ fn fileReadStreamingWindows(userdata: ?*anyopaque, file: Io.File, data: [][]u8) fn fileReadPositionalPosix(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); if (!have_preadv) @compileError("TODO"); @@ -2707,60 +3121,82 @@ fn fileReadPositionalPosix(userdata: ?*anyopaque, file: Io.File, data: [][]u8, o const dest = iovecs_buffer[0..i]; assert(dest[0].len > 0); - if (native_os == .wasi and !builtin.link_libc) while (true) { - try t.checkCancel(); - var nread: usize = undefined; - switch (std.os.wasi.fd_pread(file.handle, dest.ptr, dest.len, offset, &nread)) { - .SUCCESS => return nread, - .INTR => continue, - .CANCELED => return error.Canceled, - - .INVAL => |err| return errnoBug(err), - .FAULT => |err| return errnoBug(err), - .AGAIN => |err| return errnoBug(err), - .BADF => return error.NotOpenForReading, // File operation on directory. - .IO => return error.InputOutput, - .ISDIR => return error.IsDir, - .NOBUFS => return error.SystemResources, - .NOMEM => return error.SystemResources, - .NOTCONN => return error.SocketUnconnected, - .CONNRESET => return error.ConnectionResetByPeer, - .TIMEDOUT => return error.Timeout, - .NXIO => return error.Unseekable, - .SPIPE => return error.Unseekable, - .OVERFLOW => return error.Unseekable, - .NOTCAPABLE => return error.AccessDenied, - else => |err| return posix.unexpectedErrno(err), + if (native_os == .wasi and !builtin.link_libc) { + try current_thread.beginSyscall(); + while (true) { + var nread: usize = undefined; + switch (std.os.wasi.fd_pread(file.handle, dest.ptr, dest.len, offset, &nread)) { + .SUCCESS => { + current_thread.endSyscall(); + return nread; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .INVAL => |err| return errnoBug(err), + .FAULT => |err| return errnoBug(err), + .AGAIN => |err| return errnoBug(err), + .BADF => return error.NotOpenForReading, // File operation on directory. + .IO => return error.InputOutput, + .ISDIR => return error.IsDir, + .NOBUFS => return error.SystemResources, + .NOMEM => return error.SystemResources, + .NOTCONN => return error.SocketUnconnected, + .CONNRESET => return error.ConnectionResetByPeer, + .TIMEDOUT => return error.Timeout, + .NXIO => return error.Unseekable, + .SPIPE => return error.Unseekable, + .OVERFLOW => return error.Unseekable, + .NOTCAPABLE => return error.AccessDenied, + else => |err| return posix.unexpectedErrno(err), + } + }, + } } - }; + } + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset)); switch (posix.errno(rc)) { - .SUCCESS => return @bitCast(rc), - .INTR => continue, - .CANCELED => return error.Canceled, - - .INVAL => |err| return errnoBug(err), - .FAULT => |err| return errnoBug(err), - .SRCH => return error.ProcessNotFound, - .AGAIN => return error.WouldBlock, - .BADF => |err| { - if (native_os == .wasi) return error.NotOpenForReading; // File operation on directory. - return errnoBug(err); // File descriptor used after closed. + .SUCCESS => { + current_thread.endSyscall(); + return @bitCast(rc); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .INVAL => |err| return errnoBug(err), + .FAULT => |err| return errnoBug(err), + .SRCH => return error.ProcessNotFound, + .AGAIN => return error.WouldBlock, + .BADF => |err| { + if (native_os == .wasi) return error.NotOpenForReading; // File operation on directory. + return errnoBug(err); // File descriptor used after closed. + }, + .IO => return error.InputOutput, + .ISDIR => return error.IsDir, + .NOBUFS => return error.SystemResources, + .NOMEM => return error.SystemResources, + .NOTCONN => return error.SocketUnconnected, + .CONNRESET => return error.ConnectionResetByPeer, + .TIMEDOUT => return error.Timeout, + .NXIO => return error.Unseekable, + .SPIPE => return error.Unseekable, + .OVERFLOW => return error.Unseekable, + else => |err| return posix.unexpectedErrno(err), + } }, - .IO => return error.InputOutput, - .ISDIR => return error.IsDir, - .NOBUFS => return error.SystemResources, - .NOMEM => return error.SystemResources, - .NOTCONN => return error.SocketUnconnected, - .CONNRESET => return error.ConnectionResetByPeer, - .TIMEDOUT => return error.Timeout, - .NXIO => return error.Unseekable, - .SPIPE => return error.Unseekable, - .OVERFLOW => return error.Unseekable, - else => |err| return posix.unexpectedErrno(err), } } } @@ -2772,6 +3208,7 @@ const fileReadPositional = switch (native_os) { fn fileReadPositionalWindows(userdata: ?*anyopaque, file: Io.File, data: [][]u8, offset: u64) Io.File.ReadPositionalError!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const DWORD = windows.DWORD; @@ -2793,7 +3230,7 @@ fn fileReadPositionalWindows(userdata: ?*anyopaque, file: Io.File, data: [][]u8, }; while (true) { - try t.checkCancel(); + try current_thread.checkCancel(); var n: DWORD = undefined; if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, &overlapped) != 0) return n; @@ -2813,8 +3250,7 @@ fn fileReadPositionalWindows(userdata: ?*anyopaque, file: Io.File, data: [][]u8, fn fileSeekBy(userdata: ?*anyopaque, file: Io.File, offset: i64) Io.File.SeekError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); - try t.checkCancel(); - + _ = t; _ = file; _ = offset; @panic("TODO implement fileSeekBy"); @@ -2822,63 +3258,96 @@ fn fileSeekBy(userdata: ?*anyopaque, file: Io.File, offset: i64) Io.File.SeekErr fn fileSeekTo(userdata: ?*anyopaque, file: Io.File, offset: u64) Io.File.SeekError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const fd = file.handle; - if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) while (true) { - try t.checkCancel(); - var result: u64 = undefined; - switch (posix.errno(posix.system.llseek(fd, offset, &result, posix.SEEK.SET))) { - .SUCCESS => return, - .INTR => continue, - .CANCELED => return error.Canceled, - - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .INVAL => return error.Unseekable, - .OVERFLOW => return error.Unseekable, - .SPIPE => return error.Unseekable, - .NXIO => return error.Unseekable, - else => |err| return posix.unexpectedErrno(err), + if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { + try current_thread.beginSyscall(); + while (true) { + var result: u64 = undefined; + switch (posix.errno(posix.system.llseek(fd, offset, &result, posix.SEEK.SET))) { + .SUCCESS => { + current_thread.endSyscall(); + return; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .INVAL => return error.Unseekable, + .OVERFLOW => return error.Unseekable, + .SPIPE => return error.Unseekable, + .NXIO => return error.Unseekable, + else => |err| return posix.unexpectedErrno(err), + } + }, + } } - }; + } if (native_os == .windows) { - try t.checkCancel(); + try current_thread.checkCancel(); return windows.SetFilePointerEx_BEGIN(fd, offset); } if (native_os == .wasi and !builtin.link_libc) while (true) { - try t.checkCancel(); var new_offset: std.os.wasi.filesize_t = undefined; + try current_thread.beginSyscall(); switch (std.os.wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) { - .SUCCESS => return, - .INTR => continue, - .CANCELED => return error.Canceled, - - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .INVAL => return error.Unseekable, - .OVERFLOW => return error.Unseekable, - .SPIPE => return error.Unseekable, - .NXIO => return error.Unseekable, - .NOTCAPABLE => return error.AccessDenied, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .INVAL => return error.Unseekable, + .OVERFLOW => return error.Unseekable, + .SPIPE => return error.Unseekable, + .NXIO => return error.Unseekable, + .NOTCAPABLE => return error.AccessDenied, + else => |err| return posix.unexpectedErrno(err), + } + }, } }; if (posix.SEEK == void) return error.Unseekable; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (posix.errno(lseek_sym(fd, @bitCast(offset), posix.SEEK.SET))) { - .SUCCESS => return, - .INTR => continue, - .CANCELED => return error.Canceled, - - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .INVAL => return error.Unseekable, - .OVERFLOW => return error.Unseekable, - .SPIPE => return error.Unseekable, - .NXIO => return error.Unseekable, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .INVAL => return error.Unseekable, + .OVERFLOW => return error.Unseekable, + .SPIPE => return error.Unseekable, + .NXIO => return error.Unseekable, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -2907,8 +3376,8 @@ fn fileWritePositional( offset: u64, ) Io.File.WritePositionalError!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); + _ = t; while (true) { - try t.checkCancel(); _ = file; _ = buffer; _ = offset; @@ -2918,8 +3387,8 @@ fn fileWritePositional( fn fileWriteStreaming(userdata: ?*anyopaque, file: Io.File, buffer: [][]const u8) Io.File.WriteStreamingError!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); + _ = t; while (true) { - try t.checkCancel(); _ = file; _ = buffer; @panic("TODO implement fileWriteStreaming"); @@ -2997,6 +3466,7 @@ const sleep = switch (native_os) { fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const clock_id: posix.clockid_t = clockToPosix(switch (timeout) { .none => .awake, .duration => |d| d.clock, @@ -3008,25 +3478,37 @@ fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { .deadline => |deadline| deadline.raw.nanoseconds, }; var timespec: posix.timespec = timestampToPosix(deadline_nanoseconds); + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (std.os.linux.errno(std.os.linux.clock_nanosleep(clock_id, .{ .ABSTIME = switch (timeout) { .none, .duration => false, .deadline => true, } }, ×pec, ×pec))) { - .SUCCESS => return, - .INTR => continue, - .CANCELED => return error.Canceled, - .INVAL => return error.UnsupportedClock, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .INVAL => return error.UnsupportedClock, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const t_io = ioBasic(t); - try t.checkCancel(); + try current_thread.checkCancel(); const ms = ms: { const d = (try timeout.toDurationFromNow(t_io)) orelse break :ms std.math.maxInt(windows.DWORD); @@ -3038,9 +3520,8 @@ fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { fn sleepWasi(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const t_io = ioBasic(t); - try t.checkCancel(); - const w = std.os.wasi; const clock: w.subscription_clock_t = if (try timeout.toDurationFromNow(t_io)) |d| .{ @@ -3063,11 +3544,14 @@ fn sleepWasi(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { }; var event: w.event_t = undefined; var nevents: usize = undefined; + try current_thread.beginSyscall(); _ = w.poll_oneoff(&in, &event, 1, &nevents); + current_thread.endSyscall(); } fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const t_io = ioBasic(t); const sec_type = @typeInfo(posix.timespec).@"struct".fields[0].type; const nsec_type = @typeInfo(posix.timespec).@"struct".fields[1].type; @@ -3079,12 +3563,16 @@ fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { }; break :t timestampToPosix(d.raw.toNanoseconds()); }; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (posix.errno(posix.system.nanosleep(×pec, ×pec))) { - .INTR => continue, - .CANCELED => return error.Canceled, - else => return, // This prong handles success as well as unexpected errors. + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + // This prong handles success as well as unexpected errors. + else => return current_thread.endSyscall(), } } } @@ -3127,34 +3615,48 @@ fn netListenIpPosix( ) IpAddress.ListenError!net.Server { if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const family = posixAddressFamily(&address); - const socket_fd = try openSocketPosix(t, family, .{ + const socket_fd = try openSocketPosix(current_thread, family, .{ .mode = options.mode, .protocol = options.protocol, }); errdefer posix.close(socket_fd); if (options.reuse_address) { - try setSocketOption(t, socket_fd, posix.SOL.SOCKET, posix.SO.REUSEADDR, 1); + try setSocketOption(current_thread, socket_fd, posix.SOL.SOCKET, posix.SO.REUSEADDR, 1); if (@hasDecl(posix.SO, "REUSEPORT")) - try setSocketOption(t, socket_fd, posix.SOL.SOCKET, posix.SO.REUSEPORT, 1); + try setSocketOption(current_thread, socket_fd, posix.SOL.SOCKET, posix.SO.REUSEPORT, 1); } var storage: PosixAddress = undefined; var addr_len = addressToPosix(&address, &storage); - try posixBind(t, socket_fd, &storage.any, addr_len); + try posixBind(current_thread, socket_fd, &storage.any, addr_len); + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (posix.errno(posix.system.listen(socket_fd, options.kernel_backlog))) { - .SUCCESS => break, - .ADDRINUSE => return error.AddressInUse, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + break; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ADDRINUSE => return error.AddressInUse, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + else => |err| return posix.unexpectedErrno(err), + } + }, } } - try posixGetSockName(t, socket_fd, &storage.any, &addr_len); + try posixGetSockName(current_thread, socket_fd, &storage.any, &addr_len); return .{ .socket = .{ .handle = socket_fd, @@ -3170,8 +3672,9 @@ fn netListenIpWindows( ) IpAddress.ListenError!net.Server { if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const family = posixAddressFamily(&address); - const socket_handle = try openSocketWsa(t, family, .{ + const socket_handle = try openSocketWsa(t, current_thread, family, .{ .mode = options.mode, .protocol = options.protocol, }); @@ -3183,52 +3686,76 @@ fn netListenIpWindows( var storage: WsaAddress = undefined; var addr_len = addressToWsa(&address, &storage); + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = ws2_32.bind(socket_handle, &storage.any, addr_len); - if (rc != ws2_32.SOCKET_ERROR) break; + if (rc != ws2_32.SOCKET_ERROR) { + current_thread.endSyscall(); + break; + } switch (ws2_32.WSAGetLastError()) { - .EINTR => continue, - .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, - .NOTINITIALISED => { - try initializeWsa(t); + .EINTR => { + try current_thread.checkCancel(); continue; }, - .EADDRINUSE => return error.AddressInUse, - .EADDRNOTAVAIL => return error.AddressUnavailable, - .ENOTSOCK => |err| return wsaErrorBug(err), - .EFAULT => |err| return wsaErrorBug(err), - .EINVAL => |err| return wsaErrorBug(err), - .ENOBUFS => return error.SystemResources, - .ENETDOWN => return error.NetworkDown, - else => |err| return windows.unexpectedWSAError(err), + .NOTINITIALISED => { + try initializeWsa(t); + try current_thread.checkCancel(); + continue; + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, + .EADDRINUSE => return error.AddressInUse, + .EADDRNOTAVAIL => return error.AddressUnavailable, + .ENOTSOCK => |err| return wsaErrorBug(err), + .EFAULT => |err| return wsaErrorBug(err), + .EINVAL => |err| return wsaErrorBug(err), + .ENOBUFS => return error.SystemResources, + .ENETDOWN => return error.NetworkDown, + else => |err| return windows.unexpectedWSAError(err), + } + }, } } + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = ws2_32.listen(socket_handle, options.kernel_backlog); - if (rc != ws2_32.SOCKET_ERROR) break; + if (rc != ws2_32.SOCKET_ERROR) { + current_thread.endSyscall(); + break; + } switch (ws2_32.WSAGetLastError()) { - .EINTR => continue, - .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, - .NOTINITIALISED => { - try initializeWsa(t); + .EINTR => { + try current_thread.checkCancel(); continue; }, - .ENETDOWN => return error.NetworkDown, - .EADDRINUSE => return error.AddressInUse, - .EISCONN => |err| return wsaErrorBug(err), - .EINVAL => |err| return wsaErrorBug(err), - .EMFILE, .ENOBUFS => return error.SystemResources, - .ENOTSOCK => |err| return wsaErrorBug(err), - .EOPNOTSUPP => |err| return wsaErrorBug(err), - .EINPROGRESS => |err| return wsaErrorBug(err), - else => |err| return windows.unexpectedWSAError(err), + .NOTINITIALISED => { + try initializeWsa(t); + try current_thread.checkCancel(); + continue; + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, + .ENETDOWN => return error.NetworkDown, + .EADDRINUSE => return error.AddressInUse, + .EISCONN => |err| return wsaErrorBug(err), + .EINVAL => |err| return wsaErrorBug(err), + .EMFILE, .ENOBUFS => return error.SystemResources, + .ENOTSOCK => |err| return wsaErrorBug(err), + .EOPNOTSUPP => |err| return wsaErrorBug(err), + .EINPROGRESS => |err| return wsaErrorBug(err), + else => |err| return windows.unexpectedWSAError(err), + } + }, } } - try wsaGetSockName(t, socket_handle, &storage.any, &addr_len); + try wsaGetSockName(t, current_thread, socket_handle, &storage.any, &addr_len); return .{ .socket = .{ @@ -3256,7 +3783,8 @@ fn netListenUnixPosix( ) net.UnixAddress.ListenError!net.Socket.Handle { if (!net.has_unix_sockets) return error.AddressFamilyUnsupported; const t: *Threaded = @ptrCast(@alignCast(userdata)); - const socket_fd = openSocketPosix(t, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) { + const current_thread = Thread.getCurrent(t); + const socket_fd = openSocketPosix(current_thread, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) { error.ProtocolUnsupportedBySystem => return error.AddressFamilyUnsupported, error.ProtocolUnsupportedByAddressFamily => return error.AddressFamilyUnsupported, error.SocketModeUnsupported => return error.AddressFamilyUnsupported, @@ -3267,15 +3795,28 @@ fn netListenUnixPosix( var storage: UnixAddress = undefined; const addr_len = addressUnixToPosix(address, &storage); - try posixBindUnix(t, socket_fd, &storage.any, addr_len); + try posixBindUnix(current_thread, socket_fd, &storage.any, addr_len); + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (posix.errno(posix.system.listen(socket_fd, options.kernel_backlog))) { - .SUCCESS => break, - .ADDRINUSE => return error.AddressInUse, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + break; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ADDRINUSE => return error.AddressInUse, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + else => |err| return posix.unexpectedErrno(err), + } + }, } } @@ -3289,8 +3830,9 @@ fn netListenUnixWindows( ) net.UnixAddress.ListenError!net.Socket.Handle { if (!net.has_unix_sockets) return error.AddressFamilyUnsupported; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); - const socket_handle = openSocketWsa(t, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) { + const socket_handle = openSocketWsa(t, current_thread, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) { error.ProtocolUnsupportedByAddressFamily => return error.AddressFamilyUnsupported, else => |e| return e, }; @@ -3299,52 +3841,67 @@ fn netListenUnixWindows( var storage: WsaAddress = undefined; const addr_len = addressUnixToWsa(address, &storage); + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = ws2_32.bind(socket_handle, &storage.any, addr_len); if (rc != ws2_32.SOCKET_ERROR) break; switch (ws2_32.WSAGetLastError()) { - .EINTR => continue, - .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, - .NOTINITIALISED => { - try initializeWsa(t); + .EINTR => { + try current_thread.checkCancel(); continue; }, - .EADDRINUSE => return error.AddressInUse, - .EADDRNOTAVAIL => return error.AddressUnavailable, - .ENOTSOCK => |err| return wsaErrorBug(err), - .EFAULT => |err| return wsaErrorBug(err), - .EINVAL => |err| return wsaErrorBug(err), - .ENOBUFS => return error.SystemResources, - .ENETDOWN => return error.NetworkDown, - else => |err| return windows.unexpectedWSAError(err), + .NOTINITIALISED => { + try initializeWsa(t); + try current_thread.checkCancel(); + continue; + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, + .EADDRINUSE => return error.AddressInUse, + .EADDRNOTAVAIL => return error.AddressUnavailable, + .ENOTSOCK => |err| return wsaErrorBug(err), + .EFAULT => |err| return wsaErrorBug(err), + .EINVAL => |err| return wsaErrorBug(err), + .ENOBUFS => return error.SystemResources, + .ENETDOWN => return error.NetworkDown, + else => |err| return windows.unexpectedWSAError(err), + } + }, } } while (true) { - try t.checkCancel(); + try current_thread.checkCancel(); const rc = ws2_32.listen(socket_handle, options.kernel_backlog); - if (rc != ws2_32.SOCKET_ERROR) break; + if (rc != ws2_32.SOCKET_ERROR) { + current_thread.endSyscall(); + return socket_handle; + } switch (ws2_32.WSAGetLastError()) { .EINTR => continue, - .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, .NOTINITIALISED => { try initializeWsa(t); continue; }, - .ENETDOWN => return error.NetworkDown, - .EADDRINUSE => return error.AddressInUse, - .EISCONN => |err| return wsaErrorBug(err), - .EINVAL => |err| return wsaErrorBug(err), - .EMFILE, .ENOBUFS => return error.SystemResources, - .ENOTSOCK => |err| return wsaErrorBug(err), - .EOPNOTSUPP => |err| return wsaErrorBug(err), - .EINPROGRESS => |err| return wsaErrorBug(err), - else => |err| return windows.unexpectedWSAError(err), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, + .ENETDOWN => return error.NetworkDown, + .EADDRINUSE => return error.AddressInUse, + .EISCONN => |err| return wsaErrorBug(err), + .EINVAL => |err| return wsaErrorBug(err), + .EMFILE, .ENOBUFS => return error.SystemResources, + .ENOTSOCK => |err| return wsaErrorBug(err), + .EOPNOTSUPP => |err| return wsaErrorBug(err), + .EINPROGRESS => |err| return wsaErrorBug(err), + else => |err| return windows.unexpectedWSAError(err), + } + }, } } - - return socket_handle; } fn netListenUnixUnavailable( @@ -3358,172 +3915,275 @@ fn netListenUnixUnavailable( return error.AddressFamilyUnsupported; } -fn posixBindUnix(t: *Threaded, fd: posix.socket_t, addr: *const posix.sockaddr, addr_len: posix.socklen_t) !void { +fn posixBindUnix( + current_thread: *Thread, + fd: posix.socket_t, + addr: *const posix.sockaddr, + addr_len: posix.socklen_t, +) !void { + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (posix.errno(posix.system.bind(fd, addr, addr_len))) { - .SUCCESS => break, - .INTR => continue, - .CANCELED => return error.Canceled, - - .ACCES => return error.AccessDenied, - .ADDRINUSE => return error.AddressInUse, - .AFNOSUPPORT => return error.AddressFamilyUnsupported, - .ADDRNOTAVAIL => return error.AddressUnavailable, - .NOMEM => return error.SystemResources, - - .LOOP => return error.SymLinkLoop, - .NOENT => return error.FileNotFound, - .NOTDIR => return error.NotDir, - .ROFS => return error.ReadOnlyFileSystem, - .PERM => return error.PermissionDenied, - - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .INVAL => |err| return errnoBug(err), // invalid parameters - .NOTSOCK => |err| return errnoBug(err), // invalid `sockfd` - .FAULT => |err| return errnoBug(err), // invalid `addr` pointer - .NAMETOOLONG => |err| return errnoBug(err), - else => |err| return posix.unexpectedErrno(err), - } - } -} - -fn posixBind(t: *Threaded, socket_fd: posix.socket_t, addr: *const posix.sockaddr, addr_len: posix.socklen_t) !void { - while (true) { - try t.checkCancel(); - switch (posix.errno(posix.system.bind(socket_fd, addr, addr_len))) { - .SUCCESS => break, - .INTR => continue, - .CANCELED => return error.Canceled, - - .ADDRINUSE => return error.AddressInUse, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .INVAL => |err| return errnoBug(err), // invalid parameters - .NOTSOCK => |err| return errnoBug(err), // invalid `sockfd` - .AFNOSUPPORT => return error.AddressFamilyUnsupported, - .ADDRNOTAVAIL => return error.AddressUnavailable, - .FAULT => |err| return errnoBug(err), // invalid `addr` pointer - .NOMEM => return error.SystemResources, - else => |err| return posix.unexpectedErrno(err), - } - } -} - -fn posixConnect(t: *Threaded, socket_fd: posix.socket_t, addr: *const posix.sockaddr, addr_len: posix.socklen_t) !void { - while (true) { - try t.checkCancel(); - switch (posix.errno(posix.system.connect(socket_fd, addr, addr_len))) { - .SUCCESS => return, - .INTR => continue, - .CANCELED => return error.Canceled, - - .ADDRNOTAVAIL => return error.AddressUnavailable, - .AFNOSUPPORT => return error.AddressFamilyUnsupported, - .AGAIN, .INPROGRESS => return error.WouldBlock, - .ALREADY => return error.ConnectionPending, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .CONNREFUSED => return error.ConnectionRefused, - .CONNRESET => return error.ConnectionResetByPeer, - .FAULT => |err| return errnoBug(err), - .ISCONN => |err| return errnoBug(err), - .HOSTUNREACH => return error.HostUnreachable, - .NETUNREACH => return error.NetworkUnreachable, - .NOTSOCK => |err| return errnoBug(err), - .PROTOTYPE => |err| return errnoBug(err), - .TIMEDOUT => return error.Timeout, - .CONNABORTED => |err| return errnoBug(err), - .ACCES => return error.AccessDenied, - .PERM => |err| return errnoBug(err), - .NOENT => |err| return errnoBug(err), - .NETDOWN => return error.NetworkDown, - else => |err| return posix.unexpectedErrno(err), - } - } -} - -fn posixConnectUnix(t: *Threaded, fd: posix.socket_t, addr: *const posix.sockaddr, addr_len: posix.socklen_t) !void { - while (true) { - try t.checkCancel(); - switch (posix.errno(posix.system.connect(fd, addr, addr_len))) { - .SUCCESS => return, - .INTR => continue, - .CANCELED => return error.Canceled, - - .AFNOSUPPORT => return error.AddressFamilyUnsupported, - .AGAIN => return error.WouldBlock, - .INPROGRESS => return error.WouldBlock, - .ACCES => return error.AccessDenied, - - .LOOP => return error.SymLinkLoop, - .NOENT => return error.FileNotFound, - .NOTDIR => return error.NotDir, - .ROFS => return error.ReadOnlyFileSystem, - .PERM => return error.PermissionDenied, - - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .CONNABORTED => |err| return errnoBug(err), - .FAULT => |err| return errnoBug(err), - .ISCONN => |err| return errnoBug(err), - .NOTSOCK => |err| return errnoBug(err), - .PROTOTYPE => |err| return errnoBug(err), - else => |err| return posix.unexpectedErrno(err), - } - } -} - -fn posixGetSockName(t: *Threaded, socket_fd: posix.fd_t, addr: *posix.sockaddr, addr_len: *posix.socklen_t) !void { - while (true) { - try t.checkCancel(); - switch (posix.errno(posix.system.getsockname(socket_fd, addr, addr_len))) { - .SUCCESS => break, - .INTR => continue, - .CANCELED => return error.Canceled, - - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .FAULT => |err| return errnoBug(err), - .INVAL => |err| return errnoBug(err), // invalid parameters - .NOTSOCK => |err| return errnoBug(err), // always a race condition - .NOBUFS => return error.SystemResources, - else => |err| return posix.unexpectedErrno(err), - } - } -} - -fn wsaGetSockName(t: *Threaded, handle: ws2_32.SOCKET, addr: *ws2_32.sockaddr, addr_len: *i32) !void { - while (true) { - try t.checkCancel(); - const rc = ws2_32.getsockname(handle, addr, addr_len); - if (rc != ws2_32.SOCKET_ERROR) break; - switch (ws2_32.WSAGetLastError()) { - .EINTR => continue, - .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, - .NOTINITIALISED => { - try initializeWsa(t); + .SUCCESS => { + current_thread.endSyscall(); + break; + }, + .INTR => { + try current_thread.checkCancel(); continue; }, - .ENETDOWN => return error.NetworkDown, - .EFAULT => |err| return wsaErrorBug(err), - .ENOTSOCK => |err| return wsaErrorBug(err), - .EINVAL => |err| return wsaErrorBug(err), - else => |err| return windows.unexpectedWSAError(err), + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ACCES => return error.AccessDenied, + .ADDRINUSE => return error.AddressInUse, + .AFNOSUPPORT => return error.AddressFamilyUnsupported, + .ADDRNOTAVAIL => return error.AddressUnavailable, + .NOMEM => return error.SystemResources, + + .LOOP => return error.SymLinkLoop, + .NOENT => return error.FileNotFound, + .NOTDIR => return error.NotDir, + .ROFS => return error.ReadOnlyFileSystem, + .PERM => return error.PermissionDenied, + + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .INVAL => |err| return errnoBug(err), // invalid parameters + .NOTSOCK => |err| return errnoBug(err), // invalid `sockfd` + .FAULT => |err| return errnoBug(err), // invalid `addr` pointer + .NAMETOOLONG => |err| return errnoBug(err), + else => |err| return posix.unexpectedErrno(err), + } + }, } } } -fn setSocketOption(t: *Threaded, fd: posix.fd_t, level: i32, opt_name: u32, option: u32) !void { - const o: []const u8 = @ptrCast(&option); +fn posixBind( + current_thread: *Thread, + socket_fd: posix.socket_t, + addr: *const posix.sockaddr, + addr_len: posix.socklen_t, +) !void { + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); - switch (posix.errno(posix.system.setsockopt(fd, level, opt_name, o.ptr, @intCast(o.len)))) { - .SUCCESS => return, - .INTR => continue, - .CANCELED => return error.Canceled, + switch (posix.errno(posix.system.bind(socket_fd, addr, addr_len))) { + .SUCCESS => { + current_thread.endSyscall(); + break; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ADDRINUSE => return error.AddressInUse, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .INVAL => |err| return errnoBug(err), // invalid parameters + .NOTSOCK => |err| return errnoBug(err), // invalid `sockfd` + .AFNOSUPPORT => return error.AddressFamilyUnsupported, + .ADDRNOTAVAIL => return error.AddressUnavailable, + .FAULT => |err| return errnoBug(err), // invalid `addr` pointer + .NOMEM => return error.SystemResources, + else => |err| return posix.unexpectedErrno(err), + } + }, + } + } +} - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .NOTSOCK => |err| return errnoBug(err), - .INVAL => |err| return errnoBug(err), - .FAULT => |err| return errnoBug(err), - else => |err| return posix.unexpectedErrno(err), +fn posixConnect( + current_thread: *Thread, + socket_fd: posix.socket_t, + addr: *const posix.sockaddr, + addr_len: posix.socklen_t, +) !void { + try current_thread.beginSyscall(); + while (true) { + switch (posix.errno(posix.system.connect(socket_fd, addr, addr_len))) { + .SUCCESS => { + current_thread.endSyscall(); + return; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ADDRNOTAVAIL => return error.AddressUnavailable, + .AFNOSUPPORT => return error.AddressFamilyUnsupported, + .AGAIN, .INPROGRESS => return error.WouldBlock, + .ALREADY => return error.ConnectionPending, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .CONNREFUSED => return error.ConnectionRefused, + .CONNRESET => return error.ConnectionResetByPeer, + .FAULT => |err| return errnoBug(err), + .ISCONN => |err| return errnoBug(err), + .HOSTUNREACH => return error.HostUnreachable, + .NETUNREACH => return error.NetworkUnreachable, + .NOTSOCK => |err| return errnoBug(err), + .PROTOTYPE => |err| return errnoBug(err), + .TIMEDOUT => return error.Timeout, + .CONNABORTED => |err| return errnoBug(err), + .ACCES => return error.AccessDenied, + .PERM => |err| return errnoBug(err), + .NOENT => |err| return errnoBug(err), + .NETDOWN => return error.NetworkDown, + else => |err| return posix.unexpectedErrno(err), + } + }, + } + } +} + +fn posixConnectUnix( + current_thread: *Thread, + fd: posix.socket_t, + addr: *const posix.sockaddr, + addr_len: posix.socklen_t, +) !void { + try current_thread.beginSyscall(); + while (true) { + switch (posix.errno(posix.system.connect(fd, addr, addr_len))) { + .SUCCESS => { + current_thread.endSyscall(); + return; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .AFNOSUPPORT => return error.AddressFamilyUnsupported, + .AGAIN => return error.WouldBlock, + .INPROGRESS => return error.WouldBlock, + .ACCES => return error.AccessDenied, + + .LOOP => return error.SymLinkLoop, + .NOENT => return error.FileNotFound, + .NOTDIR => return error.NotDir, + .ROFS => return error.ReadOnlyFileSystem, + .PERM => return error.PermissionDenied, + + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .CONNABORTED => |err| return errnoBug(err), + .FAULT => |err| return errnoBug(err), + .ISCONN => |err| return errnoBug(err), + .NOTSOCK => |err| return errnoBug(err), + .PROTOTYPE => |err| return errnoBug(err), + else => |err| return posix.unexpectedErrno(err), + } + }, + } + } +} + +fn posixGetSockName( + current_thread: *Thread, + socket_fd: posix.fd_t, + addr: *posix.sockaddr, + addr_len: *posix.socklen_t, +) !void { + try current_thread.beginSyscall(); + while (true) { + switch (posix.errno(posix.system.getsockname(socket_fd, addr, addr_len))) { + .SUCCESS => { + current_thread.endSyscall(); + break; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .FAULT => |err| return errnoBug(err), + .INVAL => |err| return errnoBug(err), // invalid parameters + .NOTSOCK => |err| return errnoBug(err), // always a race condition + .NOBUFS => return error.SystemResources, + else => |err| return posix.unexpectedErrno(err), + } + }, + } + } +} + +fn wsaGetSockName( + t: *Threaded, + current_thread: *Thread, + handle: ws2_32.SOCKET, + addr: *ws2_32.sockaddr, + addr_len: *i32, +) !void { + try current_thread.beginSyscall(); + while (true) { + const rc = ws2_32.getsockname(handle, addr, addr_len); + if (rc != ws2_32.SOCKET_ERROR) { + current_thread.endSyscall(); + return; + } + switch (ws2_32.WSAGetLastError()) { + .EINTR => { + try current_thread.checkCancel(); + continue; + }, + .NOTINITIALISED => { + try initializeWsa(t); + try current_thread.checkCancel(); + continue; + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, + .ENETDOWN => return error.NetworkDown, + .EFAULT => |err| return wsaErrorBug(err), + .ENOTSOCK => |err| return wsaErrorBug(err), + .EINVAL => |err| return wsaErrorBug(err), + else => |err| return windows.unexpectedWSAError(err), + } + }, + } + } +} + +fn setSocketOption(current_thread: *Thread, fd: posix.fd_t, level: i32, opt_name: u32, option: u32) !void { + const o: []const u8 = @ptrCast(&option); + try current_thread.beginSyscall(); + while (true) { + switch (posix.errno(posix.system.setsockopt(fd, level, opt_name, o.ptr, @intCast(o.len)))) { + .SUCCESS => { + current_thread.endSyscall(); + return; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .NOTSOCK => |err| return errnoBug(err), + .INVAL => |err| return errnoBug(err), + .FAULT => |err| return errnoBug(err), + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -3557,16 +4217,17 @@ fn netConnectIpPosix( if (!have_networking) return error.NetworkDown; if (options.timeout != .none) @panic("TODO implement netConnectIpPosix with timeout"); const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const family = posixAddressFamily(address); - const socket_fd = try openSocketPosix(t, family, .{ + const socket_fd = try openSocketPosix(current_thread, family, .{ .mode = options.mode, .protocol = options.protocol, }); errdefer posix.close(socket_fd); var storage: PosixAddress = undefined; var addr_len = addressToPosix(address, &storage); - try posixConnect(t, socket_fd, &storage.any, addr_len); - try posixGetSockName(t, socket_fd, &storage.any, &addr_len); + try posixConnect(current_thread, socket_fd, &storage.any, addr_len); + try posixGetSockName(current_thread, socket_fd, &storage.any, &addr_len); return .{ .socket = .{ .handle = socket_fd, .address = addressFromPosix(&storage), @@ -3581,8 +4242,9 @@ fn netConnectIpWindows( if (!have_networking) return error.NetworkDown; if (options.timeout != .none) @panic("TODO implement netConnectIpWindows with timeout"); const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const family = posixAddressFamily(address); - const socket_handle = try openSocketWsa(t, family, .{ + const socket_handle = try openSocketWsa(t, current_thread, family, .{ .mode = options.mode, .protocol = options.protocol, }); @@ -3591,36 +4253,48 @@ fn netConnectIpWindows( var storage: WsaAddress = undefined; var addr_len = addressToWsa(address, &storage); + try current_thread.beginSyscall(); while (true) { const rc = ws2_32.connect(socket_handle, &storage.any, addr_len); - if (rc != ws2_32.SOCKET_ERROR) break; + if (rc != ws2_32.SOCKET_ERROR) { + current_thread.endSyscall(); + break; + } switch (ws2_32.WSAGetLastError()) { - .EINTR => continue, - .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, - .NOTINITIALISED => { - try initializeWsa(t); + .EINTR => { + try current_thread.checkCancel(); continue; }, - - .EADDRNOTAVAIL => return error.AddressUnavailable, - .ECONNREFUSED => return error.ConnectionRefused, - .ECONNRESET => return error.ConnectionResetByPeer, - .ETIMEDOUT => return error.Timeout, - .EHOSTUNREACH => return error.HostUnreachable, - .ENETUNREACH => return error.NetworkUnreachable, - .EFAULT => |err| return wsaErrorBug(err), - .EINVAL => |err| return wsaErrorBug(err), - .EISCONN => |err| return wsaErrorBug(err), - .ENOTSOCK => |err| return wsaErrorBug(err), - .EWOULDBLOCK => return error.WouldBlock, - .EACCES => return error.AccessDenied, - .ENOBUFS => return error.SystemResources, - .EAFNOSUPPORT => return error.AddressFamilyUnsupported, - else => |err| return windows.unexpectedWSAError(err), + .NOTINITIALISED => { + try initializeWsa(t); + try current_thread.checkCancel(); + continue; + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, + .EADDRNOTAVAIL => return error.AddressUnavailable, + .ECONNREFUSED => return error.ConnectionRefused, + .ECONNRESET => return error.ConnectionResetByPeer, + .ETIMEDOUT => return error.Timeout, + .EHOSTUNREACH => return error.HostUnreachable, + .ENETUNREACH => return error.NetworkUnreachable, + .EFAULT => |err| return wsaErrorBug(err), + .EINVAL => |err| return wsaErrorBug(err), + .EISCONN => |err| return wsaErrorBug(err), + .ENOTSOCK => |err| return wsaErrorBug(err), + .EWOULDBLOCK => return error.WouldBlock, + .EACCES => return error.AccessDenied, + .ENOBUFS => return error.SystemResources, + .EAFNOSUPPORT => return error.AddressFamilyUnsupported, + else => |err| return windows.unexpectedWSAError(err), + } + }, } } - try wsaGetSockName(t, socket_handle, &storage.any, &addr_len); + try wsaGetSockName(t, current_thread, socket_handle, &storage.any, &addr_len); return .{ .socket = .{ .handle = socket_handle, @@ -3645,14 +4319,15 @@ fn netConnectUnixPosix( ) net.UnixAddress.ConnectError!net.Socket.Handle { if (!net.has_unix_sockets) return error.AddressFamilyUnsupported; const t: *Threaded = @ptrCast(@alignCast(userdata)); - const socket_fd = openSocketPosix(t, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) { + const current_thread = Thread.getCurrent(t); + const socket_fd = openSocketPosix(current_thread, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) { error.OptionUnsupported => return error.Unexpected, else => |e| return e, }; errdefer posix.close(socket_fd); var storage: UnixAddress = undefined; const addr_len = addressUnixToPosix(address, &storage); - try posixConnectUnix(t, socket_fd, &storage.any, addr_len); + try posixConnectUnix(current_thread, socket_fd, &storage.any, addr_len); return socket_fd; } @@ -3662,8 +4337,9 @@ fn netConnectUnixWindows( ) net.UnixAddress.ConnectError!net.Socket.Handle { if (!net.has_unix_sockets) return error.AddressFamilyUnsupported; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); - const socket_handle = try openSocketWsa(t, posix.AF.UNIX, .{ .mode = .stream }); + const socket_handle = try openSocketWsa(t, current_thread, posix.AF.UNIX, .{ .mode = .stream }); errdefer closeSocketWindows(socket_handle); var storage: WsaAddress = undefined; const addr_len = addressUnixToWsa(address, &storage); @@ -3711,13 +4387,14 @@ fn netBindIpPosix( ) IpAddress.BindError!net.Socket { if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const family = posixAddressFamily(address); - const socket_fd = try openSocketPosix(t, family, options); + const socket_fd = try openSocketPosix(current_thread, family, options); errdefer posix.close(socket_fd); var storage: PosixAddress = undefined; var addr_len = addressToPosix(address, &storage); - try posixBind(t, socket_fd, &storage.any, addr_len); - try posixGetSockName(t, socket_fd, &storage.any, &addr_len); + try posixBind(current_thread, socket_fd, &storage.any, addr_len); + try posixGetSockName(current_thread, socket_fd, &storage.any, &addr_len); return .{ .handle = socket_fd, .address = addressFromPosix(&storage), @@ -3731,8 +4408,9 @@ fn netBindIpWindows( ) IpAddress.BindError!net.Socket { if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const family = posixAddressFamily(address); - const socket_handle = try openSocketWsa(t, family, .{ + const socket_handle = try openSocketWsa(t, current_thread, family, .{ .mode = options.mode, .protocol = options.protocol, }); @@ -3741,29 +4419,41 @@ fn netBindIpWindows( var storage: WsaAddress = undefined; var addr_len = addressToWsa(address, &storage); + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = ws2_32.bind(socket_handle, &storage.any, addr_len); - if (rc != ws2_32.SOCKET_ERROR) break; + if (rc != ws2_32.SOCKET_ERROR) { + current_thread.endSyscall(); + break; + } switch (ws2_32.WSAGetLastError()) { - .EINTR => continue, - .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, - .NOTINITIALISED => { - try initializeWsa(t); + .EINTR => { + try current_thread.checkCancel(); continue; }, - .EADDRINUSE => return error.AddressInUse, - .EADDRNOTAVAIL => return error.AddressUnavailable, - .ENOTSOCK => |err| return wsaErrorBug(err), - .EFAULT => |err| return wsaErrorBug(err), - .EINVAL => |err| return wsaErrorBug(err), - .ENOBUFS => return error.SystemResources, - .ENETDOWN => return error.NetworkDown, - else => |err| return windows.unexpectedWSAError(err), + .NOTINITIALISED => { + try initializeWsa(t); + try current_thread.checkCancel(); + continue; + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, + .EADDRINUSE => return error.AddressInUse, + .EADDRNOTAVAIL => return error.AddressUnavailable, + .ENOTSOCK => |err| return wsaErrorBug(err), + .EFAULT => |err| return wsaErrorBug(err), + .EINVAL => |err| return wsaErrorBug(err), + .ENOBUFS => return error.SystemResources, + .ENETDOWN => return error.NetworkDown, + else => |err| return windows.unexpectedWSAError(err), + } + }, } } - try wsaGetSockName(t, socket_handle, &storage.any, &addr_len); + try wsaGetSockName(t, current_thread, socket_handle, &storage.any, &addr_len); return .{ .handle = socket_handle, @@ -3783,7 +4473,7 @@ fn netBindIpUnavailable( } fn openSocketPosix( - t: *Threaded, + current_thread: *Thread, family: posix.sa_family_t, options: IpAddress.BindOptions, ) error{ @@ -3800,8 +4490,8 @@ fn openSocketPosix( }!posix.socket_t { const mode = posixSocketMode(options.mode); const protocol = posixProtocol(options.protocol); + try current_thread.beginSyscall(); const socket_fd = while (true) { - try t.checkCancel(); const flags: u32 = mode | if (socket_flags_unsupported) 0 else posix.SOCK.CLOEXEC; const socket_rc = posix.system.socket(family, flags, protocol); switch (posix.errno(socket_rc)) { @@ -3809,60 +4499,88 @@ fn openSocketPosix( const fd: posix.fd_t = @intCast(socket_rc); errdefer posix.close(fd); if (socket_flags_unsupported) while (true) { - try t.checkCancel(); + try current_thread.checkCancel(); switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) { .SUCCESS => break, .INTR => continue, - .CANCELED => return error.Canceled, - else => |err| return posix.unexpectedErrno(err), + .CANCELED => return current_thread.endSyscallCanceled(), + else => |err| { + current_thread.endSyscall(); + return posix.unexpectedErrno(err); + }, } }; + current_thread.endSyscall(); break fd; }, - .INTR => continue, - .CANCELED => return error.Canceled, - - .AFNOSUPPORT => return error.AddressFamilyUnsupported, - .INVAL => return error.ProtocolUnsupportedBySystem, - .MFILE => return error.ProcessFdQuotaExceeded, - .NFILE => return error.SystemFdQuotaExceeded, - .NOBUFS => return error.SystemResources, - .NOMEM => return error.SystemResources, - .PROTONOSUPPORT => return error.ProtocolUnsupportedByAddressFamily, - .PROTOTYPE => return error.SocketModeUnsupported, - else => |err| return posix.unexpectedErrno(err), + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .AFNOSUPPORT => return error.AddressFamilyUnsupported, + .INVAL => return error.ProtocolUnsupportedBySystem, + .MFILE => return error.ProcessFdQuotaExceeded, + .NFILE => return error.SystemFdQuotaExceeded, + .NOBUFS => return error.SystemResources, + .NOMEM => return error.SystemResources, + .PROTONOSUPPORT => return error.ProtocolUnsupportedByAddressFamily, + .PROTOTYPE => return error.SocketModeUnsupported, + else => |err| return posix.unexpectedErrno(err), + } + }, } }; errdefer posix.close(socket_fd); if (options.ip6_only) { if (posix.IPV6 == void) return error.OptionUnsupported; - try setSocketOption(t, socket_fd, posix.IPPROTO.IPV6, posix.IPV6.V6ONLY, 0); + try setSocketOption(current_thread, socket_fd, posix.IPPROTO.IPV6, posix.IPV6.V6ONLY, 0); } return socket_fd; } -fn openSocketWsa(t: *Threaded, family: posix.sa_family_t, options: IpAddress.BindOptions) !ws2_32.SOCKET { +fn openSocketWsa( + t: *Threaded, + current_thread: *Thread, + family: posix.sa_family_t, + options: IpAddress.BindOptions, +) !ws2_32.SOCKET { const mode = posixSocketMode(options.mode); const protocol = posixProtocol(options.protocol); const flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED | ws2_32.WSA_FLAG_NO_HANDLE_INHERIT; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = ws2_32.WSASocketW(family, @bitCast(mode), @bitCast(protocol), null, 0, flags); - if (rc != ws2_32.INVALID_SOCKET) return rc; + if (rc != ws2_32.INVALID_SOCKET) { + current_thread.endSyscall(); + return rc; + } switch (ws2_32.WSAGetLastError()) { - .EINTR => continue, - .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, - .NOTINITIALISED => { - try initializeWsa(t); + .EINTR => { + try current_thread.checkCancel(); continue; }, - .EAFNOSUPPORT => return error.AddressFamilyUnsupported, - .EMFILE => return error.ProcessFdQuotaExceeded, - .ENOBUFS => return error.SystemResources, - .EPROTONOSUPPORT => return error.ProtocolUnsupportedByAddressFamily, - else => |err| return windows.unexpectedWSAError(err), + .NOTINITIALISED => { + try initializeWsa(t); + try current_thread.checkCancel(); + continue; + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, + .EAFNOSUPPORT => return error.AddressFamilyUnsupported, + .EMFILE => return error.ProcessFdQuotaExceeded, + .ENOBUFS => return error.SystemResources, + .EPROTONOSUPPORT => return error.ProtocolUnsupportedByAddressFamily, + else => |err| return windows.unexpectedWSAError(err), + } + }, } } } @@ -3870,10 +4588,11 @@ fn openSocketWsa(t: *Threaded, family: posix.sa_family_t, options: IpAddress.Bin fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Server.AcceptError!net.Stream { if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); var storage: PosixAddress = undefined; var addr_len: posix.socklen_t = @sizeOf(PosixAddress); + try current_thread.beginSyscall(); const fd = while (true) { - try t.checkCancel(); const rc = if (have_accept4) posix.system.accept4(listen_fd, &storage.any, &addr_len, posix.SOCK.CLOEXEC) else @@ -3883,33 +4602,43 @@ fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Serve const fd: posix.fd_t = @intCast(rc); errdefer posix.close(fd); if (!have_accept4) while (true) { - try t.checkCancel(); + try current_thread.checkCancel(); switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) { .SUCCESS => break, .INTR => continue, - .CANCELED => return error.Canceled, - else => |err| return posix.unexpectedErrno(err), + else => |err| { + current_thread.endSyscall(); + return posix.unexpectedErrno(err); + }, } }; + current_thread.endSyscall(); break fd; }, - .INTR => continue, - .CANCELED => return error.Canceled, - - .AGAIN => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .CONNABORTED => return error.ConnectionAborted, - .FAULT => |err| return errnoBug(err), - .INVAL => return error.SocketNotListening, - .NOTSOCK => |err| return errnoBug(err), - .MFILE => return error.ProcessFdQuotaExceeded, - .NFILE => return error.SystemFdQuotaExceeded, - .NOBUFS => return error.SystemResources, - .NOMEM => return error.SystemResources, - .OPNOTSUPP => |err| return errnoBug(err), - .PROTO => return error.ProtocolFailure, - .PERM => return error.BlockedByFirewall, - else => |err| return posix.unexpectedErrno(err), + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .AGAIN => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .CONNABORTED => return error.ConnectionAborted, + .FAULT => |err| return errnoBug(err), + .INVAL => return error.SocketNotListening, + .NOTSOCK => |err| return errnoBug(err), + .MFILE => return error.ProcessFdQuotaExceeded, + .NFILE => return error.SystemFdQuotaExceeded, + .NOBUFS => return error.SystemResources, + .NOMEM => return error.SystemResources, + .OPNOTSUPP => |err| return errnoBug(err), + .PROTO => return error.ProtocolFailure, + .PERM => return error.BlockedByFirewall, + else => |err| return posix.unexpectedErrno(err), + } + }, } }; return .{ .socket = .{ @@ -3921,31 +4650,44 @@ fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Serve fn netAcceptWindows(userdata: ?*anyopaque, listen_handle: net.Socket.Handle) net.Server.AcceptError!net.Stream { if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); var storage: WsaAddress = undefined; var addr_len: i32 = @sizeOf(WsaAddress); + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = ws2_32.accept(listen_handle, &storage.any, &addr_len); - if (rc != ws2_32.INVALID_SOCKET) return .{ .socket = .{ - .handle = rc, - .address = addressFromWsa(&storage), - } }; + if (rc != ws2_32.INVALID_SOCKET) { + current_thread.endSyscall(); + return .{ .socket = .{ + .handle = rc, + .address = addressFromWsa(&storage), + } }; + } switch (ws2_32.WSAGetLastError()) { - .EINTR => continue, - .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, - .NOTINITIALISED => { - try initializeWsa(t); + .EINTR => { + try current_thread.checkCancel(); continue; }, - .ECONNRESET => return error.ConnectionAborted, - .EFAULT => |err| return wsaErrorBug(err), - .ENOTSOCK => |err| return wsaErrorBug(err), - .EINVAL => |err| return wsaErrorBug(err), - .EMFILE => return error.ProcessFdQuotaExceeded, - .ENETDOWN => return error.NetworkDown, - .ENOBUFS => return error.SystemResources, - .EOPNOTSUPP => |err| return wsaErrorBug(err), - else => |err| return windows.unexpectedWSAError(err), + .NOTINITIALISED => { + try initializeWsa(t); + try current_thread.checkCancel(); + continue; + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, + .ECONNRESET => return error.ConnectionAborted, + .EFAULT => |err| return wsaErrorBug(err), + .ENOTSOCK => |err| return wsaErrorBug(err), + .EINVAL => |err| return wsaErrorBug(err), + .EMFILE => return error.ProcessFdQuotaExceeded, + .ENETDOWN => return error.NetworkDown, + .ENOBUFS => return error.SystemResources, + .EOPNOTSUPP => |err| return wsaErrorBug(err), + else => |err| return windows.unexpectedWSAError(err), + } + }, } } } @@ -3959,6 +4701,7 @@ fn netAcceptUnavailable(userdata: ?*anyopaque, listen_handle: net.Socket.Handle) fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize { if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined; var i: usize = 0; @@ -3972,48 +4715,70 @@ fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net. const dest = iovecs_buffer[0..i]; assert(dest[0].len > 0); - if (native_os == .wasi and !builtin.link_libc) while (true) { - try t.checkCancel(); - var n: usize = undefined; - switch (std.os.wasi.fd_read(fd, dest.ptr, dest.len, &n)) { - .SUCCESS => return n, - .INTR => continue, - .CANCELED => return error.Canceled, - - .INVAL => |err| return errnoBug(err), - .FAULT => |err| return errnoBug(err), - .AGAIN => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .NOBUFS => return error.SystemResources, - .NOMEM => return error.SystemResources, - .NOTCONN => return error.SocketUnconnected, - .CONNRESET => return error.ConnectionResetByPeer, - .TIMEDOUT => return error.Timeout, - .NOTCAPABLE => return error.AccessDenied, - else => |err| return posix.unexpectedErrno(err), + if (native_os == .wasi and !builtin.link_libc) { + try current_thread.beginSyscall(); + while (true) { + var n: usize = undefined; + switch (std.os.wasi.fd_read(fd, dest.ptr, dest.len, &n)) { + .SUCCESS => { + current_thread.endSyscall(); + return n; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .INVAL => |err| return errnoBug(err), + .FAULT => |err| return errnoBug(err), + .AGAIN => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .NOBUFS => return error.SystemResources, + .NOMEM => return error.SystemResources, + .NOTCONN => return error.SocketUnconnected, + .CONNRESET => return error.ConnectionResetByPeer, + .TIMEDOUT => return error.Timeout, + .NOTCAPABLE => return error.AccessDenied, + else => |err| return posix.unexpectedErrno(err), + } + }, + } } - }; + } + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = posix.system.readv(fd, dest.ptr, @intCast(dest.len)); switch (posix.errno(rc)) { - .SUCCESS => return @intCast(rc), - .INTR => continue, - .CANCELED => return error.Canceled, - - .INVAL => |err| return errnoBug(err), - .FAULT => |err| return errnoBug(err), - .AGAIN => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .NOBUFS => return error.SystemResources, - .NOMEM => return error.SystemResources, - .NOTCONN => return error.SocketUnconnected, - .CONNRESET => return error.ConnectionResetByPeer, - .TIMEDOUT => return error.Timeout, - .PIPE => return error.SocketUnconnected, - .NETDOWN => return error.NetworkDown, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return @intCast(rc); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .INVAL => |err| return errnoBug(err), + .FAULT => |err| return errnoBug(err), + .AGAIN => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .NOBUFS => return error.SystemResources, + .NOMEM => return error.SystemResources, + .NOTCONN => return error.SocketUnconnected, + .CONNRESET => return error.ConnectionResetByPeer, + .TIMEDOUT => return error.Timeout, + .PIPE => return error.SocketUnconnected, + .NETDOWN => return error.NetworkDown, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -4021,6 +4786,7 @@ fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net. fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize { if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const bufs = b: { var iovec_buffer: [max_iovecs_len]ws2_32.WSABUF = undefined; @@ -4048,7 +4814,7 @@ fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8 }; while (true) { - try t.checkCancel(); + try current_thread.checkCancel(); var flags: u32 = 0; var overlapped: windows.OVERLAPPED = std.mem.zeroes(windows.OVERLAPPED); @@ -4108,6 +4874,7 @@ fn netSendPosix( ) struct { ?net.Socket.SendError, usize } { if (!have_networking) return .{ error.NetworkDown, 0 }; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const posix_flags: u32 = @as(u32, if (@hasDecl(posix.MSG, "CONFIRM") and flags.confirm) posix.MSG.CONFIRM else 0) | @@ -4120,10 +4887,10 @@ fn netSendPosix( var i: usize = 0; while (messages.len - i != 0) { if (have_sendmmsg) { - i += netSendMany(t, handle, messages[i..], posix_flags) catch |err| return .{ err, i }; + i += netSendMany(current_thread, handle, messages[i..], posix_flags) catch |err| return .{ err, i }; continue; } - netSendOne(t, handle, &messages[i], posix_flags) catch |err| return .{ err, i }; + netSendOne(t, current_thread, handle, &messages[i], posix_flags) catch |err| return .{ err, i }; i += 1; } return .{ null, i }; @@ -4159,6 +4926,7 @@ fn netSendUnavailable( fn netSendOne( t: *Threaded, + current_thread: *Thread, handle: net.Socket.Handle, message: *net.OutgoingMessage, flags: u32, @@ -4175,80 +4943,97 @@ fn netSendOne( .controllen = @intCast(message.control.len), .flags = 0, }; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = posix.system.sendmsg(handle, &msg, flags); if (is_windows) { - if (rc == ws2_32.SOCKET_ERROR) { - switch (ws2_32.WSAGetLastError()) { - .EINTR => continue, - .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, - .NOTINITIALISED => { - try initializeWsa(t); - continue; - }, - .EACCES => return error.AccessDenied, - .EADDRNOTAVAIL => return error.AddressUnavailable, - .ECONNRESET => return error.ConnectionResetByPeer, - .EMSGSIZE => return error.MessageOversize, - .ENOBUFS => return error.SystemResources, - .ENOTSOCK => return error.FileDescriptorNotASocket, - .EAFNOSUPPORT => return error.AddressFamilyUnsupported, - .EDESTADDRREQ => unreachable, // A destination address is required. - .EFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small. - .EHOSTUNREACH => return error.NetworkUnreachable, - .EINVAL => unreachable, - .ENETDOWN => return error.NetworkDown, - .ENETRESET => return error.ConnectionResetByPeer, - .ENETUNREACH => return error.NetworkUnreachable, - .ENOTCONN => return error.SocketUnconnected, - .ESHUTDOWN => |err| return wsaErrorBug(err), - else => |err| return windows.unexpectedWSAError(err), - } - } else { + if (rc != ws2_32.SOCKET_ERROR) { + current_thread.endSyscall(); message.data_len = @intCast(rc); return; } + switch (ws2_32.WSAGetLastError()) { + .EINTR => { + try current_thread.checkCancel(); + continue; + }, + .NOTINITIALISED => { + try initializeWsa(t); + try current_thread.checkCancel(); + continue; + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, + .EACCES => return error.AccessDenied, + .EADDRNOTAVAIL => return error.AddressUnavailable, + .ECONNRESET => return error.ConnectionResetByPeer, + .EMSGSIZE => return error.MessageOversize, + .ENOBUFS => return error.SystemResources, + .ENOTSOCK => return error.FileDescriptorNotASocket, + .EAFNOSUPPORT => return error.AddressFamilyUnsupported, + .EDESTADDRREQ => unreachable, // A destination address is required. + .EFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small. + .EHOSTUNREACH => return error.NetworkUnreachable, + .EINVAL => unreachable, + .ENETDOWN => return error.NetworkDown, + .ENETRESET => return error.ConnectionResetByPeer, + .ENETUNREACH => return error.NetworkUnreachable, + .ENOTCONN => return error.SocketUnconnected, + .ESHUTDOWN => |err| return wsaErrorBug(err), + else => |err| return windows.unexpectedWSAError(err), + } + }, + } } switch (posix.errno(rc)) { .SUCCESS => { + current_thread.endSyscall(); message.data_len = @intCast(rc); return; }, - .INTR => continue, - .CANCELED => return error.Canceled, - - .ACCES => return error.AccessDenied, - .ALREADY => return error.FastOpenAlreadyInProgress, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .CONNRESET => return error.ConnectionResetByPeer, - .DESTADDRREQ => |err| return errnoBug(err), - .FAULT => |err| return errnoBug(err), - .INVAL => |err| return errnoBug(err), - .ISCONN => |err| return errnoBug(err), - .MSGSIZE => return error.MessageOversize, - .NOBUFS => return error.SystemResources, - .NOMEM => return error.SystemResources, - .NOTSOCK => |err| return errnoBug(err), - .OPNOTSUPP => |err| return errnoBug(err), - .PIPE => return error.SocketUnconnected, - .AFNOSUPPORT => return error.AddressFamilyUnsupported, - .HOSTUNREACH => return error.HostUnreachable, - .NETUNREACH => return error.NetworkUnreachable, - .NOTCONN => return error.SocketUnconnected, - .NETDOWN => return error.NetworkDown, - else => |err| return posix.unexpectedErrno(err), + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ACCES => return error.AccessDenied, + .ALREADY => return error.FastOpenAlreadyInProgress, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .CONNRESET => return error.ConnectionResetByPeer, + .DESTADDRREQ => |err| return errnoBug(err), + .FAULT => |err| return errnoBug(err), + .INVAL => |err| return errnoBug(err), + .ISCONN => |err| return errnoBug(err), + .MSGSIZE => return error.MessageOversize, + .NOBUFS => return error.SystemResources, + .NOMEM => return error.SystemResources, + .NOTSOCK => |err| return errnoBug(err), + .OPNOTSUPP => |err| return errnoBug(err), + .PIPE => return error.SocketUnconnected, + .AFNOSUPPORT => return error.AddressFamilyUnsupported, + .HOSTUNREACH => return error.HostUnreachable, + .NETUNREACH => return error.NetworkUnreachable, + .NOTCONN => return error.SocketUnconnected, + .NETDOWN => return error.NetworkDown, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } fn netSendMany( - t: *Threaded, + current_thread: *Thread, handle: net.Socket.Handle, messages: []net.OutgoingMessage, flags: u32, ) net.Socket.SendError!usize { - var msg_buffer: [64]std.os.linux.mmsghdr = undefined; + var msg_buffer: [64]posix.system.mmsghdr = undefined; var addr_buffer: [msg_buffer.len]PosixAddress = undefined; var iovecs_buffer: [msg_buffer.len]posix.iovec = undefined; const min_len: usize = @min(messages.len, msg_buffer.len); @@ -4273,40 +5058,48 @@ fn netSendMany( }; } + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = posix.system.sendmmsg(handle, clamped_msgs.ptr, @intCast(clamped_msgs.len), flags); switch (posix.errno(rc)) { .SUCCESS => { + current_thread.endSyscall(); const n: usize = @intCast(rc); for (clamped_messages[0..n], clamped_msgs[0..n]) |*message, *msg| { message.data_len = msg.len; } return n; }, - .INTR => continue, - .CANCELED => return error.Canceled, - - .AGAIN => |err| return errnoBug(err), - .ALREADY => return error.FastOpenAlreadyInProgress, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .CONNRESET => return error.ConnectionResetByPeer, - .DESTADDRREQ => |err| return errnoBug(err), // The socket is not connection-mode, and no peer address is set. - .FAULT => |err| return errnoBug(err), // An invalid user space address was specified for an argument. - .INVAL => |err| return errnoBug(err), // Invalid argument passed. - .ISCONN => |err| return errnoBug(err), // connection-mode socket was connected already but a recipient was specified - .MSGSIZE => return error.MessageOversize, - .NOBUFS => return error.SystemResources, - .NOMEM => return error.SystemResources, - .NOTSOCK => |err| return errnoBug(err), // The file descriptor sockfd does not refer to a socket. - .OPNOTSUPP => |err| return errnoBug(err), // Some bit in the flags argument is inappropriate for the socket type. - .PIPE => return error.SocketUnconnected, - .AFNOSUPPORT => return error.AddressFamilyUnsupported, - .HOSTUNREACH => return error.HostUnreachable, - .NETUNREACH => return error.NetworkUnreachable, - .NOTCONN => return error.SocketUnconnected, - .NETDOWN => return error.NetworkDown, - else => |err| return posix.unexpectedErrno(err), + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .AGAIN => |err| return errnoBug(err), + .ALREADY => return error.FastOpenAlreadyInProgress, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .CONNRESET => return error.ConnectionResetByPeer, + .DESTADDRREQ => |err| return errnoBug(err), // The socket is not connection-mode, and no peer address is set. + .FAULT => |err| return errnoBug(err), // An invalid user space address was specified for an argument. + .INVAL => |err| return errnoBug(err), // Invalid argument passed. + .ISCONN => |err| return errnoBug(err), // connection-mode socket was connected already but a recipient was specified + .MSGSIZE => return error.MessageOversize, + .NOBUFS => return error.SystemResources, + .NOMEM => return error.SystemResources, + .NOTSOCK => |err| return errnoBug(err), // The file descriptor sockfd does not refer to a socket. + .OPNOTSUPP => |err| return errnoBug(err), // Some bit in the flags argument is inappropriate for the socket type. + .PIPE => return error.SocketUnconnected, + .AFNOSUPPORT => return error.AddressFamilyUnsupported, + .HOSTUNREACH => return error.HostUnreachable, + .NETUNREACH => return error.NetworkUnreachable, + .NOTCONN => return error.SocketUnconnected, + .NETDOWN => return error.NetworkDown, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -4321,6 +5114,7 @@ fn netReceivePosix( ) struct { ?net.Socket.ReceiveTimeoutError, usize } { if (!have_networking) return .{ error.NetworkDown, 0 }; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const t_io = io(t); // recvmmsg is useless, here's why: @@ -4351,8 +5145,6 @@ fn netReceivePosix( const deadline = timeout.toDeadline(t_io) catch |err| return .{ err, message_i }; recv: while (true) { - t.checkCancel() catch |err| return .{ err, message_i }; - if (message_buffer.len - message_i == 0) return .{ null, message_i }; const message = &message_buffer[message_i]; const remaining_data_buffer = data_buffer[data_i..]; @@ -4368,7 +5160,9 @@ fn netReceivePosix( .flags = undefined, }; + current_thread.beginSyscall() catch |err| return .{ err, message_i }; const recv_rc = posix.system.recvmsg(handle, &msg, posix_flags); + current_thread.endSyscall(); switch (posix.errno(recv_rc)) { .SUCCESS => { const data = remaining_data_buffer[0..@intCast(recv_rc)]; @@ -4389,7 +5183,6 @@ fn netReceivePosix( continue; }, .AGAIN => while (true) { - t.checkCancel() catch |err| return .{ err, message_i }; if (message_i != 0) return .{ null, message_i }; const max_poll_ms = std.math.maxInt(u31); @@ -4399,7 +5192,10 @@ fn netReceivePosix( break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds())); } else max_poll_ms; + current_thread.beginSyscall() catch |err| return .{ err, message_i }; const poll_rc = posix.system.poll(&poll_fds, poll_fds.len, timeout_ms); + current_thread.endSyscall(); + switch (posix.errno(poll_rc)) { .SUCCESS => { if (poll_rc == 0) { @@ -4411,7 +5207,7 @@ fn netReceivePosix( continue :recv; }, .INTR => continue, - .CANCELED => return .{ error.Canceled, message_i }, + .CANCELED => return .{ current_thread.endSyscallCanceled(), message_i }, .FAULT => |err| return .{ errnoBug(err), message_i }, .INVAL => |err| return .{ errnoBug(err), message_i }, @@ -4420,7 +5216,7 @@ fn netReceivePosix( } }, .INTR => continue, - .CANCELED => return .{ error.Canceled, message_i }, + .CANCELED => return .{ current_thread.endSyscallCanceled(), message_i }, .BADF => |err| return .{ errnoBug(err), message_i }, .NFILE => return .{ error.SystemFdQuotaExceeded, message_i }, @@ -4486,6 +5282,7 @@ fn netWritePosix( ) net.Stream.Writer.Error!usize { if (!have_networking) return error.NetworkDown; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); var iovecs: [max_iovecs_len]posix.iovec_const = undefined; var msg: posix.msghdr_const = .{ @@ -4526,35 +5323,45 @@ fn netWritePosix( }, }; const flags = posix.MSG.NOSIGNAL; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); const rc = posix.system.sendmsg(fd, &msg, flags); switch (posix.errno(rc)) { - .SUCCESS => return @intCast(rc), - .INTR => continue, - .CANCELED => return error.Canceled, - - .ACCES => |err| return errnoBug(err), - .AGAIN => |err| return errnoBug(err), - .ALREADY => return error.FastOpenAlreadyInProgress, - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .CONNRESET => return error.ConnectionResetByPeer, - .DESTADDRREQ => |err| return errnoBug(err), // The socket is not connection-mode, and no peer address is set. - .FAULT => |err| return errnoBug(err), // An invalid user space address was specified for an argument. - .INVAL => |err| return errnoBug(err), // Invalid argument passed. - .ISCONN => |err| return errnoBug(err), // connection-mode socket was connected already but a recipient was specified - .MSGSIZE => |err| return errnoBug(err), - .NOBUFS => return error.SystemResources, - .NOMEM => return error.SystemResources, - .NOTSOCK => |err| return errnoBug(err), // The file descriptor sockfd does not refer to a socket. - .OPNOTSUPP => |err| return errnoBug(err), // Some bit in the flags argument is inappropriate for the socket type. - .PIPE => return error.SocketUnconnected, - .AFNOSUPPORT => return error.AddressFamilyUnsupported, - .HOSTUNREACH => return error.HostUnreachable, - .NETUNREACH => return error.NetworkUnreachable, - .NOTCONN => return error.SocketUnconnected, - .NETDOWN => return error.NetworkDown, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return @intCast(rc); + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ACCES => |err| return errnoBug(err), + .AGAIN => |err| return errnoBug(err), + .ALREADY => return error.FastOpenAlreadyInProgress, + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .CONNRESET => return error.ConnectionResetByPeer, + .DESTADDRREQ => |err| return errnoBug(err), // The socket is not connection-mode, and no peer address is set. + .FAULT => |err| return errnoBug(err), // An invalid user space address was specified for an argument. + .INVAL => |err| return errnoBug(err), // Invalid argument passed. + .ISCONN => |err| return errnoBug(err), // connection-mode socket was connected already but a recipient was specified + .MSGSIZE => |err| return errnoBug(err), + .NOBUFS => return error.SystemResources, + .NOMEM => return error.SystemResources, + .NOTSOCK => |err| return errnoBug(err), // The file descriptor sockfd does not refer to a socket. + .OPNOTSUPP => |err| return errnoBug(err), // Some bit in the flags argument is inappropriate for the socket type. + .PIPE => return error.SocketUnconnected, + .AFNOSUPPORT => return error.AddressFamilyUnsupported, + .HOSTUNREACH => return error.HostUnreachable, + .NETUNREACH => return error.NetworkUnreachable, + .NOTCONN => return error.SocketUnconnected, + .NETDOWN => return error.NetworkDown, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } @@ -4567,6 +5374,7 @@ fn netWriteWindows( splat: usize, ) net.Stream.Writer.Error!usize { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); comptime assert(native_os == .windows); var iovecs: [max_iovecs_len]ws2_32.WSABUF = undefined; @@ -4600,7 +5408,7 @@ fn netWriteWindows( }; while (true) { - try t.checkCancel(); + try current_thread.checkCancel(); var n: u32 = undefined; var overlapped: windows.OVERLAPPED = std.mem.zeroes(windows.OVERLAPPED); @@ -4626,7 +5434,7 @@ fn netWriteWindows( }; switch (wsa_error) { .EINTR => continue, - .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled, + .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return current_thread.endSyscallCanceled(), .NOTINITIALISED => { try initializeWsa(t); continue; @@ -4707,9 +5515,10 @@ fn netInterfaceNameResolve( ) net.Interface.Name.ResolveError!net.Interface { if (!have_networking) return error.InterfaceNotFound; const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); if (native_os == .linux) { - const sock_fd = openSocketPosix(t, posix.AF.UNIX, .{ .mode = .dgram }) catch |err| switch (err) { + const sock_fd = openSocketPosix(current_thread, posix.AF.UNIX, .{ .mode = .dgram }) catch |err| switch (err) { error.ProcessFdQuotaExceeded => return error.SystemResources, error.SystemFdQuotaExceeded => return error.SystemResources, error.AddressFamilyUnsupported => return error.Unexpected, @@ -4726,32 +5535,42 @@ fn netInterfaceNameResolve( .ifru = undefined, }; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (posix.errno(posix.system.ioctl(sock_fd, posix.SIOCGIFINDEX, @intFromPtr(&ifr)))) { - .SUCCESS => return .{ .index = @bitCast(ifr.ifru.ivalue) }, - .INTR => continue, - .CANCELED => return error.Canceled, - - .INVAL => |err| return errnoBug(err), // Bad parameters. - .NOTTY => |err| return errnoBug(err), - .NXIO => |err| return errnoBug(err), - .BADF => |err| return errnoBug(err), // File descriptor used after closed. - .FAULT => |err| return errnoBug(err), // Bad pointer parameter. - .IO => |err| return errnoBug(err), // sock_fd is not a file descriptor - .NODEV => return error.InterfaceNotFound, - else => |err| return posix.unexpectedErrno(err), + .SUCCESS => { + current_thread.endSyscall(); + return .{ .index = @bitCast(ifr.ifru.ivalue) }; + }, + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + switch (e) { + .INVAL => |err| return errnoBug(err), // Bad parameters. + .NOTTY => |err| return errnoBug(err), + .NXIO => |err| return errnoBug(err), + .BADF => |err| return errnoBug(err), // File descriptor used after closed. + .FAULT => |err| return errnoBug(err), // Bad pointer parameter. + .IO => |err| return errnoBug(err), // sock_fd is not a file descriptor + .NODEV => return error.InterfaceNotFound, + else => |err| return posix.unexpectedErrno(err), + } + }, } } } if (native_os == .windows) { - try t.checkCancel(); + try current_thread.checkCancel(); @panic("TODO implement netInterfaceNameResolve for Windows"); } if (builtin.link_libc) { - try t.checkCancel(); + try current_thread.checkCancel(); const index = std.c.if_nametoindex(&name.bytes); if (index == 0) return error.InterfaceNotFound; return .{ .index = @bitCast(index) }; @@ -4771,7 +5590,8 @@ fn netInterfaceNameResolveUnavailable( fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name { const t: *Threaded = @ptrCast(@alignCast(userdata)); - try t.checkCancel(); + const current_thread = Thread.getCurrent(t); + try current_thread.checkCancel(); if (native_os == .linux) { _ = interface; @@ -4802,8 +5622,9 @@ fn netLookup( options: HostName.LookupOptions, ) void { const t: *Threaded = @ptrCast(@alignCast(userdata)); + const current_thread = Thread.getCurrent(t); const t_io = io(t); - resolved.putOneUncancelable(t_io, .{ .end = netLookupFallible(t, host_name, resolved, options) }); + resolved.putOneUncancelable(t_io, .{ .end = netLookupFallible(t, current_thread, host_name, resolved, options) }); } fn netLookupUnavailable( @@ -4821,6 +5642,7 @@ fn netLookupUnavailable( fn netLookupFallible( t: *Threaded, + current_thread: *Thread, host_name: HostName, resolved: *Io.Queue(HostName.LookupResult), options: HostName.LookupOptions, @@ -4866,7 +5688,7 @@ fn netLookupFallible( var res: *ws2_32.ADDRINFOEXW = undefined; const timeout: ?*ws2_32.timeval = null; while (true) { - try t.checkCancel(); // TODO make requestCancel call GetAddrInfoExCancel + try current_thread.checkCancel(); // TODO make requestCancel call GetAddrInfoExCancel // TODO make this append to the queue eagerly rather than blocking until // the whole thing finishes const rc: ws2_32.WinsockError = @enumFromInt(ws2_32.GetAddrInfoExW(name_w, port_w, .DNS, null, &hints, &res, timeout, null, null, cancel_handle)); @@ -5013,23 +5835,37 @@ fn netLookupFallible( .next = null, }; var res: ?*posix.addrinfo = null; + try current_thread.beginSyscall(); while (true) { - try t.checkCancel(); switch (posix.system.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) { - @as(posix.system.EAI, @enumFromInt(0)) => break, - .ADDRFAMILY => return error.AddressFamilyUnsupported, - .AGAIN => return error.NameServerFailure, - .FAIL => return error.NameServerFailure, - .FAMILY => return error.AddressFamilyUnsupported, - .MEMORY => return error.SystemResources, - .NODATA => return error.UnknownHostName, - .NONAME => return error.UnknownHostName, - .SYSTEM => switch (posix.errno(-1)) { - .INTR => continue, - .CANCELED => return error.Canceled, - else => |e| return posix.unexpectedErrno(e), + @as(posix.system.EAI, @enumFromInt(0)) => { + current_thread.endSyscall(); + break; + }, + .SYSTEM => switch (posix.errno(-1)) { + .INTR => { + try current_thread.checkCancel(); + continue; + }, + .CANCELED => return current_thread.endSyscallCanceled(), + else => |e| { + current_thread.endSyscall(); + return posix.unexpectedErrno(e); + }, + }, + else => |e| { + current_thread.endSyscall(); + switch (e) { + .ADDRFAMILY => return error.AddressFamilyUnsupported, + .AGAIN => return error.NameServerFailure, + .FAIL => return error.NameServerFailure, + .FAMILY => return error.AddressFamilyUnsupported, + .MEMORY => return error.SystemResources, + .NODATA => return error.UnknownHostName, + .NONAME => return error.UnknownHostName, + else => return error.Unexpected, + } }, - else => return error.Unexpected, } } defer if (res) |some| posix.system.freeaddrinfo(some); @@ -5726,12 +6562,12 @@ fn copyCanon(canonical_name_buffer: *[HostName.max_len]u8, name: []const u8) Hos /// ulock_wait2() uses 64-bit nano-second timeouts (with the same convention) const darwin_supports_ulock_wait2 = builtin.os.version_range.semver.min.major >= 11; -fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Cancelable!void { +fn futexWait(current_thread: *Thread, ptr: *const std.atomic.Value(u32), expect: u32) Io.Cancelable!void { @branchHint(.cold); if (builtin.cpu.arch.isWasm()) { comptime assert(builtin.cpu.has(.wasm, .atomics)); - try t.checkCancel(); + try current_thread.checkCancel(); const timeout: i64 = -1; const signed_expect: i32 = @bitCast(expect); const result = asm volatile ( @@ -5754,17 +6590,18 @@ fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Ca } else switch (native_os) { .linux => { const linux = std.os.linux; - try t.checkCancel(); + try current_thread.beginSyscall(); const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, null); - if (is_debug) switch (linux.errno(rc)) { + current_thread.endSyscall(); + switch (linux.errno(rc)) { .SUCCESS => {}, // notified by `wake()` - .INTR => {}, // gives caller a chance to check cancellation + .INTR => {}, // caller's responsibility to retry .AGAIN => {}, // ptr.* != expect .INVAL => {}, // possibly timeout overflow - .TIMEDOUT => unreachable, - .FAULT => unreachable, // ptr was invalid - else => unreachable, - }; + .TIMEDOUT => recoverableOsBugDetected(), + .FAULT => recoverableOsBugDetected(), // ptr was invalid + else => recoverableOsBugDetected(), + } }, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => { const c = std.c; @@ -5772,11 +6609,12 @@ fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Ca .op = .COMPARE_AND_WAIT, .NO_ERRNO = true, }; - try t.checkCancel(); + try current_thread.beginSyscall(); const status = if (darwin_supports_ulock_wait2) c.__ulock_wait2(flags, ptr, expect, 0, 0) else c.__ulock_wait(flags, ptr, expect, 0); + current_thread.endSyscall(); if (status >= 0) return; @@ -5791,7 +6629,7 @@ fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Ca }; }, .windows => { - try t.checkCancel(); + try current_thread.checkCancel(); switch (windows.ntdll.RtlWaitOnAddress(ptr, &expect, @sizeOf(@TypeOf(expect)), null)) { .SUCCESS => {}, .CANCELLED => return error.Canceled, @@ -5800,8 +6638,9 @@ fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Ca }, .freebsd => { const flags = @intFromEnum(std.c.UMTX_OP.WAIT_UINT_PRIVATE); - try t.checkCancel(); + try current_thread.beginSyscall(); const rc = std.c._umtx_op(@intFromPtr(&ptr.raw), flags, @as(c_ulong, expect), 0, 0); + current_thread.endSyscall(); if (is_debug) switch (posix.errno(rc)) { .SUCCESS => {}, .FAULT => unreachable, // one of the args points to invalid memory @@ -5845,7 +6684,7 @@ pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) voi const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, null); switch (linux.errno(rc)) { .SUCCESS => {}, // notified by `wake()` - .INTR => {}, // gives caller a chance to check cancellation + .INTR => {}, // caller's responsibility to repeat .AGAIN => {}, // ptr.* != expect .INVAL => {}, // possibly timeout overflow .TIMEDOUT => recoverableOsBugDetected(), @@ -5899,28 +6738,6 @@ pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) voi } } -pub fn futexWaitDurationUncancelable(ptr: *const std.atomic.Value(u32), expect: u32, timeout: Io.Duration) void { - @branchHint(.cold); - - if (native_os == .linux) { - const linux = std.os.linux; - var ts = timestampToPosix(timeout.toNanoseconds()); - const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, &ts); - if (is_debug) switch (linux.errno(rc)) { - .SUCCESS => {}, // notified by `wake()` - .INTR => {}, // gives caller a chance to check cancellation - .AGAIN => {}, // ptr.* != expect - .TIMEDOUT => {}, - .INVAL => {}, // possibly timeout overflow - .FAULT => unreachable, // ptr was invalid - else => unreachable, - }; - return; - } else { - @compileError("TODO"); - } -} - pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void { @branchHint(.cold); @@ -6050,8 +6867,9 @@ const ResetEventFutex = enum(u32) { if (state == .unset) { state = @cmpxchgStrong(ResetEventFutex, ref, state, .waiting, .acquire, .acquire) orelse .waiting; } + const current_thread = Thread.getCurrent(t); while (state == .waiting) { - try futexWait(t, @ptrCast(ref), @intFromEnum(ResetEventFutex.waiting)); + try futexWait(current_thread, @ptrCast(ref), @intFromEnum(ResetEventFutex.waiting)); state = @atomicLoad(ResetEventFutex, ref, .acquire); } assert(state == .is_set); @@ -6140,6 +6958,7 @@ const ResetEventPosix = struct { .waiting => unreachable, // Invalid state. .is_set => return, }; + const current_thread = Thread.getCurrent(t); assert(std.c.pthread_mutex_lock(&rep.mutex) == .SUCCESS); defer assert(std.c.pthread_mutex_unlock(&rep.mutex) == .SUCCESS); sw: switch (rep.state) { @@ -6148,8 +6967,9 @@ const ResetEventPosix = struct { continue :sw .waiting; }, .waiting => { - try t.checkCancel(); + try current_thread.beginSyscall(); assert(std.c.pthread_cond_wait(&rep.cond, &rep.mutex) == .SUCCESS); + current_thread.endSyscall(); continue :sw rep.state; }, .is_set => return, @@ -6222,10 +7042,10 @@ const Wsa = struct { } || Io.UnexpectedError; }; -fn initializeWsa(t: *Threaded) error{NetworkDown}!void { +fn initializeWsa(t: *Threaded) error{ NetworkDown, Canceled }!void { const t_io = io(t); const wsa = &t.wsa; - wsa.mutex.lockUncancelable(t_io); + try wsa.mutex.lock(t_io); defer wsa.mutex.unlock(t_io); switch (wsa.status) { .uninitialized => { @@ -6237,12 +7057,15 @@ fn initializeWsa(t: *Threaded) error{NetworkDown}!void { wsa.status = .initialized; return; }, - else => |err_int| switch (@as(ws2_32.WinsockError, @enumFromInt(@as(u16, @intCast(err_int))))) { - .SYSNOTREADY => wsa.init_error = error.NetworkDown, - .VERNOTSUPPORTED => wsa.init_error = error.VersionUnsupported, - .EINPROGRESS => wsa.init_error = error.BlockingOperationInProgress, - .EPROCLIM => wsa.init_error = error.ProcessFdQuotaExceeded, - else => |err| wsa.init_error = windows.unexpectedWSAError(err), + else => |err_int| { + wsa.status = .failure; + wsa.init_error = switch (@as(ws2_32.WinsockError, @enumFromInt(@as(u16, @intCast(err_int))))) { + .SYSNOTREADY => error.NetworkDown, + .VERNOTSUPPORTED => error.VersionUnsupported, + .EINPROGRESS => error.BlockingOperationInProgress, + .EPROCLIM => error.ProcessFdQuotaExceeded, + else => |err| windows.unexpectedWSAError(err), + }; }, } }, diff --git a/lib/std/c.zig b/lib/std/c.zig index f22112a086..886c5e902c 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -10881,6 +10881,23 @@ pub extern "c" fn pthread_create( start_routine: *const fn (?*anyopaque) callconv(.c) ?*anyopaque, noalias arg: ?*anyopaque, ) E; +pub const pthread_cancelstate = switch (native_os) { + .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => enum(c_int) { + ENABLE = 1, + DISABLE = 0, + }, + .linux => if (native_abi.isMusl()) enum(c_int) { + ENABLE = 0, + DISABLE = 1, + MASKED = 2, + } else if (native_abi.isGnu()) enum(c_int) { + ENABLE = 0, + DISABLE = 1, + }, + else => void, +}; +pub extern "c" fn pthread_setcancelstate(pthread_cancelstate, ?*pthread_cancelstate) E; +pub extern "c" fn pthread_cancel(pthread_t) E; pub extern "c" fn pthread_attr_init(attr: *pthread_attr_t) E; pub extern "c" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *anyopaque, stacksize: usize) E; pub extern "c" fn pthread_attr_setstacksize(attr: *pthread_attr_t, stacksize: usize) E; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 2029356a66..b80d7c606f 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1748,9 +1748,7 @@ pub fn settimeofday(tv: *const timeval, tz: *const timezone) usize { } pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { - if (native_arch == .riscv32) { - @compileError("No nanosleep syscall on this architecture."); - } else return syscall2(.nanosleep, @intFromPtr(req), @intFromPtr(rem)); + return syscall2(.nanosleep, @intFromPtr(req), @intFromPtr(rem)); } pub fn pause() usize { @@ -3773,6 +3771,7 @@ pub const SIG = if (is_mips) enum(u32) { PROF = 29, XCPU = 30, XFZ = 31, + _, } else if (is_sparc) enum(u32) { pub const BLOCK = 1; pub const UNBLOCK = 2; @@ -3818,6 +3817,7 @@ pub const SIG = if (is_mips) enum(u32) { LOST = 29, USR1 = 30, USR2 = 31, + _, } else enum(u32) { pub const BLOCK = 0; pub const UNBLOCK = 1; @@ -3861,6 +3861,7 @@ pub const SIG = if (is_mips) enum(u32) { IO = 29, PWR = 30, SYS = 31, + _, }; pub const kernel_rwf = u32; diff --git a/lib/std/posix.zig b/lib/std/posix.zig index 3e80466cd3..4176910537 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -1360,6 +1360,7 @@ pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { .PIPE => return error.BrokenPipe, .CONNRESET => return error.ConnectionResetByPeer, .BUSY => return error.DeviceBusy, + .CANCELED => return error.Canceled, else => |err| return unexpectedErrno(err), } } diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 8d8bc71a30..80ca049deb 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -1499,22 +1499,18 @@ const aarch64 = struct { .ABS64 => { try atom.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file); }, - .ADR_PREL_PG_HI21 => { try atom.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file); }, - .ADR_GOT_PAGE => { // TODO: relax if possible symbol.flags.needs_got = true; }, - .LD64_GOT_LO12_NC, .LD64_GOTPAGE_LO15, => { symbol.flags.needs_got = true; }, - .CALL26, .JUMP26, => { @@ -1522,25 +1518,21 @@ const aarch64 = struct { symbol.flags.needs_plt = true; } }, - .TLSLE_ADD_TPREL_HI12, .TLSLE_ADD_TPREL_LO12_NC, => { if (is_dyn_lib) try atom.reportPicError(symbol, rel, elf_file); }, - .TLSIE_ADR_GOTTPREL_PAGE21, .TLSIE_LD64_GOTTPREL_LO12_NC, => { symbol.flags.needs_gottp = true; }, - .TLSGD_ADR_PAGE21, .TLSGD_ADD_LO12_NC, => { symbol.flags.needs_tlsgd = true; }, - .TLSDESC_ADR_PAGE21, .TLSDESC_LD64_LO12, .TLSDESC_ADD_LO12, @@ -1551,18 +1543,17 @@ const aarch64 = struct { symbol.flags.needs_tlsdesc = true; } }, - .ADD_ABS_LO12_NC, .ADR_PREL_LO21, - .LDST8_ABS_LO12_NC, + .CONDBR19, + .LDST128_ABS_LO12_NC, .LDST16_ABS_LO12_NC, .LDST32_ABS_LO12_NC, .LDST64_ABS_LO12_NC, - .LDST128_ABS_LO12_NC, + .LDST8_ABS_LO12_NC, .PREL32, .PREL64, => {}, - else => try atom.reportUnhandledRelocError(rel, elf_file), } } @@ -1599,7 +1590,6 @@ const aarch64 = struct { r_offset, ); }, - .CALL26, .JUMP26, => { @@ -1611,27 +1601,26 @@ const aarch64 = struct { }; util.writeBranchImm(disp, code); }, - + .CONDBR19 => { + const value = math.cast(i19, S + A - P) orelse return error.Overflow; + util.writeCondBrImm(value, code); + }, .PREL32 => { const value = math.cast(i32, S + A - P) orelse return error.Overflow; mem.writeInt(u32, code, @bitCast(value), .little); }, - .PREL64 => { const value = S + A - P; mem.writeInt(u64, code_buffer[r_offset..][0..8], @bitCast(value), .little); }, - .ADR_PREL_LO21 => { const value = math.cast(i21, S + A - P) orelse return error.Overflow; util.writeAdrInst(value, code); }, - .ADR_PREL_PG_HI21 => { // TODO: check for relaxation of ADRP+ADD util.writeAdrInst(try util.calcNumberOfPages(P, S + A), code); }, - .ADR_GOT_PAGE => if (target.flags.has_got) { util.writeAdrInst(try util.calcNumberOfPages(P, G + GOT + A), code); } else { @@ -1644,18 +1633,15 @@ const aarch64 = struct { r_offset, }); }, - .LD64_GOT_LO12_NC => { assert(target.flags.has_got); const taddr = @as(u64, @intCast(G + GOT + A)); util.writeLoadStoreRegInst(@divExact(@as(u12, @truncate(taddr)), 8), code); }, - .ADD_ABS_LO12_NC => { const taddr = @as(u64, @intCast(S + A)); util.writeAddImmInst(@truncate(taddr), code); }, - .LDST8_ABS_LO12_NC, .LDST16_ABS_LO12_NC, .LDST32_ABS_LO12_NC, @@ -1674,44 +1660,37 @@ const aarch64 = struct { }; util.writeLoadStoreRegInst(off, code); }, - .TLSLE_ADD_TPREL_HI12 => { const value = math.cast(i12, (S + A - TP) >> 12) orelse return error.Overflow; util.writeAddImmInst(@bitCast(value), code); }, - .TLSLE_ADD_TPREL_LO12_NC => { const value: i12 = @truncate(S + A - TP); util.writeAddImmInst(@bitCast(value), code); }, - .TLSIE_ADR_GOTTPREL_PAGE21 => { const S_ = target.gotTpAddress(elf_file); relocs_log.debug(" [{x} => {x}]", .{ P, S_ + A }); util.writeAdrInst(try util.calcNumberOfPages(P, S_ + A), code); }, - .TLSIE_LD64_GOTTPREL_LO12_NC => { const S_ = target.gotTpAddress(elf_file); relocs_log.debug(" [{x} => {x}]", .{ P, S_ + A }); const off: u12 = try math.divExact(u12, @truncate(@as(u64, @bitCast(S_ + A))), 8); util.writeLoadStoreRegInst(off, code); }, - .TLSGD_ADR_PAGE21 => { const S_ = target.tlsGdAddress(elf_file); relocs_log.debug(" [{x} => {x}]", .{ P, S_ + A }); util.writeAdrInst(try util.calcNumberOfPages(P, S_ + A), code); }, - .TLSGD_ADD_LO12_NC => { const S_ = target.tlsGdAddress(elf_file); relocs_log.debug(" [{x} => {x}]", .{ P, S_ + A }); const off: u12 = @truncate(@as(u64, @bitCast(S_ + A))); util.writeAddImmInst(off, code); }, - .TLSDESC_ADR_PAGE21 => { if (target.flags.has_tlsdesc) { const S_ = target.tlsDescAddress(elf_file); @@ -1722,7 +1701,6 @@ const aarch64 = struct { util.encoding.Instruction.nop().write(code); } }, - .TLSDESC_LD64_LO12 => { if (target.flags.has_tlsdesc) { const S_ = target.tlsDescAddress(elf_file); @@ -1734,7 +1712,6 @@ const aarch64 = struct { util.encoding.Instruction.nop().write(code); } }, - .TLSDESC_ADD_LO12 => { if (target.flags.has_tlsdesc) { const S_ = target.tlsDescAddress(elf_file); @@ -1747,13 +1724,11 @@ const aarch64 = struct { util.encoding.Instruction.movz(.x0, value, .{ .lsl = .@"16" }).write(code); } }, - .TLSDESC_CALL => if (!target.flags.has_tlsdesc) { relocs_log.debug(" relaxing br => movk(x0, {x})", .{S + A - TP}); const value: u16 = @bitCast(@as(i16, @truncate(S + A - TP))); util.encoding.Instruction.movk(.x0, value, .{}).write(code); }, - else => try atom.reportUnhandledRelocError(rel, elf_file), } } diff --git a/src/link/aarch64.zig b/src/link/aarch64.zig index c9defc27b3..0536316f4c 100644 --- a/src/link/aarch64.zig +++ b/src/link/aarch64.zig @@ -29,6 +29,12 @@ pub fn writeBranchImm(disp: i28, code: *[4]u8) void { inst.write(code); } +pub fn writeCondBrImm(disp: i19, code: *[4]u8) void { + var inst: encoding.Instruction = .read(code); + inst.branch_exception_generating_system.conditional_branch_immediate.group.imm19 = @intCast(@shrExact(disp, 2)); + inst.write(code); +} + const assert = std.debug.assert; const builtin = @import("builtin"); const math = std.math;