| author | |
| committer | |
| log | 6f7968f1658ef92fb878f0284af7761aaaa3e71a |
| tree | 468179e8088f533e6d7deb24de94793de79e2ab3 |
| parent | cc38acfea30218ba8a1ad29d4787528ff3014e64 |
| parent | ce890060350a848590a7371f11316c2040a89a2b |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/3074115 files changed, 315 insertions(+), 1521 deletions(-)
lib/compiler/aro/aro/Driver.zig+2-2| ... | @@ -1219,12 +1219,12 @@ pub fn getDepFileName(d: *Driver, source: Source, buf: *[std.fs.max_name_bytes]u | ... | @@ -1219,12 +1219,12 @@ pub fn getDepFileName(d: *Driver, source: Source, buf: *[std.fs.max_name_bytes]u |
| 1219 | fn getRandomFilename(d: *Driver, buf: *[std.fs.max_name_bytes]u8, extension: []const u8) ![]const u8 { | 1219 | fn getRandomFilename(d: *Driver, buf: *[std.fs.max_name_bytes]u8, extension: []const u8) ![]const u8 { |
| 1220 | const io = d.comp.io; | 1220 | const io = d.comp.io; |
| 1221 | const random_bytes_count = 12; | 1221 | const random_bytes_count = 12; |
| 1222 | const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count); | 1222 | const sub_path_len = comptime std.base64.url_safe.Encoder.calcSize(random_bytes_count); |
| 1223 | 1223 | ||
| 1224 | var random_bytes: [random_bytes_count]u8 = undefined; | 1224 | var random_bytes: [random_bytes_count]u8 = undefined; |
| 1225 | io.random(&random_bytes); | 1225 | io.random(&random_bytes); |
| 1226 | var random_name: [sub_path_len]u8 = undefined; | 1226 | var random_name: [sub_path_len]u8 = undefined; |
| 1227 | _ = std.fs.base64_encoder.encode(&random_name, &random_bytes); | 1227 | _ = std.base64.url_safe.Encoder.encode(&random_name, &random_bytes); |
| 1228 | 1228 | ||
| 1229 | const fmt_template = "/tmp/{s}{s}"; | 1229 | const fmt_template = "/tmp/{s}{s}"; |
| 1230 | const fmt_args = .{ | 1230 | const fmt_args = .{ |
lib/std/Build/Watch.zig+7-5| ... | @@ -1,5 +1,7 @@ | ... | @@ -1,5 +1,7 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | |||
| 2 | const std = @import("../std.zig"); | 3 | const std = @import("../std.zig"); |
| 4 | const Io = std.Io; | ||
| 3 | const Step = std.Build.Step; | 5 | const Step = std.Build.Step; |
| 4 | const Allocator = std.mem.Allocator; | 6 | const Allocator = std.mem.Allocator; |
| 5 | const assert = std.debug.assert; | 7 | const assert = std.debug.assert; |
| ... | @@ -666,7 +668,7 @@ const Os = switch (builtin.os.tag) { | ... | @@ -666,7 +668,7 @@ const Os = switch (builtin.os.tag) { |
| 666 | .dir_table = .{}, | 668 | .dir_table = .{}, |
| 667 | .dir_count = 0, | 669 | .dir_count = 0, |
| 668 | .os = .{ | 670 | .os = .{ |
| 669 | .kq_fd = try posix.kqueue(), | 671 | .kq_fd = try Io.Kqueue.createFileDescriptor(), |
| 670 | .handles = .empty, | 672 | .handles = .empty, |
| 671 | }, | 673 | }, |
| 672 | .generation = 0, | 674 | .generation = 0, |
| ... | @@ -697,7 +699,7 @@ const Os = switch (builtin.os.tag) { | ... | @@ -697,7 +699,7 @@ const Os = switch (builtin.os.tag) { |
| 697 | .data = 0, | 699 | .data = 0, |
| 698 | .udata = gop.index, | 700 | .udata = gop.index, |
| 699 | }}; | 701 | }}; |
| 700 | _ = try posix.kevent(w.os.kq_fd, &changes, &.{}, null); | 702 | _ = try Io.Kqueue.kevent(w.os.kq_fd, &changes, &.{}, null); |
| 701 | assert(handles.len == gop.index); | 703 | assert(handles.len == gop.index); |
| 702 | try handles.append(gpa, .{ | 704 | try handles.append(gpa, .{ |
| 703 | .rs = .{}, | 705 | .rs = .{}, |
| ... | @@ -787,7 +789,7 @@ const Os = switch (builtin.os.tag) { | ... | @@ -787,7 +789,7 @@ const Os = switch (builtin.os.tag) { |
| 787 | }, | 789 | }, |
| 788 | }; | 790 | }; |
| 789 | const filtered_changes = if (i == handles.len - 1) changes[0..1] else &changes; | 791 | const filtered_changes = if (i == handles.len - 1) changes[0..1] else &changes; |
| 790 | _ = try posix.kevent(w.os.kq_fd, filtered_changes, &.{}, null); | 792 | _ = try Io.Kqueue.kevent(w.os.kq_fd, filtered_changes, &.{}, null); |
| 791 | if (path.sub_path.len != 0) posix.close(dir_fd); | 793 | if (path.sub_path.len != 0) posix.close(dir_fd); |
| 792 | 794 | ||
| 793 | w.dir_table.swapRemoveAt(i); | 795 | w.dir_table.swapRemoveAt(i); |
| ... | @@ -801,13 +803,13 @@ const Os = switch (builtin.os.tag) { | ... | @@ -801,13 +803,13 @@ const Os = switch (builtin.os.tag) { |
| 801 | fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult { | 803 | fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult { |
| 802 | var timespec_buffer: posix.timespec = undefined; | 804 | var timespec_buffer: posix.timespec = undefined; |
| 803 | var event_buffer: [100]posix.Kevent = undefined; | 805 | var event_buffer: [100]posix.Kevent = undefined; |
| 804 | var n = try posix.kevent(w.os.kq_fd, &.{}, &event_buffer, timeout.toTimespec(&timespec_buffer)); | 806 | var n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, timeout.toTimespec(&timespec_buffer)); |
| 805 | if (n == 0) return .timeout; | 807 | if (n == 0) return .timeout; |
| 806 | const reaction_sets = w.os.handles.items(.rs); | 808 | const reaction_sets = w.os.handles.items(.rs); |
| 807 | var any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], false); | 809 | var any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], false); |
| 808 | timespec_buffer = .{ .sec = 0, .nsec = 0 }; | 810 | timespec_buffer = .{ .sec = 0, .nsec = 0 }; |
| 809 | while (n == event_buffer.len) { | 811 | while (n == event_buffer.len) { |
| 810 | n = try posix.kevent(w.os.kq_fd, &.{}, &event_buffer, &timespec_buffer); | 812 | n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, &timespec_buffer); |
| 811 | if (n == 0) break; | 813 | if (n == 0) break; |
| 812 | any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], any_dirty); | 814 | any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], any_dirty); |
| 813 | } | 815 | } |
lib/std/Io.zig+6-4| ... | @@ -540,18 +540,20 @@ test { | ... | @@ -540,18 +540,20 @@ test { |
| 540 | 540 | ||
| 541 | const Io = @This(); | 541 | const Io = @This(); |
| 542 | 542 | ||
| 543 | pub const Threaded = @import("Io/Threaded.zig"); | ||
| 543 | pub const Evented = switch (builtin.os.tag) { | 544 | pub const Evented = switch (builtin.os.tag) { |
| 544 | .linux => switch (builtin.cpu.arch) { | 545 | .linux => switch (builtin.cpu.arch) { |
| 545 | .x86_64, .aarch64 => @import("Io/IoUring.zig"), | 546 | .x86_64, .aarch64 => IoUring, |
| 546 | else => void, // context-switching code not implemented yet | 547 | else => void, // context-switching code not implemented yet |
| 547 | }, | 548 | }, |
| 548 | .dragonfly, .freebsd, .netbsd, .openbsd, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (builtin.cpu.arch) { | 549 | .dragonfly, .freebsd, .netbsd, .openbsd, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (builtin.cpu.arch) { |
| 549 | .x86_64, .aarch64 => @import("Io/Kqueue.zig"), | 550 | .x86_64, .aarch64 => Kqueue, |
| 550 | else => void, // context-switching code not implemented yet | 551 | else => void, // context-switching code not implemented yet |
| 551 | }, | 552 | }, |
| 552 | else => void, | 553 | else => void, |
| 553 | }; | 554 | }; |
| 554 | pub const Threaded = @import("Io/Threaded.zig"); | 555 | pub const Kqueue = @import("Io/Kqueue.zig"); |
| 556 | pub const IoUring = @import("Io/IoUring.zig"); | ||
| 555 | pub const net = @import("Io/net.zig"); | 557 | pub const net = @import("Io/net.zig"); |
| 556 | 558 | ||
| 557 | userdata: ?*anyopaque, | 559 | userdata: ?*anyopaque, |
| ... | @@ -1356,7 +1358,7 @@ pub const Mutex = extern struct { | ... | @@ -1356,7 +1358,7 @@ pub const Mutex = extern struct { |
| 1356 | 1358 | ||
| 1357 | pub const init: Mutex = .{ .state = .init(.unlocked) }; | 1359 | pub const init: Mutex = .{ .state = .init(.unlocked) }; |
| 1358 | 1360 | ||
| 1359 | const State = enum(u32) { | 1361 | pub const State = enum(u32) { |
| 1360 | unlocked, | 1362 | unlocked, |
| 1361 | locked_once, | 1363 | locked_once, |
| 1362 | contended, | 1364 | contended, |
lib/std/Io/Kqueue.zig+164-185| ... | @@ -157,8 +157,11 @@ pub const InitOptions = struct { | ... | @@ -157,8 +157,11 @@ pub const InitOptions = struct { |
| 157 | n_threads: ?usize = null, | 157 | n_threads: ?usize = null, |
| 158 | }; | 158 | }; |
| 159 | 159 | ||
| 160 | pub const InitError = Allocator.Error || CreateFileDescriptorError; | ||
| 161 | |||
| 160 | pub fn init(k: *Kqueue, gpa: Allocator, options: InitOptions) !void { | 162 | pub fn init(k: *Kqueue, gpa: Allocator, options: InitOptions) !void { |
| 161 | assert(options.n_threads != 0); | 163 | assert(options.n_threads != 0); |
| 164 | |||
| 162 | const n_threads = @max(1, options.n_threads orelse std.Thread.getCpuCount() catch 1); | 165 | const n_threads = @max(1, options.n_threads orelse std.Thread.getCpuCount() catch 1); |
| 163 | const threads_size = n_threads * @sizeOf(Thread); | 166 | const threads_size = n_threads * @sizeOf(Thread); |
| 164 | const idle_stack_end_offset = std.mem.alignForward(usize, threads_size + idle_stack_size, std.heap.page_size_max); | 167 | const idle_stack_end_offset = std.mem.alignForward(usize, threads_size + idle_stack_size, std.heap.page_size_max); |
| ... | @@ -204,7 +207,7 @@ pub fn init(k: *Kqueue, gpa: Allocator, options: InitOptions) !void { | ... | @@ -204,7 +207,7 @@ pub fn init(k: *Kqueue, gpa: Allocator, options: InitOptions) !void { |
| 204 | }, | 207 | }, |
| 205 | .current_context = &main_fiber.context, | 208 | .current_context = &main_fiber.context, |
| 206 | .ready_queue = null, | 209 | .ready_queue = null, |
| 207 | .kq_fd = try posix.kqueue(), | 210 | .kq_fd = try createFileDescriptor(), |
| 208 | .idle_search_index = 1, | 211 | .idle_search_index = 1, |
| 209 | .steal_ready_search_index = 1, | 212 | .steal_ready_search_index = 1, |
| 210 | .wait_queues = .empty, | 213 | .wait_queues = .empty, |
| ... | @@ -231,6 +234,23 @@ pub fn deinit(k: *Kqueue) void { | ... | @@ -231,6 +234,23 @@ pub fn deinit(k: *Kqueue) void { |
| 231 | k.* = undefined; | 234 | k.* = undefined; |
| 232 | } | 235 | } |
| 233 | 236 | ||
| 237 | pub const CreateFileDescriptorError = error{ | ||
| 238 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 239 | ProcessFdQuotaExceeded, | ||
| 240 | /// The system-wide limit on the total number of open files has been reached. | ||
| 241 | SystemFdQuotaExceeded, | ||
| 242 | } || Io.UnexpectedError; | ||
| 243 | |||
| 244 | pub fn createFileDescriptor() CreateFileDescriptorError!posix.fd_t { | ||
| 245 | const rc = posix.system.kqueue(); | ||
| 246 | switch (posix.errno(rc)) { | ||
| 247 | .SUCCESS => return @intCast(rc), | ||
| 248 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 249 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 250 | else => |err| return posix.unexpectedErrno(err), | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 234 | fn findReadyFiber(k: *Kqueue, thread: *Thread) ?*Fiber { | 254 | fn findReadyFiber(k: *Kqueue, thread: *Thread) ?*Fiber { |
| 235 | if (@atomicRmw(?*Fiber, &thread.ready_queue, .Xchg, Fiber.finished, .acquire)) |ready_fiber| { | 255 | if (@atomicRmw(?*Fiber, &thread.ready_queue, .Xchg, Fiber.finished, .acquire)) |ready_fiber| { |
| 236 | @atomicStore(?*Fiber, &thread.ready_queue, ready_fiber.queue_next, .release); | 256 | @atomicStore(?*Fiber, &thread.ready_queue, ready_fiber.queue_next, .release); |
| ... | @@ -314,7 +334,10 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void { | ... | @@ -314,7 +334,10 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void { |
| 314 | }, | 334 | }, |
| 315 | }; | 335 | }; |
| 316 | // If an error occurs it only pessimises scheduling. | 336 | // If an error occurs it only pessimises scheduling. |
| 317 | _ = posix.kevent(idle_search_thread.kq_fd, &changes, &.{}, null) catch {}; | 337 | _ = kevent(idle_search_thread.kq_fd, &changes, &.{}, null) catch |err| { |
| 338 | // TODO handle EINTR for cancellation purposes | ||
| 339 | @panic(@errorName(err)); // TODO | ||
| 340 | }; | ||
| 318 | return; | 341 | return; |
| 319 | } | 342 | } |
| 320 | spawn_thread: { | 343 | spawn_thread: { |
| ... | @@ -334,7 +357,7 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void { | ... | @@ -334,7 +357,7 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void { |
| 334 | .idle_context = undefined, | 357 | .idle_context = undefined, |
| 335 | .current_context = &new_thread.idle_context, | 358 | .current_context = &new_thread.idle_context, |
| 336 | .ready_queue = ready_queue.head, | 359 | .ready_queue = ready_queue.head, |
| 337 | .kq_fd = posix.kqueue() catch |err| { | 360 | .kq_fd = createFileDescriptor() catch |err| { |
| 338 | @atomicStore(u32, &k.threads.reserved, new_thread_index, .release); | 361 | @atomicStore(u32, &k.threads.reserved, new_thread_index, .release); |
| 339 | // no more access to `thread` after giving up reservation | 362 | // no more access to `thread` after giving up reservation |
| 340 | std.log.warn("unable to create worker thread due to kqueue init failure: {t}", .{err}); | 363 | std.log.warn("unable to create worker thread due to kqueue init failure: {t}", .{err}); |
| ... | @@ -409,9 +432,9 @@ fn idle(k: *Kqueue, thread: *Thread) void { | ... | @@ -409,9 +432,9 @@ fn idle(k: *Kqueue, thread: *Thread) void { |
| 409 | k.yield(ready_fiber, .nothing); | 432 | k.yield(ready_fiber, .nothing); |
| 410 | maybe_ready_fiber = null; | 433 | maybe_ready_fiber = null; |
| 411 | } | 434 | } |
| 412 | const n = posix.kevent(thread.kq_fd, &.{}, &events_buffer, null) catch |err| { | 435 | const n = kevent(thread.kq_fd, &.{}, &events_buffer, null) catch |err| { |
| 413 | // TODO handle EINTR for cancellation purposes | 436 | // TODO handle EINTR for cancellation purposes |
| 414 | @panic(@errorName(err)); | 437 | @panic(@errorName(err)); // TODO |
| 415 | }; | 438 | }; |
| 416 | var maybe_ready_queue: ?Fiber.Queue = null; | 439 | var maybe_ready_queue: ?Fiber.Queue = null; |
| 417 | for (events_buffer[0..n]) |event| switch (@as(Completion.UserData, @enumFromInt(event.udata))) { | 440 | for (events_buffer[0..n]) |event| switch (@as(Completion.UserData, @enumFromInt(event.udata))) { |
| ... | @@ -471,14 +494,6 @@ const SwitchMessage = struct { | ... | @@ -471,14 +494,6 @@ const SwitchMessage = struct { |
| 471 | recycle: *Fiber, | 494 | recycle: *Fiber, |
| 472 | register_awaiter: *?*Fiber, | 495 | register_awaiter: *?*Fiber, |
| 473 | register_select: []const *Io.AnyFuture, | 496 | register_select: []const *Io.AnyFuture, |
| 474 | mutex_lock: struct { | ||
| 475 | prev_state: Io.Mutex.State, | ||
| 476 | mutex: *Io.Mutex, | ||
| 477 | }, | ||
| 478 | condition_wait: struct { | ||
| 479 | cond: *Io.Condition, | ||
| 480 | mutex: *Io.Mutex, | ||
| 481 | }, | ||
| 482 | exit, | 497 | exit, |
| 483 | }; | 498 | }; |
| 484 | 499 | ||
| ... | @@ -514,59 +529,6 @@ const SwitchMessage = struct { | ... | @@ -514,59 +529,6 @@ const SwitchMessage = struct { |
| 514 | } | 529 | } |
| 515 | } | 530 | } |
| 516 | }, | 531 | }, |
| 517 | .mutex_lock => |mutex_lock| { | ||
| 518 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); | ||
| 519 | assert(prev_fiber.queue_next == null); | ||
| 520 | var prev_state = mutex_lock.prev_state; | ||
| 521 | while (switch (prev_state) { | ||
| 522 | else => next_state: { | ||
| 523 | prev_fiber.queue_next = @ptrFromInt(@intFromEnum(prev_state)); | ||
| 524 | break :next_state @cmpxchgWeak( | ||
| 525 | Io.Mutex.State, | ||
| 526 | &mutex_lock.mutex.state, | ||
| 527 | prev_state, | ||
| 528 | @enumFromInt(@intFromPtr(prev_fiber)), | ||
| 529 | .release, | ||
| 530 | .acquire, | ||
| 531 | ); | ||
| 532 | }, | ||
| 533 | .unlocked => @cmpxchgWeak( | ||
| 534 | Io.Mutex.State, | ||
| 535 | &mutex_lock.mutex.state, | ||
| 536 | .unlocked, | ||
| 537 | .locked_once, | ||
| 538 | .acquire, | ||
| 539 | .acquire, | ||
| 540 | ) orelse { | ||
| 541 | prev_fiber.queue_next = null; | ||
| 542 | k.schedule(thread, .{ .head = prev_fiber, .tail = prev_fiber }); | ||
| 543 | return; | ||
| 544 | }, | ||
| 545 | }) |next_state| prev_state = next_state; | ||
| 546 | }, | ||
| 547 | .condition_wait => |condition_wait| { | ||
| 548 | const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev)); | ||
| 549 | assert(prev_fiber.queue_next == null); | ||
| 550 | const cond_impl = prev_fiber.resultPointer(Condition); | ||
| 551 | cond_impl.* = .{ | ||
| 552 | .tail = prev_fiber, | ||
| 553 | .event = .queued, | ||
| 554 | }; | ||
| 555 | if (@cmpxchgStrong( | ||
| 556 | ?*Fiber, | ||
| 557 | @as(*?*Fiber, @ptrCast(&condition_wait.cond.state)), | ||
| 558 | null, | ||
| 559 | prev_fiber, | ||
| 560 | .release, | ||
| 561 | .acquire, | ||
| 562 | )) |waiting_fiber| { | ||
| 563 | const waiting_cond_impl = waiting_fiber.?.resultPointer(Condition); | ||
| 564 | assert(waiting_cond_impl.tail.queue_next == null); | ||
| 565 | waiting_cond_impl.tail.queue_next = prev_fiber; | ||
| 566 | waiting_cond_impl.tail = prev_fiber; | ||
| 567 | } | ||
| 568 | condition_wait.mutex.unlock(k.io()); | ||
| 569 | }, | ||
| 570 | .exit => for (k.threads.allocated[0..@atomicLoad(u32, &k.threads.active, .acquire)]) |*each_thread| { | 532 | .exit => for (k.threads.allocated[0..@atomicLoad(u32, &k.threads.active, .acquire)]) |*each_thread| { |
| 571 | const changes = [_]posix.Kevent{ | 533 | const changes = [_]posix.Kevent{ |
| 572 | .{ | 534 | .{ |
| ... | @@ -578,8 +540,9 @@ const SwitchMessage = struct { | ... | @@ -578,8 +540,9 @@ const SwitchMessage = struct { |
| 578 | .udata = @intFromEnum(Completion.UserData.exit), | 540 | .udata = @intFromEnum(Completion.UserData.exit), |
| 579 | }, | 541 | }, |
| 580 | }; | 542 | }; |
| 581 | _ = posix.kevent(each_thread.kq_fd, &changes, &.{}, null) catch |err| { | 543 | _ = kevent(each_thread.kq_fd, &changes, &.{}, null) catch |err| { |
| 582 | @panic(@errorName(err)); | 544 | // TODO handle EINTR for cancellation purposes |
| 545 | @panic(@errorName(err)); // TODO | ||
| 583 | }; | 546 | }; |
| 584 | }, | 547 | }, |
| 585 | } | 548 | } |
| ... | @@ -854,21 +817,13 @@ pub fn io(k: *Kqueue) Io { | ... | @@ -854,21 +817,13 @@ pub fn io(k: *Kqueue) Io { |
| 854 | .concurrent = concurrent, | 817 | .concurrent = concurrent, |
| 855 | .await = await, | 818 | .await = await, |
| 856 | .cancel = cancel, | 819 | .cancel = cancel, |
| 857 | .cancelRequested = cancelRequested, | ||
| 858 | .select = select, | 820 | .select = select, |
| 859 | 821 | ||
| 860 | .groupAsync = groupAsync, | 822 | .groupAsync = groupAsync, |
| 861 | .groupWait = groupWait, | 823 | .groupConcurrent = groupConcurrent, |
| 824 | .groupAwait = groupAwait, | ||
| 862 | .groupCancel = groupCancel, | 825 | .groupCancel = groupCancel, |
| 863 | 826 | ||
| 864 | .mutexLock = mutexLock, | ||
| 865 | .mutexLockUncancelable = mutexLockUncancelable, | ||
| 866 | .mutexUnlock = mutexUnlock, | ||
| 867 | |||
| 868 | .conditionWait = conditionWait, | ||
| 869 | .conditionWaitUncancelable = conditionWaitUncancelable, | ||
| 870 | .conditionWake = conditionWake, | ||
| 871 | |||
| 872 | .dirCreateDir = dirCreateDir, | 827 | .dirCreateDir = dirCreateDir, |
| 873 | .dirCreateDirPath = dirCreateDirPath, | 828 | .dirCreateDirPath = dirCreateDirPath, |
| 874 | .dirCreateDirPathOpen = dirCreateDirPathOpen, | 829 | .dirCreateDirPathOpen = dirCreateDirPathOpen, |
| ... | @@ -888,7 +843,6 @@ pub fn io(k: *Kqueue) Io { | ... | @@ -888,7 +843,6 @@ pub fn io(k: *Kqueue) Io { |
| 888 | .fileReadPositional = fileReadPositional, | 843 | .fileReadPositional = fileReadPositional, |
| 889 | .fileSeekBy = fileSeekBy, | 844 | .fileSeekBy = fileSeekBy, |
| 890 | .fileSeekTo = fileSeekTo, | 845 | .fileSeekTo = fileSeekTo, |
| 891 | .openExecutable = openExecutable, | ||
| 892 | 846 | ||
| 893 | .now = now, | 847 | .now = now, |
| 894 | .sleep = sleep, | 848 | .sleep = sleep, |
| ... | @@ -1013,139 +967,111 @@ fn cancelRequested(userdata: ?*anyopaque) bool { | ... | @@ -1013,139 +967,111 @@ fn cancelRequested(userdata: ?*anyopaque) bool { |
| 1013 | 967 | ||
| 1014 | fn groupAsync( | 968 | fn groupAsync( |
| 1015 | userdata: ?*anyopaque, | 969 | userdata: ?*anyopaque, |
| 1016 | group: *Io.Group, | 970 | type_erased: *Io.Group, |
| 1017 | context: []const u8, | 971 | context: []const u8, |
| 1018 | context_alignment: std.mem.Alignment, | 972 | context_alignment: Alignment, |
| 1019 | start: *const fn (*Io.Group, context: *const anyopaque) void, | 973 | start: *const fn (context: *const anyopaque) Io.Cancelable!void, |
| 1020 | ) void { | 974 | ) void { |
| 1021 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 975 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1022 | _ = k; | 976 | _ = k; |
| 1023 | _ = group; | 977 | _ = type_erased; |
| 1024 | _ = context; | 978 | _ = context; |
| 1025 | _ = context_alignment; | 979 | _ = context_alignment; |
| 1026 | _ = start; | 980 | _ = start; |
| 1027 | @panic("TODO"); | 981 | @panic("TODO"); |
| 1028 | } | 982 | } |
| 1029 | 983 | ||
| 1030 | fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void { | 984 | fn groupConcurrent( |
| 1031 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 985 | userdata: ?*anyopaque, |
| 1032 | _ = k; | 986 | type_erased: *Io.Group, |
| 1033 | _ = group; | 987 | context: []const u8, |
| 1034 | _ = token; | 988 | context_alignment: Alignment, |
| 1035 | @panic("TODO"); | 989 | start: *const fn (context: *const anyopaque) Io.Cancelable!void, |
| 1036 | } | 990 | ) Io.ConcurrentError!void { |
| 1037 | |||
| 1038 | fn groupCancel(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void { | ||
| 1039 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 991 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1040 | _ = k; | 992 | _ = k; |
| 1041 | _ = group; | 993 | _ = type_erased; |
| 1042 | _ = token; | 994 | _ = context; |
| 995 | _ = context_alignment; | ||
| 996 | _ = start; | ||
| 1043 | @panic("TODO"); | 997 | @panic("TODO"); |
| 1044 | } | 998 | } |
| 1045 | 999 | ||
| 1046 | fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) Io.Cancelable!usize { | 1000 | fn groupAwait(userdata: ?*anyopaque, type_erased: *Io.Group, initial_token: *anyopaque) Io.Cancelable!void { |
| 1047 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1001 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1048 | _ = k; | 1002 | _ = k; |
| 1049 | _ = futures; | 1003 | _ = type_erased; |
| 1004 | _ = initial_token; | ||
| 1050 | @panic("TODO"); | 1005 | @panic("TODO"); |
| 1051 | } | 1006 | } |
| 1052 | 1007 | ||
| 1053 | fn mutexLock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) Io.Cancelable!void { | 1008 | fn groupCancel(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void { |
| 1054 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | ||
| 1055 | _ = k; | ||
| 1056 | _ = prev_state; | ||
| 1057 | _ = mutex; | ||
| 1058 | @panic("TODO"); | ||
| 1059 | } | ||
| 1060 | fn mutexLockUncancelable(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void { | ||
| 1061 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | ||
| 1062 | _ = k; | ||
| 1063 | _ = prev_state; | ||
| 1064 | _ = mutex; | ||
| 1065 | @panic("TODO"); | ||
| 1066 | } | ||
| 1067 | fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void { | ||
| 1068 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1009 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1069 | _ = k; | 1010 | _ = k; |
| 1070 | _ = prev_state; | 1011 | _ = group; |
| 1071 | _ = mutex; | 1012 | _ = token; |
| 1072 | @panic("TODO"); | 1013 | @panic("TODO"); |
| 1073 | } | 1014 | } |
| 1074 | 1015 | ||
| 1075 | fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) Io.Cancelable!void { | 1016 | fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) Io.Cancelable!usize { |
| 1076 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | ||
| 1077 | k.yield(null, .{ .condition_wait = .{ .cond = cond, .mutex = mutex } }); | ||
| 1078 | const thread = Thread.current(); | ||
| 1079 | const fiber = thread.currentFiber(); | ||
| 1080 | const cond_impl = fiber.resultPointer(Condition); | ||
| 1081 | try mutex.lock(k.io()); | ||
| 1082 | switch (cond_impl.event) { | ||
| 1083 | .queued => {}, | ||
| 1084 | .wake => |wake| if (fiber.queue_next) |next_fiber| switch (wake) { | ||
| 1085 | .one => if (@cmpxchgStrong( | ||
| 1086 | ?*Fiber, | ||
| 1087 | @as(*?*Fiber, @ptrCast(&cond.state)), | ||
| 1088 | null, | ||
| 1089 | next_fiber, | ||
| 1090 | .release, | ||
| 1091 | .acquire, | ||
| 1092 | )) |old_fiber| { | ||
| 1093 | const old_cond_impl = old_fiber.?.resultPointer(Condition); | ||
| 1094 | assert(old_cond_impl.tail.queue_next == null); | ||
| 1095 | old_cond_impl.tail.queue_next = next_fiber; | ||
| 1096 | old_cond_impl.tail = cond_impl.tail; | ||
| 1097 | }, | ||
| 1098 | .all => k.schedule(thread, .{ .head = next_fiber, .tail = cond_impl.tail }), | ||
| 1099 | }, | ||
| 1100 | } | ||
| 1101 | fiber.queue_next = null; | ||
| 1102 | } | ||
| 1103 | |||
| 1104 | fn conditionWaitUncancelable(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) void { | ||
| 1105 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1017 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1106 | _ = k; | 1018 | _ = k; |
| 1107 | _ = cond; | 1019 | _ = futures; |
| 1108 | _ = mutex; | ||
| 1109 | @panic("TODO"); | 1020 | @panic("TODO"); |
| 1110 | } | 1021 | } |
| 1111 | fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.Wake) void { | ||
| 1112 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | ||
| 1113 | const waiting_fiber = @atomicRmw(?*Fiber, @as(*?*Fiber, @ptrCast(&cond.state)), .Xchg, null, .acquire) orelse return; | ||
| 1114 | waiting_fiber.resultPointer(Condition).event = .{ .wake = wake }; | ||
| 1115 | k.yield(waiting_fiber, .reschedule); | ||
| 1116 | } | ||
| 1117 | 1022 | ||
| 1118 | fn dirCreateDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.CreateDirError!void { | 1023 | fn dirCreateDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void { |
| 1119 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1024 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1120 | _ = k; | 1025 | _ = k; |
| 1121 | _ = dir; | 1026 | _ = dir; |
| 1122 | _ = sub_path; | 1027 | _ = sub_path; |
| 1123 | _ = mode; | 1028 | _ = permissions; |
| 1124 | @panic("TODO"); | 1029 | @panic("TODO"); |
| 1125 | } | 1030 | } |
| 1126 | fn dirCreateDirPath(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.CreateDirError!void { | 1031 | |
| 1032 | fn dirCreateDirPath( | ||
| 1033 | userdata: ?*anyopaque, | ||
| 1034 | dir: Dir, | ||
| 1035 | sub_path: []const u8, | ||
| 1036 | permissions: Dir.Permissions, | ||
| 1037 | ) Dir.CreateDirPathError!Dir.CreatePathStatus { | ||
| 1127 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1038 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1128 | _ = k; | 1039 | _ = k; |
| 1129 | _ = dir; | 1040 | _ = dir; |
| 1130 | _ = sub_path; | 1041 | _ = sub_path; |
| 1131 | _ = mode; | 1042 | _ = permissions; |
| 1132 | @panic("TODO"); | 1043 | @panic("TODO"); |
| 1133 | } | 1044 | } |
| 1134 | fn dirCreateDirPathOpen(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.OpenOptions) Dir.CreateDirPathOpenError!Dir { | 1045 | |
| 1046 | fn dirCreateDirPathOpen( | ||
| 1047 | userdata: ?*anyopaque, | ||
| 1048 | dir: Dir, | ||
| 1049 | sub_path: []const u8, | ||
| 1050 | permissions: Dir.Permissions, | ||
| 1051 | options: Dir.OpenOptions, | ||
| 1052 | ) Dir.CreateDirPathOpenError!Dir { | ||
| 1135 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1053 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1136 | _ = k; | 1054 | _ = k; |
| 1137 | _ = dir; | 1055 | _ = dir; |
| 1138 | _ = sub_path; | 1056 | _ = sub_path; |
| 1057 | _ = permissions; | ||
| 1139 | _ = options; | 1058 | _ = options; |
| 1140 | @panic("TODO"); | 1059 | @panic("TODO"); |
| 1141 | } | 1060 | } |
| 1061 | |||
| 1142 | fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat { | 1062 | fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat { |
| 1143 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1063 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1144 | _ = k; | 1064 | _ = k; |
| 1145 | _ = dir; | 1065 | _ = dir; |
| 1146 | @panic("TODO"); | 1066 | @panic("TODO"); |
| 1147 | } | 1067 | } |
| 1148 | fn dirStatFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.StatPathOptions) Dir.StatFileError!File.Stat { | 1068 | |
| 1069 | fn dirStatFile( | ||
| 1070 | userdata: ?*anyopaque, | ||
| 1071 | dir: Dir, | ||
| 1072 | sub_path: []const u8, | ||
| 1073 | options: Dir.StatFileOptions, | ||
| 1074 | ) Dir.StatFileError!File.Stat { | ||
| 1149 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1075 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1150 | _ = k; | 1076 | _ = k; |
| 1151 | _ = dir; | 1077 | _ = dir; |
| ... | @@ -1185,10 +1111,10 @@ fn dirOpenDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Di | ... | @@ -1185,10 +1111,10 @@ fn dirOpenDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Di |
| 1185 | _ = options; | 1111 | _ = options; |
| 1186 | @panic("TODO"); | 1112 | @panic("TODO"); |
| 1187 | } | 1113 | } |
| 1188 | fn dirClose(userdata: ?*anyopaque, dir: Dir) void { | 1114 | fn dirClose(userdata: ?*anyopaque, dirs: []const Dir) void { |
| 1189 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1115 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1190 | _ = k; | 1116 | _ = k; |
| 1191 | _ = dir; | 1117 | _ = dirs; |
| 1192 | @panic("TODO"); | 1118 | @panic("TODO"); |
| 1193 | } | 1119 | } |
| 1194 | fn fileStat(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { | 1120 | fn fileStat(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| ... | @@ -1197,35 +1123,57 @@ fn fileStat(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { | ... | @@ -1197,35 +1123,57 @@ fn fileStat(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 1197 | _ = file; | 1123 | _ = file; |
| 1198 | @panic("TODO"); | 1124 | @panic("TODO"); |
| 1199 | } | 1125 | } |
| 1200 | fn fileClose(userdata: ?*anyopaque, file: File) void { | 1126 | |
| 1127 | fn fileClose(userdata: ?*anyopaque, files: []const File) void { | ||
| 1201 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1128 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1202 | _ = k; | 1129 | _ = k; |
| 1203 | _ = file; | 1130 | _ = files; |
| 1204 | @panic("TODO"); | 1131 | @panic("TODO"); |
| 1205 | } | 1132 | } |
| 1206 | fn fileWriteStreaming(userdata: ?*anyopaque, file: File, buffer: [][]const u8) File.WriteStreamingError!usize { | 1133 | |
| 1134 | fn fileWriteStreaming( | ||
| 1135 | userdata: ?*anyopaque, | ||
| 1136 | file: File, | ||
| 1137 | header: []const u8, | ||
| 1138 | data: []const []const u8, | ||
| 1139 | splat: usize, | ||
| 1140 | ) File.Writer.Error!usize { | ||
| 1207 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1141 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1208 | _ = k; | 1142 | _ = k; |
| 1209 | _ = file; | 1143 | _ = file; |
| 1210 | _ = buffer; | 1144 | _ = header; |
| 1145 | _ = data; | ||
| 1146 | _ = splat; | ||
| 1211 | @panic("TODO"); | 1147 | @panic("TODO"); |
| 1212 | } | 1148 | } |
| 1213 | fn fileWritePositional(userdata: ?*anyopaque, file: File, buffer: [][]const u8, offset: u64) File.WritePositionalError!usize { | 1149 | |
| 1150 | fn fileWritePositional( | ||
| 1151 | userdata: ?*anyopaque, | ||
| 1152 | file: File, | ||
| 1153 | header: []const u8, | ||
| 1154 | data: []const []const u8, | ||
| 1155 | splat: usize, | ||
| 1156 | offset: u64, | ||
| 1157 | ) File.WritePositionalError!usize { | ||
| 1214 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1158 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1215 | _ = k; | 1159 | _ = k; |
| 1216 | _ = file; | 1160 | _ = file; |
| 1217 | _ = buffer; | 1161 | _ = header; |
| 1162 | _ = data; | ||
| 1163 | _ = splat; | ||
| 1218 | _ = offset; | 1164 | _ = offset; |
| 1219 | @panic("TODO"); | 1165 | @panic("TODO"); |
| 1220 | } | 1166 | } |
| 1221 | fn fileReadStreaming(userdata: ?*anyopaque, file: File, data: [][]u8) File.Reader.Error!usize { | 1167 | |
| 1168 | fn fileReadStreaming(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize { | ||
| 1222 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1169 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1223 | _ = k; | 1170 | _ = k; |
| 1224 | _ = file; | 1171 | _ = file; |
| 1225 | _ = data; | 1172 | _ = data; |
| 1226 | @panic("TODO"); | 1173 | @panic("TODO"); |
| 1227 | } | 1174 | } |
| 1228 | fn fileReadPositional(userdata: ?*anyopaque, file: File, data: [][]u8, offset: u64) File.ReadPositionalError!usize { | 1175 | |
| 1176 | fn fileReadPositional(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize { | ||
| 1229 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1177 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1230 | _ = k; | 1178 | _ = k; |
| 1231 | _ = file; | 1179 | _ = file; |
| ... | @@ -1247,12 +1195,6 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, absolute_offset: u64) File.Seek | ... | @@ -1247,12 +1195,6 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, absolute_offset: u64) File.Seek |
| 1247 | _ = absolute_offset; | 1195 | _ = absolute_offset; |
| 1248 | @panic("TODO"); | 1196 | @panic("TODO"); |
| 1249 | } | 1197 | } |
| 1250 | fn openExecutable(userdata: ?*anyopaque, file: File.OpenFlags) File.OpenExecutableError!File { | ||
| 1251 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | ||
| 1252 | _ = k; | ||
| 1253 | _ = file; | ||
| 1254 | @panic("TODO"); | ||
| 1255 | } | ||
| 1256 | 1198 | ||
| 1257 | fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp { | 1199 | fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp { |
| 1258 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1200 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| ... | @@ -1518,7 +1460,8 @@ fn netRead(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Strea | ... | @@ -1518,7 +1460,8 @@ fn netRead(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Strea |
| 1518 | .udata = @intFromPtr(fiber), | 1460 | .udata = @intFromPtr(fiber), |
| 1519 | }, | 1461 | }, |
| 1520 | }; | 1462 | }; |
| 1521 | assert(0 == (posix.kevent(thread.kq_fd, &changes, &.{}, null) catch |err| { | 1463 | assert(0 == (kevent(thread.kq_fd, &changes, &.{}, null) catch |err| { |
| 1464 | // TODO handle EINTR for cancellation purposes | ||
| 1522 | @panic(@errorName(err)); // TODO | 1465 | @panic(@errorName(err)); // TODO |
| 1523 | })); | 1466 | })); |
| 1524 | } | 1467 | } |
| ... | @@ -1551,10 +1494,10 @@ fn netWrite(userdata: ?*anyopaque, dest: net.Socket.Handle, header: []const u8, | ... | @@ -1551,10 +1494,10 @@ fn netWrite(userdata: ?*anyopaque, dest: net.Socket.Handle, header: []const u8, |
| 1551 | @panic("TODO"); | 1494 | @panic("TODO"); |
| 1552 | } | 1495 | } |
| 1553 | 1496 | ||
| 1554 | fn netClose(userdata: ?*anyopaque, handle: net.Socket.Handle) void { | 1497 | fn netClose(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void { |
| 1555 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1498 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1556 | _ = k; | 1499 | _ = k; |
| 1557 | _ = handle; | 1500 | _ = handles; |
| 1558 | @panic("TODO"); | 1501 | @panic("TODO"); |
| 1559 | } | 1502 | } |
| 1560 | 1503 | ||
| ... | @@ -1586,13 +1529,13 @@ fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interfa | ... | @@ -1586,13 +1529,13 @@ fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interfa |
| 1586 | fn netLookup( | 1529 | fn netLookup( |
| 1587 | userdata: ?*anyopaque, | 1530 | userdata: ?*anyopaque, |
| 1588 | host_name: net.HostName, | 1531 | host_name: net.HostName, |
| 1589 | result: *Io.Queue(net.HostName.LookupResult), | 1532 | resolved: *Io.Queue(net.HostName.LookupResult), |
| 1590 | options: net.HostName.LookupOptions, | 1533 | options: net.HostName.LookupOptions, |
| 1591 | ) void { | 1534 | ) net.HostName.LookupError!void { |
| 1592 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); | 1535 | const k: *Kqueue = @ptrCast(@alignCast(userdata)); |
| 1593 | _ = k; | 1536 | _ = k; |
| 1594 | _ = host_name; | 1537 | _ = host_name; |
| 1595 | _ = result; | 1538 | _ = resolved; |
| 1596 | _ = options; | 1539 | _ = options; |
| 1597 | @panic("TODO"); | 1540 | @panic("TODO"); |
| 1598 | } | 1541 | } |
| ... | @@ -1747,10 +1690,46 @@ fn checkCancel(k: *Kqueue) error{Canceled}!void { | ... | @@ -1747,10 +1690,46 @@ fn checkCancel(k: *Kqueue) error{Canceled}!void { |
| 1747 | if (cancelRequested(k)) return error.Canceled; | 1690 | if (cancelRequested(k)) return error.Canceled; |
| 1748 | } | 1691 | } |
| 1749 | 1692 | ||
| 1750 | const Condition = struct { | 1693 | pub const KEventError = error{ |
| 1751 | tail: *Fiber, | 1694 | /// The process does not have permission to register a filter. |
| 1752 | event: union(enum) { | 1695 | AccessDenied, |
| 1753 | queued, | 1696 | /// The event could not be found to be modified or deleted. |
| 1754 | wake: Io.Condition.Wake, | 1697 | EventNotFound, |
| 1755 | }, | 1698 | /// No memory was available to register the event. |
| 1699 | SystemResources, | ||
| 1700 | /// The specified process to attach to does not exist. | ||
| 1701 | ProcessNotFound, | ||
| 1702 | /// changelist or eventlist had too many items on it. | ||
| 1703 | /// TODO remove this possibility | ||
| 1704 | Overflow, | ||
| 1756 | }; | 1705 | }; |
| 1706 | |||
| 1707 | pub fn kevent( | ||
| 1708 | kq: i32, | ||
| 1709 | changelist: []const posix.Kevent, | ||
| 1710 | eventlist: []posix.Kevent, | ||
| 1711 | timeout: ?*const posix.timespec, | ||
| 1712 | ) KEventError!usize { | ||
| 1713 | while (true) { | ||
| 1714 | const rc = posix.system.kevent( | ||
| 1715 | kq, | ||
| 1716 | changelist.ptr, | ||
| 1717 | std.math.cast(c_int, changelist.len) orelse return error.Overflow, | ||
| 1718 | eventlist.ptr, | ||
| 1719 | std.math.cast(c_int, eventlist.len) orelse return error.Overflow, | ||
| 1720 | timeout, | ||
| 1721 | ); | ||
| 1722 | switch (posix.errno(rc)) { | ||
| 1723 | .SUCCESS => return @intCast(rc), | ||
| 1724 | .ACCES => return error.AccessDenied, | ||
| 1725 | .FAULT => unreachable, // TODO use error.Unexpected for these | ||
| 1726 | .BADF => unreachable, // Always a race condition. | ||
| 1727 | .INTR => continue, // TODO handle cancelation | ||
| 1728 | .INVAL => unreachable, | ||
| 1729 | .NOENT => return error.EventNotFound, | ||
| 1730 | .NOMEM => return error.SystemResources, | ||
| 1731 | .SRCH => return error.ProcessNotFound, | ||
| 1732 | else => unreachable, | ||
| 1733 | } | ||
| 1734 | } | ||
| 1735 | } |
lib/std/Io/net/test.zig+2-2| ... | @@ -310,11 +310,11 @@ test "listen on a unix socket, send bytes, receive bytes" { | ... | @@ -310,11 +310,11 @@ test "listen on a unix socket, send bytes, receive bytes" { |
| 310 | 310 | ||
| 311 | fn generateFileName(io: Io, base_name: []const u8) ![]const u8 { | 311 | fn generateFileName(io: Io, base_name: []const u8) ![]const u8 { |
| 312 | const random_bytes_count = 12; | 312 | const random_bytes_count = 12; |
| 313 | const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count); | 313 | const sub_path_len = comptime std.base64.url_safe.Encoder.calcSize(random_bytes_count); |
| 314 | var random_bytes: [12]u8 = undefined; | 314 | var random_bytes: [12]u8 = undefined; |
| 315 | io.random(&random_bytes); | 315 | io.random(&random_bytes); |
| 316 | var sub_path: [sub_path_len]u8 = undefined; | 316 | var sub_path: [sub_path_len]u8 = undefined; |
| 317 | _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); | 317 | _ = std.base64.url_safe.Encoder.encode(&sub_path, &random_bytes); |
| 318 | return std.fmt.allocPrint(testing.allocator, "{s}-{s}", .{ sub_path[0..], base_name }); | 318 | return std.fmt.allocPrint(testing.allocator, "{s}-{s}", .{ sub_path[0..], base_name }); |
| 319 | } | 319 | } |
| 320 | 320 |
lib/std/Thread.zig+1-1| ... | @@ -175,7 +175,7 @@ pub const SetNameError = error{ | ... | @@ -175,7 +175,7 @@ pub const SetNameError = error{ |
| 175 | Unsupported, | 175 | Unsupported, |
| 176 | Unexpected, | 176 | Unexpected, |
| 177 | InvalidWtf8, | 177 | InvalidWtf8, |
| 178 | } || posix.PrctlError || posix.WriteError || Io.File.OpenError || std.fmt.BufPrintError; | 178 | } || posix.PrctlError || Io.File.Writer.Error || Io.File.OpenError || std.fmt.BufPrintError; |
| 179 | 179 | ||
| 180 | pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void { | 180 | pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void { |
| 181 | if (name.len > max_name_len) return error.NameTooLong; | 181 | if (name.len > max_name_len) return error.NameTooLong; |
lib/std/dynamic_library.zig+15-23| ... | @@ -148,7 +148,7 @@ const ElfDynLibError = error{ | ... | @@ -148,7 +148,7 @@ const ElfDynLibError = error{ |
| 148 | ElfHashTableNotFound, | 148 | ElfHashTableNotFound, |
| 149 | Canceled, | 149 | Canceled, |
| 150 | Streaming, | 150 | Streaming, |
| 151 | } || posix.OpenError || posix.MMapError; | 151 | } || Io.File.OpenError || posix.MMapError; |
| 152 | 152 | ||
| 153 | pub const ElfDynLib = struct { | 153 | pub const ElfDynLib = struct { |
| 154 | strings: [*:0]u8, | 154 | strings: [*:0]u8, |
| ... | @@ -177,27 +177,20 @@ pub const ElfDynLib = struct { | ... | @@ -177,27 +177,20 @@ pub const ElfDynLib = struct { |
| 177 | return parent; | 177 | return parent; |
| 178 | } | 178 | } |
| 179 | 179 | ||
| 180 | fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t { | 180 | fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?Io.File { |
| 181 | var paths = std.mem.tokenizeScalar(u8, search_path, delim); | 181 | var paths = std.mem.tokenizeScalar(u8, search_path, delim); |
| 182 | while (paths.next()) |p| { | 182 | while (paths.next()) |p| { |
| 183 | var dir = openPath(io, p) catch continue; | 183 | var dir = openPath(io, p) catch continue; |
| 184 | defer dir.close(io); | 184 | defer dir.close(io); |
| 185 | const fd = posix.openat(dir.handle, file_name, .{ | 185 | return dir.openFile(io, file_name, .{}) catch continue; |
| 186 | .ACCMODE = .RDONLY, | ||
| 187 | .CLOEXEC = true, | ||
| 188 | }, 0) catch continue; | ||
| 189 | return fd; | ||
| 190 | } | 186 | } |
| 191 | return null; | 187 | return null; |
| 192 | } | 188 | } |
| 193 | 189 | ||
| 194 | fn resolveFromParent(io: Io, dir_path: []const u8, file_name: []const u8) ?posix.fd_t { | 190 | fn resolveFromParent(io: Io, dir_path: []const u8, file_name: []const u8) ?Io.File { |
| 195 | var dir = Io.Dir.cwd().openDir(io, dir_path, .{}) catch return null; | 191 | var dir = Io.Dir.cwd().openDir(io, dir_path, .{}) catch return null; |
| 196 | defer dir.close(io); | 192 | defer dir.close(io); |
| 197 | return posix.openat(dir.handle, file_name, .{ | 193 | return dir.openFile(io, file_name, .{}) catch null; |
| 198 | .ACCMODE = .RDONLY, | ||
| 199 | .CLOEXEC = true, | ||
| 200 | }, 0) catch null; | ||
| 201 | } | 194 | } |
| 202 | 195 | ||
| 203 | // This implements enough to be able to load system libraries in general | 196 | // This implements enough to be able to load system libraries in general |
| ... | @@ -205,10 +198,10 @@ pub const ElfDynLib = struct { | ... | @@ -205,10 +198,10 @@ pub const ElfDynLib = struct { |
| 205 | // - DT_RPATH of the calling binary is not used as a search path | 198 | // - DT_RPATH of the calling binary is not used as a search path |
| 206 | // - DT_RUNPATH of the calling binary is not used as a search path | 199 | // - DT_RUNPATH of the calling binary is not used as a search path |
| 207 | // - /etc/ld.so.cache is not read | 200 | // - /etc/ld.so.cache is not read |
| 208 | fn resolveFromName(io: Io, path_or_name: []const u8, LD_LIBRARY_PATH: ?[]const u8) !posix.fd_t { | 201 | fn resolveFromName(io: Io, path_or_name: []const u8, LD_LIBRARY_PATH: ?[]const u8) !Io.File { |
| 209 | // If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname | 202 | // If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname |
| 210 | if (std.mem.findScalarPos(u8, path_or_name, 0, '/')) |_| { | 203 | if (std.mem.findScalarPos(u8, path_or_name, 0, '/')) |_| { |
| 211 | return posix.open(path_or_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | 204 | return Io.Dir.cwd().openFile(io, path_or_name, .{}); |
| 212 | } | 205 | } |
| 213 | 206 | ||
| 214 | // Only read LD_LIBRARY_PATH if the binary is not setuid/setgid | 207 | // Only read LD_LIBRARY_PATH if the binary is not setuid/setgid |
| ... | @@ -216,15 +209,15 @@ pub const ElfDynLib = struct { | ... | @@ -216,15 +209,15 @@ pub const ElfDynLib = struct { |
| 216 | std.os.linux.getegid() == std.os.linux.getgid()) | 209 | std.os.linux.getegid() == std.os.linux.getgid()) |
| 217 | { | 210 | { |
| 218 | if (LD_LIBRARY_PATH) |ld_library_path| { | 211 | if (LD_LIBRARY_PATH) |ld_library_path| { |
| 219 | if (resolveFromSearchPath(io, ld_library_path, path_or_name, ':')) |fd| { | 212 | if (resolveFromSearchPath(io, ld_library_path, path_or_name, ':')) |file| { |
| 220 | return fd; | 213 | return file; |
| 221 | } | 214 | } |
| 222 | } | 215 | } |
| 223 | } | 216 | } |
| 224 | 217 | ||
| 225 | // Lastly the directories /lib and /usr/lib are searched (in this exact order) | 218 | // Lastly the directories /lib and /usr/lib are searched (in this exact order) |
| 226 | if (resolveFromParent(io, "/lib", path_or_name)) |fd| return fd; | 219 | if (resolveFromParent(io, "/lib", path_or_name)) |file| return file; |
| 227 | if (resolveFromParent(io, "/usr/lib", path_or_name)) |fd| return fd; | 220 | if (resolveFromParent(io, "/usr/lib", path_or_name)) |file| return file; |
| 228 | return error.FileNotFound; | 221 | return error.FileNotFound; |
| 229 | } | 222 | } |
| 230 | 223 | ||
| ... | @@ -232,10 +225,9 @@ pub const ElfDynLib = struct { | ... | @@ -232,10 +225,9 @@ pub const ElfDynLib = struct { |
| 232 | pub fn open(path: []const u8, LD_LIBRARY_PATH: ?[]const u8) Error!ElfDynLib { | 225 | pub fn open(path: []const u8, LD_LIBRARY_PATH: ?[]const u8) Error!ElfDynLib { |
| 233 | const io = std.Options.debug_io; | 226 | const io = std.Options.debug_io; |
| 234 | 227 | ||
| 235 | const fd = try resolveFromName(io, path, LD_LIBRARY_PATH); | 228 | const file = try resolveFromName(io, path, LD_LIBRARY_PATH); |
| 236 | defer posix.close(fd); | 229 | defer file.close(io); |
| 237 | 230 | ||
| 238 | const file: Io.File = .{ .handle = fd }; | ||
| 239 | const stat = try file.stat(io); | 231 | const stat = try file.stat(io); |
| 240 | const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig; | 232 | const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig; |
| 241 | 233 | ||
| ... | @@ -248,7 +240,7 @@ pub const ElfDynLib = struct { | ... | @@ -248,7 +240,7 @@ pub const ElfDynLib = struct { |
| 248 | mem.alignForward(usize, size, page_size), | 240 | mem.alignForward(usize, size, page_size), |
| 249 | posix.PROT.READ, | 241 | posix.PROT.READ, |
| 250 | .{ .TYPE = .PRIVATE }, | 242 | .{ .TYPE = .PRIVATE }, |
| 251 | fd, | 243 | file.handle, |
| 252 | 0, | 244 | 0, |
| 253 | ); | 245 | ); |
| 254 | defer posix.munmap(file_bytes); | 246 | defer posix.munmap(file_bytes); |
| ... | @@ -318,7 +310,7 @@ pub const ElfDynLib = struct { | ... | @@ -318,7 +310,7 @@ pub const ElfDynLib = struct { |
| 318 | extended_memsz, | 310 | extended_memsz, |
| 319 | prot, | 311 | prot, |
| 320 | .{ .TYPE = .PRIVATE, .FIXED = true }, | 312 | .{ .TYPE = .PRIVATE, .FIXED = true }, |
| 321 | fd, | 313 | file.handle, |
| 322 | ph.p_offset - extra_bytes, | 314 | ph.p_offset - extra_bytes, |
| 323 | ); | 315 | ); |
| 324 | } else { | 316 | } else { |
lib/std/fs.zig+6-9| ... | @@ -4,15 +4,12 @@ const std = @import("std.zig"); | ... | @@ -4,15 +4,12 @@ const std = @import("std.zig"); |
| 4 | 4 | ||
| 5 | /// Deprecated, use `std.Io.Dir.path`. | 5 | /// Deprecated, use `std.Io.Dir.path`. |
| 6 | pub const path = @import("fs/path.zig"); | 6 | pub const path = @import("fs/path.zig"); |
| 7 | 7 | /// Deprecated, use `std.base64.url_safe_alphabet_chars`. | |
| 8 | pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*; | 8 | pub const base64_alphabet = std.base64.url_safe_alphabet_chars; |
| 9 | 9 | /// Deprecated, use `std.base64.url_safe.Encoder`. | |
| 10 | /// Base64 encoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem. | 10 | pub const base64_encoder = std.base64.url_safe.Encoder; |
| 11 | pub const base64_encoder = std.base64.Base64Encoder.init(base64_alphabet, null); | 11 | /// Deprecated, use `std.base64.url_safe.Decoder`. |
| 12 | 12 | pub const base64_decoder = std.base64.url_safe.Decoder; | |
| 13 | /// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem. | ||
| 14 | pub const base64_decoder = std.base64.Base64Decoder.init(base64_alphabet, null); | ||
| 15 | |||
| 16 | /// Deprecated, use `std.Io.Dir.max_path_bytes`. | 13 | /// Deprecated, use `std.Io.Dir.max_path_bytes`. |
| 17 | pub const max_path_bytes = std.Io.Dir.max_path_bytes; | 14 | pub const max_path_bytes = std.Io.Dir.max_path_bytes; |
| 18 | /// Deprecated, use `std.Io.Dir.max_name_bytes`. | 15 | /// Deprecated, use `std.Io.Dir.max_name_bytes`. |
lib/std/fs/test.zig+2-2| ... | @@ -1777,8 +1777,8 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { | ... | @@ -1777,8 +1777,8 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { |
| 1777 | var random_bytes: [12]u8 = undefined; | 1777 | var random_bytes: [12]u8 = undefined; |
| 1778 | io.random(&random_bytes); | 1778 | io.random(&random_bytes); |
| 1779 | 1779 | ||
| 1780 | var random_b64: [std.fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined; | 1780 | var random_b64: [std.base64.url_safe.Encoder.calcSize(random_bytes.len)]u8 = undefined; |
| 1781 | _ = std.fs.base64_encoder.encode(&random_b64, &random_bytes); | 1781 | _ = std.base64.url_safe.Encoder.encode(&random_b64, &random_bytes); |
| 1782 | 1782 | ||
| 1783 | const sub_path = random_b64 ++ "-zig-test-absolute-paths.txt"; | 1783 | const sub_path = random_b64 ++ "-zig-test-absolute-paths.txt"; |
| 1784 | 1784 |
lib/std/os/linux/IoUring/test.zig+82-39| ... | @@ -529,17 +529,17 @@ test "sendmsg/recvmsg" { | ... | @@ -529,17 +529,17 @@ test "sendmsg/recvmsg" { |
| 529 | .addr = @bitCast([4]u8{ 127, 0, 0, 1 }), | 529 | .addr = @bitCast([4]u8{ 127, 0, 0, 1 }), |
| 530 | }; | 530 | }; |
| 531 | 531 | ||
| 532 | const server = try posix.socket(address_server.family, posix.SOCK.DGRAM, 0); | 532 | const server = try socket(address_server.family, posix.SOCK.DGRAM, 0); |
| 533 | defer posix.close(server); | 533 | defer posix.close(server); |
| 534 | try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1))); | 534 | try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1))); |
| 535 | try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); | 535 | try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); |
| 536 | try posix.bind(server, addrAny(&address_server), @sizeOf(linux.sockaddr.in)); | 536 | try bind(server, addrAny(&address_server), @sizeOf(linux.sockaddr.in)); |
| 537 | 537 | ||
| 538 | // set address_server to the OS-chosen IP/port. | 538 | // set address_server to the OS-chosen IP/port. |
| 539 | var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in); | 539 | var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in); |
| 540 | try posix.getsockname(server, addrAny(&address_server), &slen); | 540 | try getsockname(server, addrAny(&address_server), &slen); |
| 541 | 541 | ||
| 542 | const client = try posix.socket(address_server.family, posix.SOCK.DGRAM, 0); | 542 | const client = try socket(address_server.family, posix.SOCK.DGRAM, 0); |
| 543 | defer posix.close(client); | 543 | defer posix.close(client); |
| 544 | 544 | ||
| 545 | const buffer_send = [_]u8{42} ** 128; | 545 | const buffer_send = [_]u8{42} ** 128; |
| ... | @@ -932,6 +932,8 @@ test "accept/connect/recv/cancel" { | ... | @@ -932,6 +932,8 @@ test "accept/connect/recv/cancel" { |
| 932 | } | 932 | } |
| 933 | 933 | ||
| 934 | test "register_files_update" { | 934 | test "register_files_update" { |
| 935 | const io = testing.io; | ||
| 936 | |||
| 935 | var ring = IoUring.init(1, 0) catch |err| switch (err) { | 937 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 936 | error.SystemOutdated => return error.SkipZigTest, | 938 | error.SystemOutdated => return error.SkipZigTest, |
| 937 | error.PermissionDenied => return error.SkipZigTest, | 939 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -939,13 +941,13 @@ test "register_files_update" { | ... | @@ -939,13 +941,13 @@ test "register_files_update" { |
| 939 | }; | 941 | }; |
| 940 | defer ring.deinit(); | 942 | defer ring.deinit(); |
| 941 | 943 | ||
| 942 | const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | 944 | const file = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{}); |
| 943 | defer posix.close(fd); | 945 | defer file.close(io); |
| 944 | 946 | ||
| 945 | var registered_fds = [_]linux.fd_t{0} ** 2; | 947 | var registered_fds = [_]linux.fd_t{0} ** 2; |
| 946 | const fd_index = 0; | 948 | const fd_index = 0; |
| 947 | const fd_index2 = 1; | 949 | const fd_index2 = 1; |
| 948 | registered_fds[fd_index] = fd; | 950 | registered_fds[fd_index] = file.handle; |
| 949 | registered_fds[fd_index2] = -1; | 951 | registered_fds[fd_index2] = -1; |
| 950 | 952 | ||
| 951 | ring.register_files(registered_fds[0..]) catch |err| switch (err) { | 953 | ring.register_files(registered_fds[0..]) catch |err| switch (err) { |
| ... | @@ -957,10 +959,10 @@ test "register_files_update" { | ... | @@ -957,10 +959,10 @@ test "register_files_update" { |
| 957 | // Test IORING_REGISTER_FILES_UPDATE | 959 | // Test IORING_REGISTER_FILES_UPDATE |
| 958 | // Only available since Linux 5.5 | 960 | // Only available since Linux 5.5 |
| 959 | 961 | ||
| 960 | const fd2 = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | 962 | const file2 = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{}); |
| 961 | defer posix.close(fd2); | 963 | defer file2.close(io); |
| 962 | 964 | ||
| 963 | registered_fds[fd_index] = fd2; | 965 | registered_fds[fd_index] = file2.handle; |
| 964 | registered_fds[fd_index2] = -1; | 966 | registered_fds[fd_index2] = -1; |
| 965 | try ring.register_files_update(0, registered_fds[0..]); | 967 | try ring.register_files_update(0, registered_fds[0..]); |
| 966 | 968 | ||
| ... | @@ -1031,15 +1033,15 @@ test "shutdown" { | ... | @@ -1031,15 +1033,15 @@ test "shutdown" { |
| 1031 | 1033 | ||
| 1032 | // Socket bound, expect shutdown to work | 1034 | // Socket bound, expect shutdown to work |
| 1033 | { | 1035 | { |
| 1034 | const server = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | 1036 | const server = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); |
| 1035 | defer posix.close(server); | 1037 | defer posix.close(server); |
| 1036 | try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); | 1038 | try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); |
| 1037 | try posix.bind(server, addrAny(&address), @sizeOf(linux.sockaddr.in)); | 1039 | try bind(server, addrAny(&address), @sizeOf(linux.sockaddr.in)); |
| 1038 | try posix.listen(server, 1); | 1040 | try listen(server, 1); |
| 1039 | 1041 | ||
| 1040 | // set address to the OS-chosen IP/port. | 1042 | // set address to the OS-chosen IP/port. |
| 1041 | var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in); | 1043 | var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in); |
| 1042 | try posix.getsockname(server, addrAny(&address), &slen); | 1044 | try getsockname(server, addrAny(&address), &slen); |
| 1043 | 1045 | ||
| 1044 | const shutdown_sqe = try ring.shutdown(0x445445445, server, linux.SHUT.RD); | 1046 | const shutdown_sqe = try ring.shutdown(0x445445445, server, linux.SHUT.RD); |
| 1045 | try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode); | 1047 | try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode); |
| ... | @@ -1064,7 +1066,7 @@ test "shutdown" { | ... | @@ -1064,7 +1066,7 @@ test "shutdown" { |
| 1064 | 1066 | ||
| 1065 | // Socket not bound, expect to fail with ENOTCONN | 1067 | // Socket not bound, expect to fail with ENOTCONN |
| 1066 | { | 1068 | { |
| 1067 | const server = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | 1069 | const server = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); |
| 1068 | defer posix.close(server); | 1070 | defer posix.close(server); |
| 1069 | 1071 | ||
| 1070 | const shutdown_sqe = ring.shutdown(0x445445445, server, linux.SHUT.RD) catch |err| switch (err) { | 1072 | const shutdown_sqe = ring.shutdown(0x445445445, server, linux.SHUT.RD) catch |err| switch (err) { |
| ... | @@ -1339,6 +1341,8 @@ test "linkat" { | ... | @@ -1339,6 +1341,8 @@ test "linkat" { |
| 1339 | } | 1341 | } |
| 1340 | 1342 | ||
| 1341 | test "provide_buffers: read" { | 1343 | test "provide_buffers: read" { |
| 1344 | const io = testing.io; | ||
| 1345 | |||
| 1342 | var ring = IoUring.init(1, 0) catch |err| switch (err) { | 1346 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 1343 | error.SystemOutdated => return error.SkipZigTest, | 1347 | error.SystemOutdated => return error.SkipZigTest, |
| 1344 | error.PermissionDenied => return error.SkipZigTest, | 1348 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -1346,8 +1350,8 @@ test "provide_buffers: read" { | ... | @@ -1346,8 +1350,8 @@ test "provide_buffers: read" { |
| 1346 | }; | 1350 | }; |
| 1347 | defer ring.deinit(); | 1351 | defer ring.deinit(); |
| 1348 | 1352 | ||
| 1349 | const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | 1353 | const file = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{}); |
| 1350 | defer posix.close(fd); | 1354 | defer file.close(io); |
| 1351 | 1355 | ||
| 1352 | const group_id = 1337; | 1356 | const group_id = 1337; |
| 1353 | const buffer_id = 0; | 1357 | const buffer_id = 0; |
| ... | @@ -1380,9 +1384,9 @@ test "provide_buffers: read" { | ... | @@ -1380,9 +1384,9 @@ test "provide_buffers: read" { |
| 1380 | 1384 | ||
| 1381 | var i: usize = 0; | 1385 | var i: usize = 0; |
| 1382 | while (i < buffers.len) : (i += 1) { | 1386 | while (i < buffers.len) : (i += 1) { |
| 1383 | const sqe = try ring.read(0xdededede, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); | 1387 | const sqe = try ring.read(0xdededede, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); |
| 1384 | try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); | 1388 | try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); |
| 1385 | try testing.expectEqual(@as(i32, fd), sqe.fd); | 1389 | try testing.expectEqual(@as(i32, file.handle), sqe.fd); |
| 1386 | try testing.expectEqual(@as(u64, 0), sqe.addr); | 1390 | try testing.expectEqual(@as(u64, 0), sqe.addr); |
| 1387 | try testing.expectEqual(@as(u32, buffer_len), sqe.len); | 1391 | try testing.expectEqual(@as(u32, buffer_len), sqe.len); |
| 1388 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); | 1392 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); |
| ... | @@ -1406,9 +1410,9 @@ test "provide_buffers: read" { | ... | @@ -1406,9 +1410,9 @@ test "provide_buffers: read" { |
| 1406 | // This read should fail | 1410 | // This read should fail |
| 1407 | 1411 | ||
| 1408 | { | 1412 | { |
| 1409 | const sqe = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); | 1413 | const sqe = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); |
| 1410 | try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); | 1414 | try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); |
| 1411 | try testing.expectEqual(@as(i32, fd), sqe.fd); | 1415 | try testing.expectEqual(@as(i32, file.handle), sqe.fd); |
| 1412 | try testing.expectEqual(@as(u64, 0), sqe.addr); | 1416 | try testing.expectEqual(@as(u64, 0), sqe.addr); |
| 1413 | try testing.expectEqual(@as(u32, buffer_len), sqe.len); | 1417 | try testing.expectEqual(@as(u32, buffer_len), sqe.len); |
| 1414 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); | 1418 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); |
| ... | @@ -1445,9 +1449,9 @@ test "provide_buffers: read" { | ... | @@ -1445,9 +1449,9 @@ test "provide_buffers: read" { |
| 1445 | // Final read which should work | 1449 | // Final read which should work |
| 1446 | 1450 | ||
| 1447 | { | 1451 | { |
| 1448 | const sqe = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); | 1452 | const sqe = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); |
| 1449 | try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); | 1453 | try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode); |
| 1450 | try testing.expectEqual(@as(i32, fd), sqe.fd); | 1454 | try testing.expectEqual(@as(i32, file.handle), sqe.fd); |
| 1451 | try testing.expectEqual(@as(u64, 0), sqe.addr); | 1455 | try testing.expectEqual(@as(u64, 0), sqe.addr); |
| 1452 | try testing.expectEqual(@as(u32, buffer_len), sqe.len); | 1456 | try testing.expectEqual(@as(u32, buffer_len), sqe.len); |
| 1453 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); | 1457 | try testing.expectEqual(@as(u16, group_id), sqe.buf_index); |
| ... | @@ -1469,6 +1473,8 @@ test "provide_buffers: read" { | ... | @@ -1469,6 +1473,8 @@ test "provide_buffers: read" { |
| 1469 | } | 1473 | } |
| 1470 | 1474 | ||
| 1471 | test "remove_buffers" { | 1475 | test "remove_buffers" { |
| 1476 | const io = testing.io; | ||
| 1477 | |||
| 1472 | var ring = IoUring.init(1, 0) catch |err| switch (err) { | 1478 | var ring = IoUring.init(1, 0) catch |err| switch (err) { |
| 1473 | error.SystemOutdated => return error.SkipZigTest, | 1479 | error.SystemOutdated => return error.SkipZigTest, |
| 1474 | error.PermissionDenied => return error.SkipZigTest, | 1480 | error.PermissionDenied => return error.SkipZigTest, |
| ... | @@ -1476,8 +1482,8 @@ test "remove_buffers" { | ... | @@ -1476,8 +1482,8 @@ test "remove_buffers" { |
| 1476 | }; | 1482 | }; |
| 1477 | defer ring.deinit(); | 1483 | defer ring.deinit(); |
| 1478 | 1484 | ||
| 1479 | const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); | 1485 | const file = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{}); |
| 1480 | defer posix.close(fd); | 1486 | defer file.close(io); |
| 1481 | 1487 | ||
| 1482 | const group_id = 1337; | 1488 | const group_id = 1337; |
| 1483 | const buffer_id = 0; | 1489 | const buffer_id = 0; |
| ... | @@ -1522,7 +1528,7 @@ test "remove_buffers" { | ... | @@ -1522,7 +1528,7 @@ test "remove_buffers" { |
| 1522 | // This read should work | 1528 | // This read should work |
| 1523 | 1529 | ||
| 1524 | { | 1530 | { |
| 1525 | _ = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); | 1531 | _ = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); |
| 1526 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 1532 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 1527 | 1533 | ||
| 1528 | const cqe = try ring.copy_cqe(); | 1534 | const cqe = try ring.copy_cqe(); |
| ... | @@ -1542,7 +1548,7 @@ test "remove_buffers" { | ... | @@ -1542,7 +1548,7 @@ test "remove_buffers" { |
| 1542 | // Final read should _not_ work | 1548 | // Final read should _not_ work |
| 1543 | 1549 | ||
| 1544 | { | 1550 | { |
| 1545 | _ = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); | 1551 | _ = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0); |
| 1546 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 1552 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 1547 | 1553 | ||
| 1548 | const cqe = try ring.copy_cqe(); | 1554 | const cqe = try ring.copy_cqe(); |
| ... | @@ -1747,7 +1753,7 @@ test "accept multishot" { | ... | @@ -1747,7 +1753,7 @@ test "accept multishot" { |
| 1747 | var nr: usize = 4; // number of clients to connect | 1753 | var nr: usize = 4; // number of clients to connect |
| 1748 | while (nr > 0) : (nr -= 1) { | 1754 | while (nr > 0) : (nr -= 1) { |
| 1749 | // connect client | 1755 | // connect client |
| 1750 | const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | 1756 | const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); |
| 1751 | errdefer posix.close(client); | 1757 | errdefer posix.close(client); |
| 1752 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); | 1758 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); |
| 1753 | 1759 | ||
| ... | @@ -1856,7 +1862,7 @@ test "accept_direct" { | ... | @@ -1856,7 +1862,7 @@ test "accept_direct" { |
| 1856 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 1862 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 1857 | 1863 | ||
| 1858 | // connect | 1864 | // connect |
| 1859 | const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | 1865 | const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); |
| 1860 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); | 1866 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); |
| 1861 | defer posix.close(client); | 1867 | defer posix.close(client); |
| 1862 | 1868 | ||
| ... | @@ -1868,7 +1874,7 @@ test "accept_direct" { | ... | @@ -1868,7 +1874,7 @@ test "accept_direct" { |
| 1868 | try testing.expect(cqe_accept.user_data == accept_userdata); | 1874 | try testing.expect(cqe_accept.user_data == accept_userdata); |
| 1869 | 1875 | ||
| 1870 | // send data | 1876 | // send data |
| 1871 | _ = try posix.send(client, buffer_send, 0); | 1877 | _ = try send(client, buffer_send, 0); |
| 1872 | 1878 | ||
| 1873 | // Example of how to use registered fd: | 1879 | // Example of how to use registered fd: |
| 1874 | // Submit receive to fixed file returned by accept (fd_index). | 1880 | // Submit receive to fixed file returned by accept (fd_index). |
| ... | @@ -1890,7 +1896,7 @@ test "accept_direct" { | ... | @@ -1890,7 +1896,7 @@ test "accept_direct" { |
| 1890 | _ = try ring.accept_direct(accept_userdata, listener_socket, null, null, 0); | 1896 | _ = try ring.accept_direct(accept_userdata, listener_socket, null, null, 0); |
| 1891 | try testing.expectEqual(@as(u32, 1), try ring.submit()); | 1897 | try testing.expectEqual(@as(u32, 1), try ring.submit()); |
| 1892 | // connect | 1898 | // connect |
| 1893 | const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | 1899 | const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); |
| 1894 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); | 1900 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); |
| 1895 | defer posix.close(client); | 1901 | defer posix.close(client); |
| 1896 | // completion with error | 1902 | // completion with error |
| ... | @@ -1940,7 +1946,7 @@ test "accept_multishot_direct" { | ... | @@ -1940,7 +1946,7 @@ test "accept_multishot_direct" { |
| 1940 | 1946 | ||
| 1941 | for (registered_fds) |_| { | 1947 | for (registered_fds) |_| { |
| 1942 | // connect | 1948 | // connect |
| 1943 | const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | 1949 | const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); |
| 1944 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); | 1950 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); |
| 1945 | defer posix.close(client); | 1951 | defer posix.close(client); |
| 1946 | 1952 | ||
| ... | @@ -1955,7 +1961,7 @@ test "accept_multishot_direct" { | ... | @@ -1955,7 +1961,7 @@ test "accept_multishot_direct" { |
| 1955 | // Multishot is terminated (more flag is not set). | 1961 | // Multishot is terminated (more flag is not set). |
| 1956 | { | 1962 | { |
| 1957 | // connect | 1963 | // connect |
| 1958 | const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | 1964 | const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); |
| 1959 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); | 1965 | try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in)); |
| 1960 | defer posix.close(client); | 1966 | defer posix.close(client); |
| 1961 | // completion with error | 1967 | // completion with error |
| ... | @@ -2456,7 +2462,7 @@ test "bind/listen/connect" { | ... | @@ -2456,7 +2462,7 @@ test "bind/listen/connect" { |
| 2456 | 2462 | ||
| 2457 | // Read system assigned port into addr | 2463 | // Read system assigned port into addr |
| 2458 | var addr_len: posix.socklen_t = @sizeOf(linux.sockaddr.in); | 2464 | var addr_len: posix.socklen_t = @sizeOf(linux.sockaddr.in); |
| 2459 | try posix.getsockname(listen_fd, addrAny(&addr), &addr_len); | 2465 | try getsockname(listen_fd, addrAny(&addr), &addr_len); |
| 2460 | 2466 | ||
| 2461 | break :brk listen_fd; | 2467 | break :brk listen_fd; |
| 2462 | }; | 2468 | }; |
| ... | @@ -2611,7 +2617,7 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness { | ... | @@ -2611,7 +2617,7 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness { |
| 2611 | _ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0); | 2617 | _ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0); |
| 2612 | 2618 | ||
| 2613 | // Create a TCP client socket | 2619 | // Create a TCP client socket |
| 2614 | const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | 2620 | const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); |
| 2615 | errdefer posix.close(client); | 2621 | errdefer posix.close(client); |
| 2616 | _ = try ring.connect(0xcccccccc, client, addrAny(&address), @sizeOf(linux.sockaddr.in)); | 2622 | _ = try ring.connect(0xcccccccc, client, addrAny(&address), @sizeOf(linux.sockaddr.in)); |
| 2617 | 2623 | ||
| ... | @@ -2651,16 +2657,16 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness { | ... | @@ -2651,16 +2657,16 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness { |
| 2651 | 2657 | ||
| 2652 | fn createListenerSocket(address: *linux.sockaddr.in) !posix.socket_t { | 2658 | fn createListenerSocket(address: *linux.sockaddr.in) !posix.socket_t { |
| 2653 | const kernel_backlog = 1; | 2659 | const kernel_backlog = 1; |
| 2654 | const listener_socket = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); | 2660 | const listener_socket = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0); |
| 2655 | errdefer posix.close(listener_socket); | 2661 | errdefer posix.close(listener_socket); |
| 2656 | 2662 | ||
| 2657 | try posix.setsockopt(listener_socket, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); | 2663 | try posix.setsockopt(listener_socket, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1))); |
| 2658 | try posix.bind(listener_socket, addrAny(address), @sizeOf(linux.sockaddr.in)); | 2664 | try bind(listener_socket, addrAny(address), @sizeOf(linux.sockaddr.in)); |
| 2659 | try posix.listen(listener_socket, kernel_backlog); | 2665 | try listen(listener_socket, kernel_backlog); |
| 2660 | 2666 | ||
| 2661 | // set address to the OS-chosen IP/port. | 2667 | // set address to the OS-chosen IP/port. |
| 2662 | var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in); | 2668 | var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in); |
| 2663 | try posix.getsockname(listener_socket, addrAny(address), &slen); | 2669 | try getsockname(listener_socket, addrAny(address), &slen); |
| 2664 | 2670 | ||
| 2665 | return listener_socket; | 2671 | return listener_socket; |
| 2666 | } | 2672 | } |
| ... | @@ -2689,3 +2695,40 @@ inline fn skipKernelLessThan(required: std.SemanticVersion) !void { | ... | @@ -2689,3 +2695,40 @@ inline fn skipKernelLessThan(required: std.SemanticVersion) !void { |
| 2689 | fn addrAny(addr: *linux.sockaddr.in) *linux.sockaddr { | 2695 | fn addrAny(addr: *linux.sockaddr.in) *linux.sockaddr { |
| 2690 | return @ptrCast(addr); | 2696 | return @ptrCast(addr); |
| 2691 | } | 2697 | } |
| 2698 | |||
| 2699 | fn socket(domain: u32, socket_type: u32, protocol: u32) !posix.socket_t { | ||
| 2700 | const rc = posix.system.socket(domain, socket_type, protocol); | ||
| 2701 | switch (posix.errno(rc)) { | ||
| 2702 | .SUCCESS => return @intCast(rc), | ||
| 2703 | else => return error.SocketCreationFailure, | ||
| 2704 | } | ||
| 2705 | } | ||
| 2706 | |||
| 2707 | fn bind(sock: posix.socket_t, addr: *const posix.sockaddr, len: posix.socklen_t) !void { | ||
| 2708 | switch (posix.errno(posix.system.bind(sock, addr, len))) { | ||
| 2709 | .SUCCESS => return, | ||
| 2710 | else => return error.BindFailure, | ||
| 2711 | } | ||
| 2712 | } | ||
| 2713 | |||
| 2714 | fn listen(sock: posix.socket_t, backlog: u31) !void { | ||
| 2715 | switch (posix.errno(posix.system.listen(sock, backlog))) { | ||
| 2716 | .SUCCESS => return, | ||
| 2717 | else => return error.ListenFailure, | ||
| 2718 | } | ||
| 2719 | } | ||
| 2720 | |||
| 2721 | fn getsockname(sock: posix.socket_t, addr: *posix.sockaddr, addrlen: *posix.socklen_t) !void { | ||
| 2722 | switch (posix.errno(posix.system.getsockname(sock, addr, addrlen))) { | ||
| 2723 | .SUCCESS => return, | ||
| 2724 | else => return error.GetSockNameFailure, | ||
| 2725 | } | ||
| 2726 | } | ||
| 2727 | |||
| 2728 | fn send(sockfd: posix.socket_t, buf: []const u8, flags: u32) !usize { | ||
| 2729 | const rc = posix.system.sendto(sockfd, buf.ptr, buf.len, flags, null, 0); | ||
| 2730 | switch (posix.errno(rc)) { | ||
| 2731 | .SUCCESS => return @intCast(rc), | ||
| 2732 | else => return error.SendFailed, | ||
| 2733 | } | ||
| 2734 | } |
lib/std/posix.zig+1-1173| ... | @@ -446,165 +446,8 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { | ... | @@ -446,165 +446,8 @@ pub fn read(fd: fd_t, buf: []u8) ReadError!usize { |
| 446 | } | 446 | } |
| 447 | } | 447 | } |
| 448 | 448 | ||
| 449 | pub const WriteError = error{ | ||
| 450 | DiskQuota, | ||
| 451 | FileTooBig, | ||
| 452 | InputOutput, | ||
| 453 | NoSpaceLeft, | ||
| 454 | DeviceBusy, | ||
| 455 | InvalidArgument, | ||
| 456 | |||
| 457 | /// File descriptor does not hold the required rights to write to it. | ||
| 458 | AccessDenied, | ||
| 459 | PermissionDenied, | ||
| 460 | BrokenPipe, | ||
| 461 | SystemResources, | ||
| 462 | Canceled, | ||
| 463 | NotOpenForWriting, | ||
| 464 | |||
| 465 | /// The process cannot access the file because another process has locked | ||
| 466 | /// a portion of the file. Windows-only. | ||
| 467 | LockViolation, | ||
| 468 | |||
| 469 | /// This error occurs when no global event loop is configured, | ||
| 470 | /// and reading from the file descriptor would block. | ||
| 471 | WouldBlock, | ||
| 472 | |||
| 473 | /// Connection reset by peer. | ||
| 474 | ConnectionResetByPeer, | ||
| 475 | |||
| 476 | /// This error occurs in Linux if the process being written to | ||
| 477 | /// no longer exists. | ||
| 478 | ProcessNotFound, | ||
| 479 | /// This error occurs when a device gets disconnected before or mid-flush | ||
| 480 | /// while it's being written to - errno(6): No such device or address. | ||
| 481 | NoDevice, | ||
| 482 | |||
| 483 | /// The socket type requires that message be sent atomically, and the size of the message | ||
| 484 | /// to be sent made this impossible. The message is not transmitted. | ||
| 485 | MessageOversize, | ||
| 486 | } || UnexpectedError; | ||
| 487 | |||
| 488 | /// Write to a file descriptor. | ||
| 489 | /// Retries when interrupted by a signal. | ||
| 490 | /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero. | ||
| 491 | /// | ||
| 492 | /// Note that a successful write() may transfer fewer than count bytes. Such partial writes can | ||
| 493 | /// occur for various reasons; for example, because there was insufficient space on the disk | ||
| 494 | /// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or | ||
| 495 | /// similar was interrupted by a signal handler after it had transferred some, but before it had | ||
| 496 | /// transferred all of the requested bytes. In the event of a partial write, the caller can make | ||
| 497 | /// another write() call to transfer the remaining bytes. The subsequent call will either | ||
| 498 | /// transfer further bytes or may result in an error (e.g., if the disk is now full). | ||
| 499 | /// | ||
| 500 | /// For POSIX systems, if `fd` is opened in non blocking mode, the function will | ||
| 501 | /// return error.WouldBlock when EAGAIN is received. | ||
| 502 | /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are | ||
| 503 | /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. | ||
| 504 | /// | ||
| 505 | /// Linux has a limit on how many bytes may be transferred in one `write` call, which is `0x7ffff000` | ||
| 506 | /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as | ||
| 507 | /// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page. | ||
| 508 | /// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL. | ||
| 509 | /// The corresponding POSIX limit is `maxInt(isize)`. | ||
| 510 | pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { | ||
| 511 | if (bytes.len == 0) return 0; | ||
| 512 | if (native_os == .windows) @compileError("unsupported OS"); | ||
| 513 | if (native_os == .wasi) @compileError("unsupported OS"); | ||
| 514 | |||
| 515 | const max_count = switch (native_os) { | ||
| 516 | .linux => 0x7ffff000, | ||
| 517 | .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => maxInt(i32), | ||
| 518 | else => maxInt(isize), | ||
| 519 | }; | ||
| 520 | while (true) { | ||
| 521 | const rc = system.write(fd, bytes.ptr, @min(bytes.len, max_count)); | ||
| 522 | switch (errno(rc)) { | ||
| 523 | .SUCCESS => return @intCast(rc), | ||
| 524 | .INTR => continue, | ||
| 525 | .INVAL => return error.InvalidArgument, | ||
| 526 | .FAULT => unreachable, | ||
| 527 | .AGAIN => return error.WouldBlock, | ||
| 528 | .BADF => return error.NotOpenForWriting, // can be a race condition. | ||
| 529 | .DESTADDRREQ => unreachable, // `connect` was never called. | ||
| 530 | .DQUOT => return error.DiskQuota, | ||
| 531 | .FBIG => return error.FileTooBig, | ||
| 532 | .IO => return error.InputOutput, | ||
| 533 | .NOSPC => return error.NoSpaceLeft, | ||
| 534 | .ACCES => return error.AccessDenied, | ||
| 535 | .PERM => return error.PermissionDenied, | ||
| 536 | .PIPE => return error.BrokenPipe, | ||
| 537 | .CONNRESET => return error.ConnectionResetByPeer, | ||
| 538 | .BUSY => return error.DeviceBusy, | ||
| 539 | .NXIO => return error.NoDevice, | ||
| 540 | .MSGSIZE => return error.MessageOversize, | ||
| 541 | else => |err| return unexpectedErrno(err), | ||
| 542 | } | ||
| 543 | } | ||
| 544 | } | ||
| 545 | |||
| 546 | pub const OpenError = std.Io.File.OpenError || error{WouldBlock}; | 449 | pub const OpenError = std.Io.File.OpenError || error{WouldBlock}; |
| 547 | 450 | ||
| 548 | /// Open and possibly create a file. Keeps trying if it gets interrupted. | ||
| 549 | /// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 550 | /// On WASI, `file_path` should be encoded as valid UTF-8. | ||
| 551 | /// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding. | ||
| 552 | /// See also `openZ`. | ||
| 553 | pub fn open(file_path: []const u8, flags: O, perm: mode_t) OpenError!fd_t { | ||
| 554 | if (native_os == .windows) { | ||
| 555 | @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API"); | ||
| 556 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 557 | return openat(AT.FDCWD, file_path, flags, perm); | ||
| 558 | } | ||
| 559 | const file_path_c = try toPosixPath(file_path); | ||
| 560 | return openZ(&file_path_c, flags, perm); | ||
| 561 | } | ||
| 562 | |||
| 563 | /// Open and possibly create a file. Keeps trying if it gets interrupted. | ||
| 564 | /// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | ||
| 565 | /// On WASI, `file_path` should be encoded as valid UTF-8. | ||
| 566 | /// On other platforms, `file_path` is an opaque sequence of bytes with no particular encoding. | ||
| 567 | /// See also `open`. | ||
| 568 | pub fn openZ(file_path: [*:0]const u8, flags: O, perm: mode_t) OpenError!fd_t { | ||
| 569 | if (native_os == .windows) { | ||
| 570 | @compileError("Windows does not support POSIX; use Windows-specific API or cross-platform std.fs API"); | ||
| 571 | } else if (native_os == .wasi and !builtin.link_libc) { | ||
| 572 | return open(mem.sliceTo(file_path, 0), flags, perm); | ||
| 573 | } | ||
| 574 | |||
| 575 | const open_sym = if (lfs64_abi) system.open64 else system.open; | ||
| 576 | while (true) { | ||
| 577 | const rc = open_sym(file_path, flags, perm); | ||
| 578 | switch (errno(rc)) { | ||
| 579 | .SUCCESS => return @intCast(rc), | ||
| 580 | .INTR => continue, | ||
| 581 | |||
| 582 | .FAULT => unreachable, | ||
| 583 | .INVAL => return error.BadPathName, | ||
| 584 | .ACCES => return error.AccessDenied, | ||
| 585 | .FBIG => return error.FileTooBig, | ||
| 586 | .OVERFLOW => return error.FileTooBig, | ||
| 587 | .ISDIR => return error.IsDir, | ||
| 588 | .LOOP => return error.SymLinkLoop, | ||
| 589 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 590 | .NAMETOOLONG => return error.NameTooLong, | ||
| 591 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 592 | .NODEV => return error.NoDevice, | ||
| 593 | .NOENT => return error.FileNotFound, | ||
| 594 | // Can happen on Linux when opening procfs files. | ||
| 595 | .SRCH => return error.FileNotFound, | ||
| 596 | .NOMEM => return error.SystemResources, | ||
| 597 | .NOSPC => return error.NoSpaceLeft, | ||
| 598 | .NOTDIR => return error.NotDir, | ||
| 599 | .PERM => return error.PermissionDenied, | ||
| 600 | .EXIST => return error.PathAlreadyExists, | ||
| 601 | .BUSY => return error.DeviceBusy, | ||
| 602 | .ILSEQ => return error.BadPathName, | ||
| 603 | else => |err| return unexpectedErrno(err), | ||
| 604 | } | ||
| 605 | } | ||
| 606 | } | ||
| 607 | |||
| 608 | /// Open and possibly create a file. Keeps trying if it gets interrupted. | 451 | /// Open and possibly create a file. Keeps trying if it gets interrupted. |
| 609 | /// `file_path` is relative to the open directory handle `dir_fd`. | 452 | /// `file_path` is relative to the open directory handle `dir_fd`. |
| 610 | /// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). | 453 | /// On Windows, `file_path` should be encoded as [WTF-8](https://wtf-8.codeberg.page/). |
| ... | @@ -708,67 +551,6 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { | ... | @@ -708,67 +551,6 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { |
| 708 | } | 551 | } |
| 709 | } | 552 | } |
| 710 | 553 | ||
| 711 | pub const SetEidError = error{ | ||
| 712 | InvalidUserId, | ||
| 713 | PermissionDenied, | ||
| 714 | } || UnexpectedError; | ||
| 715 | |||
| 716 | pub const SetIdError = error{ResourceLimitReached} || SetEidError; | ||
| 717 | |||
| 718 | pub fn setuid(uid: uid_t) SetIdError!void { | ||
| 719 | switch (errno(system.setuid(uid))) { | ||
| 720 | .SUCCESS => return, | ||
| 721 | .AGAIN => return error.ResourceLimitReached, | ||
| 722 | .INVAL => return error.InvalidUserId, | ||
| 723 | .PERM => return error.PermissionDenied, | ||
| 724 | else => |err| return unexpectedErrno(err), | ||
| 725 | } | ||
| 726 | } | ||
| 727 | |||
| 728 | pub fn seteuid(uid: uid_t) SetEidError!void { | ||
| 729 | switch (errno(system.seteuid(uid))) { | ||
| 730 | .SUCCESS => return, | ||
| 731 | .INVAL => return error.InvalidUserId, | ||
| 732 | .PERM => return error.PermissionDenied, | ||
| 733 | else => |err| return unexpectedErrno(err), | ||
| 734 | } | ||
| 735 | } | ||
| 736 | |||
| 737 | pub fn setgid(gid: gid_t) SetIdError!void { | ||
| 738 | switch (errno(system.setgid(gid))) { | ||
| 739 | .SUCCESS => return, | ||
| 740 | .AGAIN => return error.ResourceLimitReached, | ||
| 741 | .INVAL => return error.InvalidUserId, | ||
| 742 | .PERM => return error.PermissionDenied, | ||
| 743 | else => |err| return unexpectedErrno(err), | ||
| 744 | } | ||
| 745 | } | ||
| 746 | |||
| 747 | pub fn setegid(uid: uid_t) SetEidError!void { | ||
| 748 | switch (errno(system.setegid(uid))) { | ||
| 749 | .SUCCESS => return, | ||
| 750 | .INVAL => return error.InvalidUserId, | ||
| 751 | .PERM => return error.PermissionDenied, | ||
| 752 | else => |err| return unexpectedErrno(err), | ||
| 753 | } | ||
| 754 | } | ||
| 755 | |||
| 756 | pub fn getuid() uid_t { | ||
| 757 | return system.getuid(); | ||
| 758 | } | ||
| 759 | |||
| 760 | pub fn geteuid() uid_t { | ||
| 761 | return system.geteuid(); | ||
| 762 | } | ||
| 763 | |||
| 764 | pub fn getgid() gid_t { | ||
| 765 | return system.getgid(); | ||
| 766 | } | ||
| 767 | |||
| 768 | pub fn getegid() gid_t { | ||
| 769 | return system.getegid(); | ||
| 770 | } | ||
| 771 | |||
| 772 | pub const SocketError = error{ | 554 | pub const SocketError = error{ |
| 773 | /// Permission to create a socket of the specified type and/or | 555 | /// Permission to create a socket of the specified type and/or |
| 774 | /// pro‐tocol is denied. | 556 | /// pro‐tocol is denied. |
| ... | @@ -797,35 +579,6 @@ pub const SocketError = error{ | ... | @@ -797,35 +579,6 @@ pub const SocketError = error{ |
| 797 | SocketTypeNotSupported, | 579 | SocketTypeNotSupported, |
| 798 | } || UnexpectedError; | 580 | } || UnexpectedError; |
| 799 | 581 | ||
| 800 | pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t { | ||
| 801 | const have_sock_flags = !builtin.target.os.tag.isDarwin() and native_os != .haiku; | ||
| 802 | const filtered_sock_type = if (!have_sock_flags) | ||
| 803 | socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC) | ||
| 804 | else | ||
| 805 | socket_type; | ||
| 806 | const rc = system.socket(domain, filtered_sock_type, protocol); | ||
| 807 | switch (errno(rc)) { | ||
| 808 | .SUCCESS => { | ||
| 809 | const fd: fd_t = @intCast(rc); | ||
| 810 | errdefer close(fd); | ||
| 811 | if (!have_sock_flags) { | ||
| 812 | try setSockFlags(fd, socket_type); | ||
| 813 | } | ||
| 814 | return fd; | ||
| 815 | }, | ||
| 816 | .ACCES => return error.AccessDenied, | ||
| 817 | .AFNOSUPPORT => return error.AddressFamilyUnsupported, | ||
| 818 | .INVAL => return error.ProtocolFamilyNotAvailable, | ||
| 819 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 820 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 821 | .NOBUFS => return error.SystemResources, | ||
| 822 | .NOMEM => return error.SystemResources, | ||
| 823 | .PROTONOSUPPORT => return error.ProtocolNotSupported, | ||
| 824 | .PROTOTYPE => return error.SocketTypeNotSupported, | ||
| 825 | else => |err| return unexpectedErrno(err), | ||
| 826 | } | ||
| 827 | } | ||
| 828 | |||
| 829 | pub fn socketpair(domain: u32, socket_type: u32, protocol: u32) SocketError![2]socket_t { | 582 | pub fn socketpair(domain: u32, socket_type: u32, protocol: u32) SocketError![2]socket_t { |
| 830 | // Note to the future: we could provide a shim here for e.g. windows which | 583 | // Note to the future: we could provide a shim here for e.g. windows which |
| 831 | // creates a listening socket, then creates a second socket and connects it | 584 | // creates a listening socket, then creates a second socket and connects it |
| ... | @@ -866,168 +619,6 @@ pub fn socketpair(domain: u32, socket_type: u32, protocol: u32) SocketError![2]s | ... | @@ -866,168 +619,6 @@ pub fn socketpair(domain: u32, socket_type: u32, protocol: u32) SocketError![2]s |
| 866 | } | 619 | } |
| 867 | } | 620 | } |
| 868 | 621 | ||
| 869 | pub const ShutdownError = error{ | ||
| 870 | ConnectionAborted, | ||
| 871 | |||
| 872 | /// Connection was reset by peer, application should close socket as it is no longer usable. | ||
| 873 | ConnectionResetByPeer, | ||
| 874 | BlockingOperationInProgress, | ||
| 875 | |||
| 876 | /// The network subsystem has failed. | ||
| 877 | NetworkDown, | ||
| 878 | |||
| 879 | /// The socket is not connected (connection-oriented sockets only). | ||
| 880 | SocketUnconnected, | ||
| 881 | SystemResources, | ||
| 882 | } || UnexpectedError; | ||
| 883 | |||
| 884 | pub const ShutdownHow = enum { recv, send, both }; | ||
| 885 | |||
| 886 | /// Shutdown socket send/receive operations | ||
| 887 | pub fn shutdown(sock: socket_t, how: ShutdownHow) ShutdownError!void { | ||
| 888 | if (native_os == .windows) { | ||
| 889 | const result = windows.ws2_32.shutdown(sock, switch (how) { | ||
| 890 | .recv => windows.ws2_32.SD_RECEIVE, | ||
| 891 | .send => windows.ws2_32.SD_SEND, | ||
| 892 | .both => windows.ws2_32.SD_BOTH, | ||
| 893 | }); | ||
| 894 | if (0 != result) switch (windows.ws2_32.WSAGetLastError()) { | ||
| 895 | .ECONNABORTED => return error.ConnectionAborted, | ||
| 896 | .ECONNRESET => return error.ConnectionResetByPeer, | ||
| 897 | .EINPROGRESS => return error.BlockingOperationInProgress, | ||
| 898 | .EINVAL => unreachable, | ||
| 899 | .ENETDOWN => return error.NetworkDown, | ||
| 900 | .ENOTCONN => return error.SocketUnconnected, | ||
| 901 | .ENOTSOCK => unreachable, | ||
| 902 | .NOTINITIALISED => unreachable, | ||
| 903 | else => |err| return windows.unexpectedWSAError(err), | ||
| 904 | }; | ||
| 905 | } else { | ||
| 906 | const rc = system.shutdown(sock, switch (how) { | ||
| 907 | .recv => SHUT.RD, | ||
| 908 | .send => SHUT.WR, | ||
| 909 | .both => SHUT.RDWR, | ||
| 910 | }); | ||
| 911 | switch (errno(rc)) { | ||
| 912 | .SUCCESS => return, | ||
| 913 | .BADF => unreachable, | ||
| 914 | .INVAL => unreachable, | ||
| 915 | .NOTCONN => return error.SocketUnconnected, | ||
| 916 | .NOTSOCK => unreachable, | ||
| 917 | .NOBUFS => return error.SystemResources, | ||
| 918 | else => |err| return unexpectedErrno(err), | ||
| 919 | } | ||
| 920 | } | ||
| 921 | } | ||
| 922 | |||
| 923 | pub const BindError = error{ | ||
| 924 | SymLinkLoop, | ||
| 925 | NameTooLong, | ||
| 926 | FileNotFound, | ||
| 927 | NotDir, | ||
| 928 | ReadOnlyFileSystem, | ||
| 929 | AccessDenied, | ||
| 930 | } || std.Io.net.IpAddress.BindError; | ||
| 931 | |||
| 932 | pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!void { | ||
| 933 | if (native_os == .windows) { | ||
| 934 | @compileError("use std.Io instead"); | ||
| 935 | } else { | ||
| 936 | const rc = system.bind(sock, addr, len); | ||
| 937 | switch (errno(rc)) { | ||
| 938 | .SUCCESS => return, | ||
| 939 | .ACCES, .PERM => return error.AccessDenied, | ||
| 940 | .ADDRINUSE => return error.AddressInUse, | ||
| 941 | .BADF => unreachable, // always a race condition if this error is returned | ||
| 942 | .INVAL => unreachable, // invalid parameters | ||
| 943 | .NOTSOCK => unreachable, // invalid `sockfd` | ||
| 944 | .AFNOSUPPORT => return error.AddressFamilyUnsupported, | ||
| 945 | .ADDRNOTAVAIL => return error.AddressUnavailable, | ||
| 946 | .FAULT => unreachable, // invalid `addr` pointer | ||
| 947 | .LOOP => return error.SymLinkLoop, | ||
| 948 | .NAMETOOLONG => return error.NameTooLong, | ||
| 949 | .NOENT => return error.FileNotFound, | ||
| 950 | .NOMEM => return error.SystemResources, | ||
| 951 | .NOTDIR => return error.NotDir, | ||
| 952 | .ROFS => return error.ReadOnlyFileSystem, | ||
| 953 | else => |err| return unexpectedErrno(err), | ||
| 954 | } | ||
| 955 | } | ||
| 956 | unreachable; | ||
| 957 | } | ||
| 958 | |||
| 959 | pub const ListenError = error{ | ||
| 960 | FileDescriptorNotASocket, | ||
| 961 | OperationUnsupported, | ||
| 962 | } || std.Io.net.IpAddress.ListenError || std.Io.net.UnixAddress.ListenError; | ||
| 963 | |||
| 964 | pub fn listen(sock: socket_t, backlog: u31) ListenError!void { | ||
| 965 | if (native_os == .windows) { | ||
| 966 | @compileError("use std.Io instead"); | ||
| 967 | } else { | ||
| 968 | const rc = system.listen(sock, backlog); | ||
| 969 | switch (errno(rc)) { | ||
| 970 | .SUCCESS => return, | ||
| 971 | .ADDRINUSE => return error.AddressInUse, | ||
| 972 | .BADF => unreachable, | ||
| 973 | .NOTSOCK => return error.FileDescriptorNotASocket, | ||
| 974 | .OPNOTSUPP => return error.OperationUnsupported, | ||
| 975 | else => |err| return unexpectedErrno(err), | ||
| 976 | } | ||
| 977 | } | ||
| 978 | } | ||
| 979 | |||
| 980 | pub const AcceptError = std.Io.net.Server.AcceptError; | ||
| 981 | |||
| 982 | pub fn accept( | ||
| 983 | sock: socket_t, | ||
| 984 | addr: ?*sockaddr, | ||
| 985 | addr_size: ?*socklen_t, | ||
| 986 | flags: u32, | ||
| 987 | ) AcceptError!socket_t { | ||
| 988 | const have_accept4 = !(builtin.target.os.tag.isDarwin() or native_os == .windows or native_os == .haiku); | ||
| 989 | assert(0 == (flags & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC))); // Unsupported flag(s) | ||
| 990 | |||
| 991 | const accepted_sock: socket_t = while (true) { | ||
| 992 | const rc = if (have_accept4) | ||
| 993 | system.accept4(sock, addr, addr_size, flags) | ||
| 994 | else | ||
| 995 | system.accept(sock, addr, addr_size); | ||
| 996 | |||
| 997 | if (native_os == .windows) { | ||
| 998 | @compileError("use std.Io instead"); | ||
| 999 | } else { | ||
| 1000 | switch (errno(rc)) { | ||
| 1001 | .SUCCESS => break @intCast(rc), | ||
| 1002 | .INTR => continue, | ||
| 1003 | .AGAIN => return error.WouldBlock, | ||
| 1004 | .BADF => unreachable, // always a race condition | ||
| 1005 | .CONNABORTED => return error.ConnectionAborted, | ||
| 1006 | .FAULT => unreachable, | ||
| 1007 | .INVAL => return error.SocketNotListening, | ||
| 1008 | .NOTSOCK => unreachable, | ||
| 1009 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 1010 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 1011 | .NOBUFS => return error.SystemResources, | ||
| 1012 | .NOMEM => return error.SystemResources, | ||
| 1013 | .OPNOTSUPP => unreachable, | ||
| 1014 | .PROTO => return error.ProtocolFailure, | ||
| 1015 | .PERM => return error.BlockedByFirewall, | ||
| 1016 | else => |err| return unexpectedErrno(err), | ||
| 1017 | } | ||
| 1018 | } | ||
| 1019 | }; | ||
| 1020 | |||
| 1021 | errdefer switch (native_os) { | ||
| 1022 | .windows => windows.closesocket(accepted_sock) catch unreachable, | ||
| 1023 | else => close(accepted_sock), | ||
| 1024 | }; | ||
| 1025 | if (!have_accept4) { | ||
| 1026 | try setSockFlags(accepted_sock, flags); | ||
| 1027 | } | ||
| 1028 | return accepted_sock; | ||
| 1029 | } | ||
| 1030 | |||
| 1031 | fn setSockFlags(sock: socket_t, flags: u32) !void { | 622 | fn setSockFlags(sock: socket_t, flags: u32) !void { |
| 1032 | if ((flags & SOCK.CLOEXEC) != 0) { | 623 | if ((flags & SOCK.CLOEXEC) != 0) { |
| 1033 | if (native_os == .windows) { | 624 | if (native_os == .windows) { |
| ... | @@ -1086,94 +677,6 @@ fn setSockFlags(sock: socket_t, flags: u32) !void { | ... | @@ -1086,94 +677,6 @@ fn setSockFlags(sock: socket_t, flags: u32) !void { |
| 1086 | } | 677 | } |
| 1087 | } | 678 | } |
| 1088 | 679 | ||
| 1089 | pub const EpollCreateError = error{ | ||
| 1090 | /// The per-user limit on the number of epoll instances imposed by | ||
| 1091 | /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further | ||
| 1092 | /// details. | ||
| 1093 | /// Or, The per-process limit on the number of open file descriptors has been reached. | ||
| 1094 | ProcessFdQuotaExceeded, | ||
| 1095 | |||
| 1096 | /// The system-wide limit on the total number of open files has been reached. | ||
| 1097 | SystemFdQuotaExceeded, | ||
| 1098 | |||
| 1099 | /// There was insufficient memory to create the kernel object. | ||
| 1100 | SystemResources, | ||
| 1101 | } || UnexpectedError; | ||
| 1102 | |||
| 1103 | pub fn epoll_create1(flags: u32) EpollCreateError!i32 { | ||
| 1104 | const rc = system.epoll_create1(flags); | ||
| 1105 | switch (errno(rc)) { | ||
| 1106 | .SUCCESS => return @intCast(rc), | ||
| 1107 | else => |err| return unexpectedErrno(err), | ||
| 1108 | |||
| 1109 | .INVAL => unreachable, | ||
| 1110 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 1111 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 1112 | .NOMEM => return error.SystemResources, | ||
| 1113 | } | ||
| 1114 | } | ||
| 1115 | |||
| 1116 | pub const EpollCtlError = error{ | ||
| 1117 | /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered | ||
| 1118 | /// with this epoll instance. | ||
| 1119 | FileDescriptorAlreadyPresentInSet, | ||
| 1120 | |||
| 1121 | /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a | ||
| 1122 | /// circular loop of epoll instances monitoring one another. | ||
| 1123 | OperationCausesCircularLoop, | ||
| 1124 | |||
| 1125 | /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll | ||
| 1126 | /// instance. | ||
| 1127 | FileDescriptorNotRegistered, | ||
| 1128 | |||
| 1129 | /// There was insufficient memory to handle the requested op control operation. | ||
| 1130 | SystemResources, | ||
| 1131 | |||
| 1132 | /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while | ||
| 1133 | /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance. | ||
| 1134 | /// See epoll(7) for further details. | ||
| 1135 | UserResourceLimitReached, | ||
| 1136 | |||
| 1137 | /// The target file fd does not support epoll. This error can occur if fd refers to, | ||
| 1138 | /// for example, a regular file or a directory. | ||
| 1139 | FileDescriptorIncompatibleWithEpoll, | ||
| 1140 | } || UnexpectedError; | ||
| 1141 | |||
| 1142 | pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: ?*system.epoll_event) EpollCtlError!void { | ||
| 1143 | const rc = system.epoll_ctl(epfd, op, fd, event); | ||
| 1144 | switch (errno(rc)) { | ||
| 1145 | .SUCCESS => return, | ||
| 1146 | else => |err| return unexpectedErrno(err), | ||
| 1147 | |||
| 1148 | .BADF => unreachable, // always a race condition if this happens | ||
| 1149 | .EXIST => return error.FileDescriptorAlreadyPresentInSet, | ||
| 1150 | .INVAL => unreachable, | ||
| 1151 | .LOOP => return error.OperationCausesCircularLoop, | ||
| 1152 | .NOENT => return error.FileDescriptorNotRegistered, | ||
| 1153 | .NOMEM => return error.SystemResources, | ||
| 1154 | .NOSPC => return error.UserResourceLimitReached, | ||
| 1155 | .PERM => return error.FileDescriptorIncompatibleWithEpoll, | ||
| 1156 | } | ||
| 1157 | } | ||
| 1158 | |||
| 1159 | /// Waits for an I/O event on an epoll file descriptor. | ||
| 1160 | /// Returns the number of file descriptors ready for the requested I/O, | ||
| 1161 | /// or zero if no file descriptor became ready during the requested timeout milliseconds. | ||
| 1162 | pub fn epoll_wait(epfd: i32, events: []system.epoll_event, timeout: i32) usize { | ||
| 1163 | while (true) { | ||
| 1164 | // TODO get rid of the @intCast | ||
| 1165 | const rc = system.epoll_wait(epfd, events.ptr, @intCast(events.len), timeout); | ||
| 1166 | switch (errno(rc)) { | ||
| 1167 | .SUCCESS => return @intCast(rc), | ||
| 1168 | .INTR => continue, | ||
| 1169 | .BADF => unreachable, | ||
| 1170 | .FAULT => unreachable, | ||
| 1171 | .INVAL => unreachable, | ||
| 1172 | else => unreachable, | ||
| 1173 | } | ||
| 1174 | } | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | pub const EventFdError = error{ | 680 | pub const EventFdError = error{ |
| 1178 | SystemResources, | 681 | SystemResources, |
| 1179 | ProcessFdQuotaExceeded, | 682 | ProcessFdQuotaExceeded, |
| ... | @@ -1207,35 +710,6 @@ pub const GetSockNameError = error{ | ... | @@ -1207,35 +710,6 @@ pub const GetSockNameError = error{ |
| 1207 | FileDescriptorNotASocket, | 710 | FileDescriptorNotASocket, |
| 1208 | } || UnexpectedError; | 711 | } || UnexpectedError; |
| 1209 | 712 | ||
| 1210 | pub fn getsockname(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void { | ||
| 1211 | if (native_os == .windows) { | ||
| 1212 | const rc = windows.getsockname(sock, addr, addrlen); | ||
| 1213 | if (rc == windows.ws2_32.SOCKET_ERROR) { | ||
| 1214 | switch (windows.ws2_32.WSAGetLastError()) { | ||
| 1215 | .NOTINITIALISED => unreachable, | ||
| 1216 | .ENETDOWN => return error.NetworkDown, | ||
| 1217 | .EFAULT => unreachable, // addr or addrlen have invalid pointers or addrlen points to an incorrect value | ||
| 1218 | .ENOTSOCK => return error.FileDescriptorNotASocket, | ||
| 1219 | .EINVAL => return error.SocketNotBound, | ||
| 1220 | else => |err| return windows.unexpectedWSAError(err), | ||
| 1221 | } | ||
| 1222 | } | ||
| 1223 | return; | ||
| 1224 | } else { | ||
| 1225 | const rc = system.getsockname(sock, addr, addrlen); | ||
| 1226 | switch (errno(rc)) { | ||
| 1227 | .SUCCESS => return, | ||
| 1228 | else => |err| return unexpectedErrno(err), | ||
| 1229 | |||
| 1230 | .BADF => unreachable, // always a race condition | ||
| 1231 | .FAULT => unreachable, | ||
| 1232 | .INVAL => unreachable, // invalid parameters | ||
| 1233 | .NOTSOCK => return error.FileDescriptorNotASocket, | ||
| 1234 | .NOBUFS => return error.SystemResources, | ||
| 1235 | } | ||
| 1236 | } | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | pub fn getpeername(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void { | 713 | pub fn getpeername(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void { |
| 1240 | if (native_os == .windows) { | 714 | if (native_os == .windows) { |
| 1241 | const rc = windows.getpeername(sock, addr, addrlen); | 715 | const rc = windows.getpeername(sock, addr, addrlen); |
| ... | @@ -1295,72 +769,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne | ... | @@ -1295,72 +769,7 @@ pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) Conne |
| 1295 | .NOENT => return error.FileNotFound, // Returned when socket is AF.UNIX and the given path does not exist. | 769 | .NOENT => return error.FileNotFound, // Returned when socket is AF.UNIX and the given path does not exist. |
| 1296 | .CONNABORTED => unreachable, // Tried to reuse socket that previously received error.ConnectionRefused. | 770 | .CONNABORTED => unreachable, // Tried to reuse socket that previously received error.ConnectionRefused. |
| 1297 | else => |err| return unexpectedErrno(err), | 771 | else => |err| return unexpectedErrno(err), |
| 1298 | } | 772 | } |
| 1299 | } | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | pub const GetSockOptError = error{ | ||
| 1303 | /// The calling process does not have the appropriate privileges. | ||
| 1304 | AccessDenied, | ||
| 1305 | |||
| 1306 | /// The option is not supported by the protocol. | ||
| 1307 | InvalidProtocolOption, | ||
| 1308 | |||
| 1309 | /// Insufficient resources are available in the system to complete the call. | ||
| 1310 | SystemResources, | ||
| 1311 | } || UnexpectedError; | ||
| 1312 | |||
| 1313 | pub fn getsockopt(fd: socket_t, level: i32, optname: u32, opt: []u8) GetSockOptError!void { | ||
| 1314 | var len: socklen_t = @intCast(opt.len); | ||
| 1315 | switch (errno(system.getsockopt(fd, level, optname, opt.ptr, &len))) { | ||
| 1316 | .SUCCESS => { | ||
| 1317 | std.debug.assert(len == opt.len); | ||
| 1318 | }, | ||
| 1319 | .BADF => unreachable, | ||
| 1320 | .NOTSOCK => unreachable, | ||
| 1321 | .INVAL => unreachable, | ||
| 1322 | .FAULT => unreachable, | ||
| 1323 | .NOPROTOOPT => return error.InvalidProtocolOption, | ||
| 1324 | .NOMEM => return error.SystemResources, | ||
| 1325 | .NOBUFS => return error.SystemResources, | ||
| 1326 | .ACCES => return error.AccessDenied, | ||
| 1327 | else => |err| return unexpectedErrno(err), | ||
| 1328 | } | ||
| 1329 | } | ||
| 1330 | |||
| 1331 | pub fn getsockoptError(sockfd: fd_t) ConnectError!void { | ||
| 1332 | var err_code: i32 = undefined; | ||
| 1333 | var size: u32 = @sizeOf(u32); | ||
| 1334 | const rc = system.getsockopt(sockfd, SOL.SOCKET, SO.ERROR, @ptrCast(&err_code), &size); | ||
| 1335 | assert(size == 4); | ||
| 1336 | switch (errno(rc)) { | ||
| 1337 | .SUCCESS => switch (@as(E, @enumFromInt(err_code))) { | ||
| 1338 | .SUCCESS => return, | ||
| 1339 | .ACCES => return error.AccessDenied, | ||
| 1340 | .PERM => return error.PermissionDenied, | ||
| 1341 | .ADDRINUSE => return error.AddressInUse, | ||
| 1342 | .ADDRNOTAVAIL => return error.AddressUnavailable, | ||
| 1343 | .AFNOSUPPORT => return error.AddressFamilyUnsupported, | ||
| 1344 | .AGAIN => return error.SystemResources, | ||
| 1345 | .ALREADY => return error.ConnectionPending, | ||
| 1346 | .BADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 1347 | .CONNREFUSED => return error.ConnectionRefused, | ||
| 1348 | .FAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 1349 | .ISCONN => return error.AlreadyConnected, // The socket is already connected. | ||
| 1350 | .HOSTUNREACH => return error.NetworkUnreachable, | ||
| 1351 | .NETUNREACH => return error.NetworkUnreachable, | ||
| 1352 | .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 1353 | .PROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 1354 | .TIMEDOUT => return error.Timeout, | ||
| 1355 | .CONNRESET => return error.ConnectionResetByPeer, | ||
| 1356 | else => |err| return unexpectedErrno(err), | ||
| 1357 | }, | ||
| 1358 | .BADF => unreachable, // The argument sockfd is not a valid file descriptor. | ||
| 1359 | .FAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. | ||
| 1360 | .INVAL => unreachable, | ||
| 1361 | .NOPROTOOPT => unreachable, // The option is unknown at the level indicated. | ||
| 1362 | .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 1363 | else => |err| return unexpectedErrno(err), | ||
| 1364 | } | 773 | } |
| 1365 | } | 774 | } |
| 1366 | 775 | ||
| ... | @@ -1383,120 +792,6 @@ pub fn fstat(fd: fd_t) FStatError!Stat { | ... | @@ -1383,120 +792,6 @@ pub fn fstat(fd: fd_t) FStatError!Stat { |
| 1383 | } | 792 | } |
| 1384 | } | 793 | } |
| 1385 | 794 | ||
| 1386 | pub const FStatAtError = FStatError || error{ | ||
| 1387 | NameTooLong, | ||
| 1388 | FileNotFound, | ||
| 1389 | SymLinkLoop, | ||
| 1390 | BadPathName, | ||
| 1391 | }; | ||
| 1392 | |||
| 1393 | /// Similar to `fstat`, but returns stat of a resource pointed to by `pathname` | ||
| 1394 | /// which is relative to `dirfd` handle. | ||
| 1395 | /// On WASI, `pathname` should be encoded as valid UTF-8. | ||
| 1396 | /// On other platforms, `pathname` is an opaque sequence of bytes with no particular encoding. | ||
| 1397 | /// See also `fstatatZ`. | ||
| 1398 | pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat { | ||
| 1399 | if (native_os == .wasi and !builtin.link_libc) { | ||
| 1400 | @compileError("use std.Io instead"); | ||
| 1401 | } else if (native_os == .windows) { | ||
| 1402 | @compileError("fstatat is not yet implemented on Windows"); | ||
| 1403 | } else { | ||
| 1404 | const pathname_c = try toPosixPath(pathname); | ||
| 1405 | return fstatatZ(dirfd, &pathname_c, flags); | ||
| 1406 | } | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | /// Same as `fstatat` but `pathname` is null-terminated. | ||
| 1410 | /// See also `fstatat`. | ||
| 1411 | pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat { | ||
| 1412 | if (native_os == .wasi and !builtin.link_libc) { | ||
| 1413 | @compileError("use std.Io instead"); | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | var stat = mem.zeroes(Stat); | ||
| 1417 | switch (errno(system.fstatat(dirfd, pathname, &stat, flags))) { | ||
| 1418 | .SUCCESS => return stat, | ||
| 1419 | .INVAL => unreachable, | ||
| 1420 | .BADF => unreachable, // Always a race condition. | ||
| 1421 | .NOMEM => return error.SystemResources, | ||
| 1422 | .ACCES => return error.AccessDenied, | ||
| 1423 | .PERM => return error.PermissionDenied, | ||
| 1424 | .FAULT => unreachable, | ||
| 1425 | .NAMETOOLONG => return error.NameTooLong, | ||
| 1426 | .LOOP => return error.SymLinkLoop, | ||
| 1427 | .NOENT => return error.FileNotFound, | ||
| 1428 | .NOTDIR => return error.FileNotFound, | ||
| 1429 | .ILSEQ => return error.BadPathName, | ||
| 1430 | else => |err| return unexpectedErrno(err), | ||
| 1431 | } | ||
| 1432 | } | ||
| 1433 | |||
| 1434 | pub const KQueueError = error{ | ||
| 1435 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 1436 | ProcessFdQuotaExceeded, | ||
| 1437 | |||
| 1438 | /// The system-wide limit on the total number of open files has been reached. | ||
| 1439 | SystemFdQuotaExceeded, | ||
| 1440 | } || UnexpectedError; | ||
| 1441 | |||
| 1442 | pub fn kqueue() KQueueError!i32 { | ||
| 1443 | const rc = system.kqueue(); | ||
| 1444 | switch (errno(rc)) { | ||
| 1445 | .SUCCESS => return @intCast(rc), | ||
| 1446 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 1447 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 1448 | else => |err| return unexpectedErrno(err), | ||
| 1449 | } | ||
| 1450 | } | ||
| 1451 | |||
| 1452 | pub const KEventError = error{ | ||
| 1453 | /// The process does not have permission to register a filter. | ||
| 1454 | AccessDenied, | ||
| 1455 | |||
| 1456 | /// The event could not be found to be modified or deleted. | ||
| 1457 | EventNotFound, | ||
| 1458 | |||
| 1459 | /// No memory was available to register the event. | ||
| 1460 | SystemResources, | ||
| 1461 | |||
| 1462 | /// The specified process to attach to does not exist. | ||
| 1463 | ProcessNotFound, | ||
| 1464 | |||
| 1465 | /// changelist or eventlist had too many items on it. | ||
| 1466 | /// TODO remove this possibility | ||
| 1467 | Overflow, | ||
| 1468 | }; | ||
| 1469 | |||
| 1470 | pub fn kevent( | ||
| 1471 | kq: i32, | ||
| 1472 | changelist: []const Kevent, | ||
| 1473 | eventlist: []Kevent, | ||
| 1474 | timeout: ?*const timespec, | ||
| 1475 | ) KEventError!usize { | ||
| 1476 | while (true) { | ||
| 1477 | const rc = system.kevent( | ||
| 1478 | kq, | ||
| 1479 | changelist.ptr, | ||
| 1480 | cast(c_int, changelist.len) orelse return error.Overflow, | ||
| 1481 | eventlist.ptr, | ||
| 1482 | cast(c_int, eventlist.len) orelse return error.Overflow, | ||
| 1483 | timeout, | ||
| 1484 | ); | ||
| 1485 | switch (errno(rc)) { | ||
| 1486 | .SUCCESS => return @intCast(rc), | ||
| 1487 | .ACCES => return error.AccessDenied, | ||
| 1488 | .FAULT => unreachable, | ||
| 1489 | .BADF => unreachable, // Always a race condition. | ||
| 1490 | .INTR => continue, | ||
| 1491 | .INVAL => unreachable, | ||
| 1492 | .NOENT => return error.EventNotFound, | ||
| 1493 | .NOMEM => return error.SystemResources, | ||
| 1494 | .SRCH => return error.ProcessNotFound, | ||
| 1495 | else => unreachable, | ||
| 1496 | } | ||
| 1497 | } | ||
| 1498 | } | ||
| 1499 | |||
| 1500 | pub const INotifyInitError = error{ | 795 | pub const INotifyInitError = error{ |
| 1501 | ProcessFdQuotaExceeded, | 796 | ProcessFdQuotaExceeded, |
| 1502 | SystemFdQuotaExceeded, | 797 | SystemFdQuotaExceeded, |
| ... | @@ -1751,18 +1046,6 @@ pub fn mprotect(memory: []align(page_size_min) u8, protection: u32) MProtectErro | ... | @@ -1751,18 +1046,6 @@ pub fn mprotect(memory: []align(page_size_min) u8, protection: u32) MProtectErro |
| 1751 | } | 1046 | } |
| 1752 | } | 1047 | } |
| 1753 | 1048 | ||
| 1754 | pub const ForkError = error{SystemResources} || UnexpectedError; | ||
| 1755 | |||
| 1756 | pub fn fork() ForkError!pid_t { | ||
| 1757 | const rc = system.fork(); | ||
| 1758 | switch (errno(rc)) { | ||
| 1759 | .SUCCESS => return @intCast(rc), | ||
| 1760 | .AGAIN => return error.SystemResources, | ||
| 1761 | .NOMEM => return error.SystemResources, | ||
| 1762 | else => |err| return unexpectedErrno(err), | ||
| 1763 | } | ||
| 1764 | } | ||
| 1765 | |||
| 1766 | pub const MMapError = error{ | 1049 | pub const MMapError = error{ |
| 1767 | /// The underlying filesystem of the specified file does not support memory mapping. | 1050 | /// The underlying filesystem of the specified file does not support memory mapping. |
| 1768 | MemoryMappingNotSupported, | 1051 | MemoryMappingNotSupported, |
| ... | @@ -1991,31 +1274,6 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize { | ... | @@ -1991,31 +1274,6 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize { |
| 1991 | } | 1274 | } |
| 1992 | } | 1275 | } |
| 1993 | 1276 | ||
| 1994 | /// Spurious wakeups are possible and no precision of timing is guaranteed. | ||
| 1995 | pub fn nanosleep(seconds: u64, nanoseconds: u64) void { | ||
| 1996 | var req = timespec{ | ||
| 1997 | .sec = cast(isize, seconds) orelse maxInt(isize), | ||
| 1998 | .nsec = cast(isize, nanoseconds) orelse maxInt(isize), | ||
| 1999 | }; | ||
| 2000 | var rem: timespec = undefined; | ||
| 2001 | while (true) { | ||
| 2002 | switch (errno(system.nanosleep(&req, &rem))) { | ||
| 2003 | .FAULT => unreachable, | ||
| 2004 | .INVAL => { | ||
| 2005 | // Sometimes Darwin returns EINVAL for no reason. | ||
| 2006 | // We treat it as a spurious wakeup. | ||
| 2007 | return; | ||
| 2008 | }, | ||
| 2009 | .INTR => { | ||
| 2010 | req = rem; | ||
| 2011 | continue; | ||
| 2012 | }, | ||
| 2013 | // This prong handles success as well as unexpected errors. | ||
| 2014 | else => return, | ||
| 2015 | } | ||
| 2016 | } | ||
| 2017 | } | ||
| 2018 | |||
| 2019 | pub fn getSelfPhdrs() []std.elf.ElfN.Phdr { | 1277 | pub fn getSelfPhdrs() []std.elf.ElfN.Phdr { |
| 2020 | const getauxval = if (builtin.link_libc) std.c.getauxval else std.os.linux.getauxval; | 1278 | const getauxval = if (builtin.link_libc) std.c.getauxval else std.os.linux.getauxval; |
| 2021 | assert(getauxval(std.elf.AT_PHENT) == @sizeOf(std.elf.ElfN.Phdr)); | 1279 | assert(getauxval(std.elf.AT_PHENT) == @sizeOf(std.elf.ElfN.Phdr)); |
| ... | @@ -2287,289 +1545,6 @@ pub fn uname() utsname { | ... | @@ -2287,289 +1545,6 @@ pub fn uname() utsname { |
| 2287 | } | 1545 | } |
| 2288 | } | 1546 | } |
| 2289 | 1547 | ||
| 2290 | pub const SendError = error{ | ||
| 2291 | /// (For UNIX domain sockets, which are identified by pathname) Write permission is denied | ||
| 2292 | /// on the destination socket file, or search permission is denied for one of the | ||
| 2293 | /// directories the path prefix. (See path_resolution(7).) | ||
| 2294 | /// (For UDP sockets) An attempt was made to send to a network/broadcast address as though | ||
| 2295 | /// it was a unicast address. | ||
| 2296 | AccessDenied, | ||
| 2297 | |||
| 2298 | /// The socket is marked nonblocking and the requested operation would block, and | ||
| 2299 | /// there is no global event loop configured. | ||
| 2300 | /// It's also possible to get this error under the following condition: | ||
| 2301 | /// (Internet domain datagram sockets) The socket referred to by sockfd had not previously | ||
| 2302 | /// been bound to an address and, upon attempting to bind it to an ephemeral port, it was | ||
| 2303 | /// determined that all port numbers in the ephemeral port range are currently in use. See | ||
| 2304 | /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). | ||
| 2305 | WouldBlock, | ||
| 2306 | |||
| 2307 | /// Another Fast Open is already in progress. | ||
| 2308 | FastOpenAlreadyInProgress, | ||
| 2309 | |||
| 2310 | /// Connection reset by peer. | ||
| 2311 | ConnectionResetByPeer, | ||
| 2312 | |||
| 2313 | /// The socket type requires that message be sent atomically, and the size of the message | ||
| 2314 | /// to be sent made this impossible. The message is not transmitted. | ||
| 2315 | MessageOversize, | ||
| 2316 | |||
| 2317 | /// The output queue for a network interface was full. This generally indicates that the | ||
| 2318 | /// interface has stopped sending, but may be caused by transient congestion. (Normally, | ||
| 2319 | /// this does not occur in Linux. Packets are just silently dropped when a device queue | ||
| 2320 | /// overflows.) | ||
| 2321 | /// This is also caused when there is not enough kernel memory available. | ||
| 2322 | SystemResources, | ||
| 2323 | |||
| 2324 | /// The local end has been shut down on a connection oriented socket. In this case, the | ||
| 2325 | /// process will also receive a SIGPIPE unless MSG.NOSIGNAL is set. | ||
| 2326 | BrokenPipe, | ||
| 2327 | |||
| 2328 | FileDescriptorNotASocket, | ||
| 2329 | |||
| 2330 | /// Network is unreachable. | ||
| 2331 | NetworkUnreachable, | ||
| 2332 | |||
| 2333 | /// The local network interface used to reach the destination is down. | ||
| 2334 | NetworkDown, | ||
| 2335 | |||
| 2336 | /// The destination address is not listening. | ||
| 2337 | ConnectionRefused, | ||
| 2338 | } || UnexpectedError; | ||
| 2339 | |||
| 2340 | pub const SendMsgError = SendError || error{ | ||
| 2341 | /// The passed address didn't have the correct address family in its sa_family field. | ||
| 2342 | AddressFamilyUnsupported, | ||
| 2343 | |||
| 2344 | /// Returned when socket is AF.UNIX and the given path has a symlink loop. | ||
| 2345 | SymLinkLoop, | ||
| 2346 | |||
| 2347 | /// Returned when socket is AF.UNIX and the given path length exceeds `max_path_bytes` bytes. | ||
| 2348 | NameTooLong, | ||
| 2349 | |||
| 2350 | /// Returned when socket is AF.UNIX and the given path does not point to an existing file. | ||
| 2351 | FileNotFound, | ||
| 2352 | NotDir, | ||
| 2353 | |||
| 2354 | /// The socket is not connected (connection-oriented sockets only). | ||
| 2355 | SocketUnconnected, | ||
| 2356 | AddressUnavailable, | ||
| 2357 | }; | ||
| 2358 | |||
| 2359 | pub fn sendmsg( | ||
| 2360 | /// The file descriptor of the sending socket. | ||
| 2361 | sockfd: socket_t, | ||
| 2362 | /// Message header and iovecs | ||
| 2363 | msg: *const msghdr_const, | ||
| 2364 | flags: u32, | ||
| 2365 | ) SendMsgError!usize { | ||
| 2366 | while (true) { | ||
| 2367 | const rc = system.sendmsg(sockfd, msg, flags); | ||
| 2368 | if (native_os == .windows) { | ||
| 2369 | if (rc == windows.ws2_32.SOCKET_ERROR) { | ||
| 2370 | switch (windows.ws2_32.WSAGetLastError()) { | ||
| 2371 | .EACCES => return error.AccessDenied, | ||
| 2372 | .EADDRNOTAVAIL => return error.AddressUnavailable, | ||
| 2373 | .ECONNRESET => return error.ConnectionResetByPeer, | ||
| 2374 | .EMSGSIZE => return error.MessageOversize, | ||
| 2375 | .ENOBUFS => return error.SystemResources, | ||
| 2376 | .ENOTSOCK => return error.FileDescriptorNotASocket, | ||
| 2377 | .EAFNOSUPPORT => return error.AddressFamilyUnsupported, | ||
| 2378 | .EDESTADDRREQ => unreachable, // A destination address is required. | ||
| 2379 | .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. | ||
| 2380 | .EHOSTUNREACH => return error.NetworkUnreachable, | ||
| 2381 | // TODO: EINPROGRESS, EINTR | ||
| 2382 | .EINVAL => unreachable, | ||
| 2383 | .ENETDOWN => return error.NetworkDown, | ||
| 2384 | .ENETRESET => return error.ConnectionResetByPeer, | ||
| 2385 | .ENETUNREACH => return error.NetworkUnreachable, | ||
| 2386 | .ENOTCONN => return error.SocketUnconnected, | ||
| 2387 | .ESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH. | ||
| 2388 | .EWOULDBLOCK => return error.WouldBlock, | ||
| 2389 | .NOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function. | ||
| 2390 | else => |err| return windows.unexpectedWSAError(err), | ||
| 2391 | } | ||
| 2392 | } else { | ||
| 2393 | return @intCast(rc); | ||
| 2394 | } | ||
| 2395 | } else { | ||
| 2396 | switch (errno(rc)) { | ||
| 2397 | .SUCCESS => return @intCast(rc), | ||
| 2398 | |||
| 2399 | .ACCES => return error.AccessDenied, | ||
| 2400 | .AGAIN => return error.WouldBlock, | ||
| 2401 | .ALREADY => return error.FastOpenAlreadyInProgress, | ||
| 2402 | .BADF => unreachable, // always a race condition | ||
| 2403 | .CONNRESET => return error.ConnectionResetByPeer, | ||
| 2404 | .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set. | ||
| 2405 | .FAULT => unreachable, // An invalid user space address was specified for an argument. | ||
| 2406 | .INTR => continue, | ||
| 2407 | .INVAL => unreachable, // Invalid argument passed. | ||
| 2408 | .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified | ||
| 2409 | .MSGSIZE => return error.MessageOversize, | ||
| 2410 | .NOBUFS => return error.SystemResources, | ||
| 2411 | .NOMEM => return error.SystemResources, | ||
| 2412 | .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2413 | .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type. | ||
| 2414 | .PIPE => return error.BrokenPipe, | ||
| 2415 | .AFNOSUPPORT => return error.AddressFamilyUnsupported, | ||
| 2416 | .LOOP => return error.SymLinkLoop, | ||
| 2417 | .NAMETOOLONG => return error.NameTooLong, | ||
| 2418 | .NOENT => return error.FileNotFound, | ||
| 2419 | .NOTDIR => return error.NotDir, | ||
| 2420 | .HOSTUNREACH => return error.NetworkUnreachable, | ||
| 2421 | .NETUNREACH => return error.NetworkUnreachable, | ||
| 2422 | .NOTCONN => return error.SocketUnconnected, | ||
| 2423 | .NETDOWN => return error.NetworkDown, | ||
| 2424 | else => |err| return unexpectedErrno(err), | ||
| 2425 | } | ||
| 2426 | } | ||
| 2427 | } | ||
| 2428 | } | ||
| 2429 | |||
| 2430 | pub const SendToError = SendMsgError || error{ | ||
| 2431 | /// The destination address is not reachable by the bound address. | ||
| 2432 | UnreachableAddress, | ||
| 2433 | /// The destination address is not listening. | ||
| 2434 | ConnectionRefused, | ||
| 2435 | }; | ||
| 2436 | |||
| 2437 | /// Transmit a message to another socket. | ||
| 2438 | /// | ||
| 2439 | /// The `sendto` call may be used only when the socket is in a connected state (so that the intended | ||
| 2440 | /// recipient is known). The following call | ||
| 2441 | /// | ||
| 2442 | /// send(sockfd, buf, len, flags); | ||
| 2443 | /// | ||
| 2444 | /// is equivalent to | ||
| 2445 | /// | ||
| 2446 | /// sendto(sockfd, buf, len, flags, NULL, 0); | ||
| 2447 | /// | ||
| 2448 | /// If sendto() is used on a connection-mode (`SOCK.STREAM`, `SOCK.SEQPACKET`) socket, the arguments | ||
| 2449 | /// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted | ||
| 2450 | /// that the socket was actually connected. | ||
| 2451 | /// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying its size. | ||
| 2452 | /// | ||
| 2453 | /// If the message is too long to pass atomically through the underlying protocol, | ||
| 2454 | /// `SendError.MessageOversize` is returned, and the message is not transmitted. | ||
| 2455 | /// | ||
| 2456 | /// There is no indication of failure to deliver. | ||
| 2457 | /// | ||
| 2458 | /// When the message does not fit into the send buffer of the socket, `sendto` normally blocks, | ||
| 2459 | /// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail | ||
| 2460 | /// with `SendError.WouldBlock`. The `select` call may be used to determine when it is | ||
| 2461 | /// possible to send more data. | ||
| 2462 | pub fn sendto( | ||
| 2463 | /// The file descriptor of the sending socket. | ||
| 2464 | sockfd: socket_t, | ||
| 2465 | /// Message to send. | ||
| 2466 | buf: []const u8, | ||
| 2467 | flags: u32, | ||
| 2468 | dest_addr: ?*const sockaddr, | ||
| 2469 | addrlen: socklen_t, | ||
| 2470 | ) SendToError!usize { | ||
| 2471 | if (native_os == .windows) { | ||
| 2472 | switch (windows.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen)) { | ||
| 2473 | windows.ws2_32.SOCKET_ERROR => switch (windows.ws2_32.WSAGetLastError()) { | ||
| 2474 | .EACCES => return error.AccessDenied, | ||
| 2475 | .EADDRNOTAVAIL => return error.AddressUnavailable, | ||
| 2476 | .ECONNRESET => return error.ConnectionResetByPeer, | ||
| 2477 | .EMSGSIZE => return error.MessageOversize, | ||
| 2478 | .ENOBUFS => return error.SystemResources, | ||
| 2479 | .ENOTSOCK => return error.FileDescriptorNotASocket, | ||
| 2480 | .EAFNOSUPPORT => return error.AddressFamilyUnsupported, | ||
| 2481 | .EDESTADDRREQ => unreachable, // A destination address is required. | ||
| 2482 | .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. | ||
| 2483 | .EHOSTUNREACH => return error.NetworkUnreachable, | ||
| 2484 | // TODO: EINPROGRESS, EINTR | ||
| 2485 | .EINVAL => unreachable, | ||
| 2486 | .ENETDOWN => return error.NetworkDown, | ||
| 2487 | .ENETRESET => return error.ConnectionResetByPeer, | ||
| 2488 | .ENETUNREACH => return error.NetworkUnreachable, | ||
| 2489 | .ENOTCONN => return error.SocketUnconnected, | ||
| 2490 | .ESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH. | ||
| 2491 | .EWOULDBLOCK => return error.WouldBlock, | ||
| 2492 | .NOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function. | ||
| 2493 | else => |err| return windows.unexpectedWSAError(err), | ||
| 2494 | }, | ||
| 2495 | else => |rc| return @intCast(rc), | ||
| 2496 | } | ||
| 2497 | } | ||
| 2498 | while (true) { | ||
| 2499 | const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen); | ||
| 2500 | switch (errno(rc)) { | ||
| 2501 | .SUCCESS => return @intCast(rc), | ||
| 2502 | |||
| 2503 | .ACCES => return error.AccessDenied, | ||
| 2504 | .AGAIN => return error.WouldBlock, | ||
| 2505 | .ALREADY => return error.FastOpenAlreadyInProgress, | ||
| 2506 | .BADF => unreachable, // always a race condition | ||
| 2507 | .CONNREFUSED => return error.ConnectionRefused, | ||
| 2508 | .CONNRESET => return error.ConnectionResetByPeer, | ||
| 2509 | .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set. | ||
| 2510 | .FAULT => unreachable, // An invalid user space address was specified for an argument. | ||
| 2511 | .INTR => continue, | ||
| 2512 | .INVAL => return error.UnreachableAddress, | ||
| 2513 | .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified | ||
| 2514 | .MSGSIZE => return error.MessageOversize, | ||
| 2515 | .NOBUFS => return error.SystemResources, | ||
| 2516 | .NOMEM => return error.SystemResources, | ||
| 2517 | .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2518 | .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type. | ||
| 2519 | .PIPE => return error.BrokenPipe, | ||
| 2520 | .AFNOSUPPORT => return error.AddressFamilyUnsupported, | ||
| 2521 | .LOOP => return error.SymLinkLoop, | ||
| 2522 | .NAMETOOLONG => return error.NameTooLong, | ||
| 2523 | .NOENT => return error.FileNotFound, | ||
| 2524 | .NOTDIR => return error.NotDir, | ||
| 2525 | .HOSTUNREACH => return error.NetworkUnreachable, | ||
| 2526 | .NETUNREACH => return error.NetworkUnreachable, | ||
| 2527 | .NOTCONN => return error.SocketUnconnected, | ||
| 2528 | .NETDOWN => return error.NetworkDown, | ||
| 2529 | else => |err| return unexpectedErrno(err), | ||
| 2530 | } | ||
| 2531 | } | ||
| 2532 | } | ||
| 2533 | |||
| 2534 | /// Transmit a message to another socket. | ||
| 2535 | /// | ||
| 2536 | /// The `send` call may be used only when the socket is in a connected state (so that the intended | ||
| 2537 | /// recipient is known). The only difference between `send` and `write` is the presence of | ||
| 2538 | /// flags. With a zero flags argument, `send` is equivalent to `write`. Also, the following | ||
| 2539 | /// call | ||
| 2540 | /// | ||
| 2541 | /// send(sockfd, buf, len, flags); | ||
| 2542 | /// | ||
| 2543 | /// is equivalent to | ||
| 2544 | /// | ||
| 2545 | /// sendto(sockfd, buf, len, flags, NULL, 0); | ||
| 2546 | /// | ||
| 2547 | /// There is no indication of failure to deliver. | ||
| 2548 | /// | ||
| 2549 | /// When the message does not fit into the send buffer of the socket, `send` normally blocks, | ||
| 2550 | /// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail | ||
| 2551 | /// with `SendError.WouldBlock`. The `select` call may be used to determine when it is | ||
| 2552 | /// possible to send more data. | ||
| 2553 | pub fn send( | ||
| 2554 | /// The file descriptor of the sending socket. | ||
| 2555 | sockfd: socket_t, | ||
| 2556 | buf: []const u8, | ||
| 2557 | flags: u32, | ||
| 2558 | ) SendError!usize { | ||
| 2559 | return sendto(sockfd, buf, flags, null, 0) catch |err| switch (err) { | ||
| 2560 | error.AddressFamilyUnsupported => unreachable, | ||
| 2561 | error.SymLinkLoop => unreachable, | ||
| 2562 | error.NameTooLong => unreachable, | ||
| 2563 | error.FileNotFound => unreachable, | ||
| 2564 | error.NotDir => unreachable, | ||
| 2565 | error.NetworkUnreachable => unreachable, | ||
| 2566 | error.AddressUnavailable => unreachable, | ||
| 2567 | error.SocketUnconnected => unreachable, | ||
| 2568 | error.UnreachableAddress => unreachable, | ||
| 2569 | else => |e| return e, | ||
| 2570 | }; | ||
| 2571 | } | ||
| 2572 | |||
| 2573 | pub const PollError = error{ | 1548 | pub const PollError = error{ |
| 2574 | /// The network subsystem has failed. | 1549 | /// The network subsystem has failed. |
| 2575 | NetworkDown, | 1550 | NetworkDown, |
| ... | @@ -2633,139 +1608,6 @@ pub fn ppoll(fds: []pollfd, timeout: ?*const timespec, mask: ?*const sigset_t) P | ... | @@ -2633,139 +1608,6 @@ pub fn ppoll(fds: []pollfd, timeout: ?*const timespec, mask: ?*const sigset_t) P |
| 2633 | } | 1608 | } |
| 2634 | } | 1609 | } |
| 2635 | 1610 | ||
| 2636 | pub const RecvFromError = error{ | ||
| 2637 | /// The socket is marked nonblocking and the requested operation would block, and | ||
| 2638 | /// there is no global event loop configured. | ||
| 2639 | WouldBlock, | ||
| 2640 | |||
| 2641 | /// A remote host refused to allow the network connection, typically because it is not | ||
| 2642 | /// running the requested service. | ||
| 2643 | ConnectionRefused, | ||
| 2644 | |||
| 2645 | /// Could not allocate kernel memory. | ||
| 2646 | SystemResources, | ||
| 2647 | |||
| 2648 | ConnectionResetByPeer, | ||
| 2649 | Timeout, | ||
| 2650 | |||
| 2651 | /// The socket has not been bound. | ||
| 2652 | SocketNotBound, | ||
| 2653 | |||
| 2654 | /// The UDP message was too big for the buffer and part of it has been discarded | ||
| 2655 | MessageOversize, | ||
| 2656 | |||
| 2657 | /// The network subsystem has failed. | ||
| 2658 | NetworkDown, | ||
| 2659 | |||
| 2660 | /// The socket is not connected (connection-oriented sockets only). | ||
| 2661 | SocketUnconnected, | ||
| 2662 | |||
| 2663 | /// The other end closed the socket unexpectedly or a read is executed on a shut down socket | ||
| 2664 | BrokenPipe, | ||
| 2665 | } || UnexpectedError; | ||
| 2666 | |||
| 2667 | pub fn recv(sock: socket_t, buf: []u8, flags: u32) RecvFromError!usize { | ||
| 2668 | return recvfrom(sock, buf, flags, null, null); | ||
| 2669 | } | ||
| 2670 | |||
| 2671 | /// If `sockfd` is opened in non blocking mode, the function will | ||
| 2672 | /// return error.WouldBlock when EAGAIN is received. | ||
| 2673 | pub fn recvfrom( | ||
| 2674 | sockfd: socket_t, | ||
| 2675 | buf: []u8, | ||
| 2676 | flags: u32, | ||
| 2677 | src_addr: ?*sockaddr, | ||
| 2678 | addrlen: ?*socklen_t, | ||
| 2679 | ) RecvFromError!usize { | ||
| 2680 | while (true) { | ||
| 2681 | const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen); | ||
| 2682 | if (native_os == .windows) { | ||
| 2683 | if (rc == windows.ws2_32.SOCKET_ERROR) { | ||
| 2684 | switch (windows.ws2_32.WSAGetLastError()) { | ||
| 2685 | .NOTINITIALISED => unreachable, | ||
| 2686 | .ECONNRESET => return error.ConnectionResetByPeer, | ||
| 2687 | .EINVAL => return error.SocketNotBound, | ||
| 2688 | .EMSGSIZE => return error.MessageOversize, | ||
| 2689 | .ENETDOWN => return error.NetworkDown, | ||
| 2690 | .ENOTCONN => return error.SocketUnconnected, | ||
| 2691 | .EWOULDBLOCK => return error.WouldBlock, | ||
| 2692 | .ETIMEDOUT => return error.Timeout, | ||
| 2693 | // TODO: handle more errors | ||
| 2694 | else => |err| return windows.unexpectedWSAError(err), | ||
| 2695 | } | ||
| 2696 | } else { | ||
| 2697 | return @intCast(rc); | ||
| 2698 | } | ||
| 2699 | } else { | ||
| 2700 | switch (errno(rc)) { | ||
| 2701 | .SUCCESS => return @intCast(rc), | ||
| 2702 | .BADF => unreachable, // always a race condition | ||
| 2703 | .FAULT => unreachable, | ||
| 2704 | .INVAL => unreachable, | ||
| 2705 | .NOTCONN => return error.SocketUnconnected, | ||
| 2706 | .NOTSOCK => unreachable, | ||
| 2707 | .INTR => continue, | ||
| 2708 | .AGAIN => return error.WouldBlock, | ||
| 2709 | .NOMEM => return error.SystemResources, | ||
| 2710 | .CONNREFUSED => return error.ConnectionRefused, | ||
| 2711 | .CONNRESET => return error.ConnectionResetByPeer, | ||
| 2712 | .TIMEDOUT => return error.Timeout, | ||
| 2713 | .PIPE => return error.BrokenPipe, | ||
| 2714 | else => |err| return unexpectedErrno(err), | ||
| 2715 | } | ||
| 2716 | } | ||
| 2717 | } | ||
| 2718 | } | ||
| 2719 | |||
| 2720 | pub const RecvMsgError = RecvFromError || error{ | ||
| 2721 | /// Reception of SCM_RIGHTS fds via ancillary data in msg.control would | ||
| 2722 | /// exceed some system limit (generally this is retryable by trying to | ||
| 2723 | /// receive fewer fds or closing some existing fds) | ||
| 2724 | SystemFdQuotaExceeded, | ||
| 2725 | |||
| 2726 | /// Reception of SCM_RIGHTS fds via ancillary data in msg.control would | ||
| 2727 | /// exceed some process limit (generally this is retryable by trying to | ||
| 2728 | /// receive fewer fds, closing some existing fds, or changing the ulimit) | ||
| 2729 | ProcessFdQuotaExceeded, | ||
| 2730 | }; | ||
| 2731 | |||
| 2732 | /// If `sockfd` is opened in non blocking mode, the function will | ||
| 2733 | /// return error.WouldBlock when EAGAIN is received. | ||
| 2734 | pub fn recvmsg( | ||
| 2735 | /// The file descriptor of the sending socket. | ||
| 2736 | sockfd: socket_t, | ||
| 2737 | /// Message header and iovecs | ||
| 2738 | msg: *msghdr, | ||
| 2739 | flags: u32, | ||
| 2740 | ) RecvMsgError!usize { | ||
| 2741 | if (@TypeOf(system.recvmsg) == void) | ||
| 2742 | @compileError("recvmsg() not supported on this OS"); | ||
| 2743 | while (true) { | ||
| 2744 | const rc = system.recvmsg(sockfd, msg, flags); | ||
| 2745 | switch (errno(rc)) { | ||
| 2746 | .SUCCESS => return @intCast(rc), | ||
| 2747 | .AGAIN => return error.WouldBlock, | ||
| 2748 | .BADF => unreachable, // always a race condition | ||
| 2749 | .NFILE => return error.SystemFdQuotaExceeded, | ||
| 2750 | .MFILE => return error.ProcessFdQuotaExceeded, | ||
| 2751 | .INTR => continue, | ||
| 2752 | .FAULT => unreachable, // An invalid user space address was specified for an argument. | ||
| 2753 | .INVAL => unreachable, // Invalid argument passed. | ||
| 2754 | .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified | ||
| 2755 | .NOBUFS => return error.SystemResources, | ||
| 2756 | .NOMEM => return error.SystemResources, | ||
| 2757 | .NOTCONN => return error.SocketUnconnected, | ||
| 2758 | .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2759 | .MSGSIZE => return error.MessageOversize, | ||
| 2760 | .PIPE => return error.BrokenPipe, | ||
| 2761 | .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type. | ||
| 2762 | .CONNRESET => return error.ConnectionResetByPeer, | ||
| 2763 | .NETDOWN => return error.NetworkDown, | ||
| 2764 | else => |err| return unexpectedErrno(err), | ||
| 2765 | } | ||
| 2766 | } | ||
| 2767 | } | ||
| 2768 | |||
| 2769 | pub const SetSockOptError = error{ | 1611 | pub const SetSockOptError = error{ |
| 2770 | /// The socket is already connected, and a specified option cannot be set while the socket is connected. | 1612 | /// The socket is already connected, and a specified option cannot be set while the socket is connected. |
| 2771 | AlreadyConnected, | 1613 | AlreadyConnected, |
| ... | @@ -2957,20 +1799,6 @@ pub fn tcsetpgrp(handle: fd_t, pgrp: pid_t) TermioSetPgrpError!void { | ... | @@ -2957,20 +1799,6 @@ pub fn tcsetpgrp(handle: fd_t, pgrp: pid_t) TermioSetPgrpError!void { |
| 2957 | } | 1799 | } |
| 2958 | } | 1800 | } |
| 2959 | 1801 | ||
| 2960 | pub const SetSidError = error{ | ||
| 2961 | /// The calling process is already a process group leader, or the process group ID of a process other than the calling process matches the process ID of the calling process. | ||
| 2962 | PermissionDenied, | ||
| 2963 | } || UnexpectedError; | ||
| 2964 | |||
| 2965 | pub fn setsid() SetSidError!pid_t { | ||
| 2966 | const rc = system.setsid(); | ||
| 2967 | switch (errno(rc)) { | ||
| 2968 | .SUCCESS => return @intCast(rc), | ||
| 2969 | .PERM => return error.PermissionDenied, | ||
| 2970 | else => |err| return unexpectedErrno(err), | ||
| 2971 | } | ||
| 2972 | } | ||
| 2973 | |||
| 2974 | pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t { | 1802 | pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t { |
| 2975 | const rc = system.signalfd(fd, mask, flags); | 1803 | const rc = system.signalfd(fd, mask, flags); |
| 2976 | switch (errno(rc)) { | 1804 | switch (errno(rc)) { |
lib/std/posix/test.zig+25-16| ... | @@ -35,14 +35,14 @@ test "check WASI CWD" { | ... | @@ -35,14 +35,14 @@ test "check WASI CWD" { |
| 35 | 35 | ||
| 36 | test "getuid" { | 36 | test "getuid" { |
| 37 | if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; | 37 | if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; |
| 38 | _ = posix.getuid(); | 38 | _ = posix.system.getuid(); |
| 39 | _ = posix.geteuid(); | 39 | _ = posix.system.geteuid(); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | test "getgid" { | 42 | test "getgid" { |
| 43 | if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; | 43 | if (native_os == .windows or native_os == .wasi) return error.SkipZigTest; |
| 44 | _ = posix.getgid(); | 44 | _ = posix.system.getgid(); |
| 45 | _ = posix.getegid(); | 45 | _ = posix.system.getegid(); |
| 46 | } | 46 | } |
| 47 | 47 | ||
| 48 | test "sigaltstack" { | 48 | test "sigaltstack" { |
| ... | @@ -121,13 +121,18 @@ test "pipe" { | ... | @@ -121,13 +121,18 @@ test "pipe" { |
| 121 | if (native_os == .windows or native_os == .wasi) | 121 | if (native_os == .windows or native_os == .wasi) |
| 122 | return error.SkipZigTest; | 122 | return error.SkipZigTest; |
| 123 | 123 | ||
| 124 | const io = testing.io; | ||
| 125 | |||
| 124 | const fds = try std.Io.Threaded.pipe2(.{}); | 126 | const fds = try std.Io.Threaded.pipe2(.{}); |
| 125 | try expect((try posix.write(fds[1], "hello")) == 5); | 127 | const out: Io.File = .{ .handle = fds[0] }; |
| 128 | const in: Io.File = .{ .handle = fds[1] }; | ||
| 129 | try in.writeStreamingAll(io, "hello"); | ||
| 126 | var buf: [16]u8 = undefined; | 130 | var buf: [16]u8 = undefined; |
| 127 | try expect((try posix.read(fds[0], buf[0..])) == 5); | 131 | try expect((try out.readStreaming(io, &.{&buf})) == 5); |
| 132 | |||
| 128 | try expectEqualSlices(u8, buf[0..5], "hello"); | 133 | try expectEqualSlices(u8, buf[0..5], "hello"); |
| 129 | posix.close(fds[1]); | 134 | out.close(io); |
| 130 | posix.close(fds[0]); | 135 | in.close(io); |
| 131 | } | 136 | } |
| 132 | 137 | ||
| 133 | test "memfd_create" { | 138 | test "memfd_create" { |
| ... | @@ -458,8 +463,12 @@ test "rename smoke test" { | ... | @@ -458,8 +463,12 @@ test "rename smoke test" { |
| 458 | // Create some file using `open`. | 463 | // Create some file using `open`. |
| 459 | const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" }); | 464 | const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" }); |
| 460 | defer gpa.free(file_path); | 465 | defer gpa.free(file_path); |
| 461 | const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode); | 466 | const file = try Io.Dir.cwd().createFile(io, file_path, .{ |
| 462 | posix.close(fd); | 467 | .read = true, |
| 468 | .exclusive = true, | ||
| 469 | .permissions = .fromMode(mode), | ||
| 470 | }); | ||
| 471 | file.close(io); | ||
| 463 | 472 | ||
| 464 | // Rename the file | 473 | // Rename the file |
| 465 | const new_file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" }); | 474 | const new_file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" }); |
| ... | @@ -471,15 +480,15 @@ test "rename smoke test" { | ... | @@ -471,15 +480,15 @@ test "rename smoke test" { |
| 471 | // Try opening renamed file | 480 | // Try opening renamed file |
| 472 | const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" }); | 481 | const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" }); |
| 473 | defer gpa.free(file_path); | 482 | defer gpa.free(file_path); |
| 474 | const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR }, mode); | 483 | const file = try Io.Dir.cwd().openFile(io, file_path, .{ .mode = .read_write }); |
| 475 | posix.close(fd); | 484 | file.close(io); |
| 476 | } | 485 | } |
| 477 | 486 | ||
| 478 | { | 487 | { |
| 479 | // Try opening original file - should fail with error.FileNotFound | 488 | // Try opening original file - should fail with error.FileNotFound |
| 480 | const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" }); | 489 | const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" }); |
| 481 | defer gpa.free(file_path); | 490 | defer gpa.free(file_path); |
| 482 | try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode)); | 491 | try expectError(error.FileNotFound, Io.Dir.cwd().openFile(io, file_path, .{ .mode = .read_write })); |
| 483 | } | 492 | } |
| 484 | 493 | ||
| 485 | { | 494 | { |
| ... | @@ -498,15 +507,15 @@ test "rename smoke test" { | ... | @@ -498,15 +507,15 @@ test "rename smoke test" { |
| 498 | // Try opening renamed directory | 507 | // Try opening renamed directory |
| 499 | const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_dir" }); | 508 | const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_dir" }); |
| 500 | defer gpa.free(file_path); | 509 | defer gpa.free(file_path); |
| 501 | const fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode); | 510 | const dir = try Io.Dir.cwd().openDir(io, file_path, .{}); |
| 502 | posix.close(fd); | 511 | dir.close(io); |
| 503 | } | 512 | } |
| 504 | 513 | ||
| 505 | { | 514 | { |
| 506 | // Try opening original directory - should fail with error.FileNotFound | 515 | // Try opening original directory - should fail with error.FileNotFound |
| 507 | const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" }); | 516 | const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" }); |
| 508 | defer gpa.free(file_path); | 517 | defer gpa.free(file_path); |
| 509 | try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode)); | 518 | try expectError(error.FileNotFound, Io.Dir.cwd().openDir(io, file_path, .{})); |
| 510 | } | 519 | } |
| 511 | } | 520 | } |
| 512 | 521 |
lib/std/testing.zig+2-2| ... | @@ -618,7 +618,7 @@ pub const TmpDir = struct { | ... | @@ -618,7 +618,7 @@ pub const TmpDir = struct { |
| 618 | sub_path: [sub_path_len]u8, | 618 | sub_path: [sub_path_len]u8, |
| 619 | 619 | ||
| 620 | const random_bytes_count = 12; | 620 | const random_bytes_count = 12; |
| 621 | const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count); | 621 | const sub_path_len = std.base64.url_safe.Encoder.calcSize(random_bytes_count); |
| 622 | 622 | ||
| 623 | pub fn cleanup(self: *TmpDir) void { | 623 | pub fn cleanup(self: *TmpDir) void { |
| 624 | self.dir.close(io); | 624 | self.dir.close(io); |
| ... | @@ -633,7 +633,7 @@ pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir { | ... | @@ -633,7 +633,7 @@ pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir { |
| 633 | var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; | 633 | var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; |
| 634 | io.random(&random_bytes); | 634 | io.random(&random_bytes); |
| 635 | var sub_path: [TmpDir.sub_path_len]u8 = undefined; | 635 | var sub_path: [TmpDir.sub_path_len]u8 = undefined; |
| 636 | _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); | 636 | _ = std.base64.url_safe.Encoder.encode(&sub_path, &random_bytes); |
| 637 | 637 | ||
| 638 | const cwd = Io.Dir.cwd(); | 638 | const cwd = Io.Dir.cwd(); |
| 639 | var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch | 639 | var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch |
test/cases/function_pointers.zig deleted-30| ... | @@ -1,30 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | const PrintFn = *const fn () void; | ||
| 4 | |||
| 5 | pub fn main() void { | ||
| 6 | var printFn: PrintFn = stopSayingThat; | ||
| 7 | var i: u32 = 0; | ||
| 8 | while (i < 4) : (i += 1) printFn(); | ||
| 9 | |||
| 10 | printFn = moveEveryZig; | ||
| 11 | printFn(); | ||
| 12 | } | ||
| 13 | |||
| 14 | fn stopSayingThat() void { | ||
| 15 | _ = std.posix.write(1, "Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n") catch {}; | ||
| 16 | } | ||
| 17 | |||
| 18 | fn moveEveryZig() void { | ||
| 19 | _ = std.posix.write(1, "All your codebase are belong to us\n") catch {}; | ||
| 20 | } | ||
| 21 | |||
| 22 | // run | ||
| 23 | // target=x86_64-macos | ||
| 24 | // | ||
| 25 | // Hello, my name is Inigo Montoya; you killed my father, prepare to die. | ||
| 26 | // Hello, my name is Inigo Montoya; you killed my father, prepare to die. | ||
| 27 | // Hello, my name is Inigo Montoya; you killed my father, prepare to die. | ||
| 28 | // Hello, my name is Inigo Montoya; you killed my father, prepare to die. | ||
| 29 | // All your codebase are belong to us | ||
| 30 | // | ||
test/cases/print_u32s.zig deleted-28| ... | @@ -1,28 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | pub fn main() void { | ||
| 4 | printNumberHex(0x00000000); | ||
| 5 | printNumberHex(0xaaaaaaaa); | ||
| 6 | printNumberHex(0xdeadbeef); | ||
| 7 | printNumberHex(0x31415926); | ||
| 8 | } | ||
| 9 | |||
| 10 | fn printNumberHex(x: u32) void { | ||
| 11 | const digit_chars = "0123456789abcdef"; | ||
| 12 | var i: u5 = 28; | ||
| 13 | while (true) : (i -= 4) { | ||
| 14 | const digit = (x >> i) & 0xf; | ||
| 15 | _ = std.posix.write(1, &.{digit_chars[digit]}) catch {}; | ||
| 16 | if (i == 0) break; | ||
| 17 | } | ||
| 18 | _ = std.posix.write(1, "\n") catch {}; | ||
| 19 | } | ||
| 20 | |||
| 21 | // run | ||
| 22 | // target=x86_64-macos | ||
| 23 | // | ||
| 24 | // 00000000 | ||
| 25 | // aaaaaaaa | ||
| 26 | // deadbeef | ||
| 27 | // 31415926 | ||
| 28 | // | ||