Merge branch "remove many std.posix functions"

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30741
This commit is contained in:
Andrew Kelley 2026-01-08 00:19:12 -08:00
commit 6f7968f165
15 changed files with 314 additions and 1520 deletions

View file

@ -1219,12 +1219,12 @@ pub fn getDepFileName(d: *Driver, source: Source, buf: *[std.fs.max_name_bytes]u
fn getRandomFilename(d: *Driver, buf: *[std.fs.max_name_bytes]u8, extension: []const u8) ![]const u8 {
const io = d.comp.io;
const random_bytes_count = 12;
const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);
const sub_path_len = comptime std.base64.url_safe.Encoder.calcSize(random_bytes_count);
var random_bytes: [random_bytes_count]u8 = undefined;
io.random(&random_bytes);
var random_name: [sub_path_len]u8 = undefined;
_ = std.fs.base64_encoder.encode(&random_name, &random_bytes);
_ = std.base64.url_safe.Encoder.encode(&random_name, &random_bytes);
const fmt_template = "/tmp/{s}{s}";
const fmt_args = .{

View file

@ -1,5 +1,7 @@
const builtin = @import("builtin");
const std = @import("../std.zig");
const Io = std.Io;
const Step = std.Build.Step;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
@ -666,7 +668,7 @@ const Os = switch (builtin.os.tag) {
.dir_table = .{},
.dir_count = 0,
.os = .{
.kq_fd = try posix.kqueue(),
.kq_fd = try Io.Kqueue.createFileDescriptor(),
.handles = .empty,
},
.generation = 0,
@ -697,7 +699,7 @@ const Os = switch (builtin.os.tag) {
.data = 0,
.udata = gop.index,
}};
_ = try posix.kevent(w.os.kq_fd, &changes, &.{}, null);
_ = try Io.Kqueue.kevent(w.os.kq_fd, &changes, &.{}, null);
assert(handles.len == gop.index);
try handles.append(gpa, .{
.rs = .{},
@ -787,7 +789,7 @@ const Os = switch (builtin.os.tag) {
},
};
const filtered_changes = if (i == handles.len - 1) changes[0..1] else &changes;
_ = try posix.kevent(w.os.kq_fd, filtered_changes, &.{}, null);
_ = try Io.Kqueue.kevent(w.os.kq_fd, filtered_changes, &.{}, null);
if (path.sub_path.len != 0) posix.close(dir_fd);
w.dir_table.swapRemoveAt(i);
@ -801,13 +803,13 @@ const Os = switch (builtin.os.tag) {
fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
var timespec_buffer: posix.timespec = undefined;
var event_buffer: [100]posix.Kevent = undefined;
var n = try posix.kevent(w.os.kq_fd, &.{}, &event_buffer, timeout.toTimespec(&timespec_buffer));
var n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, timeout.toTimespec(&timespec_buffer));
if (n == 0) return .timeout;
const reaction_sets = w.os.handles.items(.rs);
var any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], false);
timespec_buffer = .{ .sec = 0, .nsec = 0 };
while (n == event_buffer.len) {
n = try posix.kevent(w.os.kq_fd, &.{}, &event_buffer, &timespec_buffer);
n = try Io.Kqueue.kevent(w.os.kq_fd, &.{}, &event_buffer, &timespec_buffer);
if (n == 0) break;
any_dirty = markDirtySteps(gpa, reaction_sets, event_buffer[0..n], any_dirty);
}

View file

@ -540,18 +540,20 @@ test {
const Io = @This();
pub const Threaded = @import("Io/Threaded.zig");
pub const Evented = switch (builtin.os.tag) {
.linux => switch (builtin.cpu.arch) {
.x86_64, .aarch64 => @import("Io/IoUring.zig"),
.x86_64, .aarch64 => IoUring,
else => void, // context-switching code not implemented yet
},
.dragonfly, .freebsd, .netbsd, .openbsd, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (builtin.cpu.arch) {
.x86_64, .aarch64 => @import("Io/Kqueue.zig"),
.x86_64, .aarch64 => Kqueue,
else => void, // context-switching code not implemented yet
},
else => void,
};
pub const Threaded = @import("Io/Threaded.zig");
pub const Kqueue = @import("Io/Kqueue.zig");
pub const IoUring = @import("Io/IoUring.zig");
pub const net = @import("Io/net.zig");
userdata: ?*anyopaque,
@ -1356,7 +1358,7 @@ pub const Mutex = extern struct {
pub const init: Mutex = .{ .state = .init(.unlocked) };
const State = enum(u32) {
pub const State = enum(u32) {
unlocked,
locked_once,
contended,

View file

@ -157,8 +157,11 @@ pub const InitOptions = struct {
n_threads: ?usize = null,
};
pub const InitError = Allocator.Error || CreateFileDescriptorError;
pub fn init(k: *Kqueue, gpa: Allocator, options: InitOptions) !void {
assert(options.n_threads != 0);
const n_threads = @max(1, options.n_threads orelse std.Thread.getCpuCount() catch 1);
const threads_size = n_threads * @sizeOf(Thread);
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 {
},
.current_context = &main_fiber.context,
.ready_queue = null,
.kq_fd = try posix.kqueue(),
.kq_fd = try createFileDescriptor(),
.idle_search_index = 1,
.steal_ready_search_index = 1,
.wait_queues = .empty,
@ -231,6 +234,23 @@ pub fn deinit(k: *Kqueue) void {
k.* = undefined;
}
pub const CreateFileDescriptorError = error{
/// The per-process limit on the number of open file descriptors has been reached.
ProcessFdQuotaExceeded,
/// The system-wide limit on the total number of open files has been reached.
SystemFdQuotaExceeded,
} || Io.UnexpectedError;
pub fn createFileDescriptor() CreateFileDescriptorError!posix.fd_t {
const rc = posix.system.kqueue();
switch (posix.errno(rc)) {
.SUCCESS => return @intCast(rc),
.MFILE => return error.ProcessFdQuotaExceeded,
.NFILE => return error.SystemFdQuotaExceeded,
else => |err| return posix.unexpectedErrno(err),
}
}
fn findReadyFiber(k: *Kqueue, thread: *Thread) ?*Fiber {
if (@atomicRmw(?*Fiber, &thread.ready_queue, .Xchg, Fiber.finished, .acquire)) |ready_fiber| {
@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 {
},
};
// If an error occurs it only pessimises scheduling.
_ = posix.kevent(idle_search_thread.kq_fd, &changes, &.{}, null) catch {};
_ = kevent(idle_search_thread.kq_fd, &changes, &.{}, null) catch |err| {
// TODO handle EINTR for cancellation purposes
@panic(@errorName(err)); // TODO
};
return;
}
spawn_thread: {
@ -334,7 +357,7 @@ fn schedule(k: *Kqueue, thread: *Thread, ready_queue: Fiber.Queue) void {
.idle_context = undefined,
.current_context = &new_thread.idle_context,
.ready_queue = ready_queue.head,
.kq_fd = posix.kqueue() catch |err| {
.kq_fd = createFileDescriptor() catch |err| {
@atomicStore(u32, &k.threads.reserved, new_thread_index, .release);
// no more access to `thread` after giving up reservation
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 {
k.yield(ready_fiber, .nothing);
maybe_ready_fiber = null;
}
const n = posix.kevent(thread.kq_fd, &.{}, &events_buffer, null) catch |err| {
const n = kevent(thread.kq_fd, &.{}, &events_buffer, null) catch |err| {
// TODO handle EINTR for cancellation purposes
@panic(@errorName(err));
@panic(@errorName(err)); // TODO
};
var maybe_ready_queue: ?Fiber.Queue = null;
for (events_buffer[0..n]) |event| switch (@as(Completion.UserData, @enumFromInt(event.udata))) {
@ -471,14 +494,6 @@ const SwitchMessage = struct {
recycle: *Fiber,
register_awaiter: *?*Fiber,
register_select: []const *Io.AnyFuture,
mutex_lock: struct {
prev_state: Io.Mutex.State,
mutex: *Io.Mutex,
},
condition_wait: struct {
cond: *Io.Condition,
mutex: *Io.Mutex,
},
exit,
};
@ -514,59 +529,6 @@ const SwitchMessage = struct {
}
}
},
.mutex_lock => |mutex_lock| {
const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev));
assert(prev_fiber.queue_next == null);
var prev_state = mutex_lock.prev_state;
while (switch (prev_state) {
else => next_state: {
prev_fiber.queue_next = @ptrFromInt(@intFromEnum(prev_state));
break :next_state @cmpxchgWeak(
Io.Mutex.State,
&mutex_lock.mutex.state,
prev_state,
@enumFromInt(@intFromPtr(prev_fiber)),
.release,
.acquire,
);
},
.unlocked => @cmpxchgWeak(
Io.Mutex.State,
&mutex_lock.mutex.state,
.unlocked,
.locked_once,
.acquire,
.acquire,
) orelse {
prev_fiber.queue_next = null;
k.schedule(thread, .{ .head = prev_fiber, .tail = prev_fiber });
return;
},
}) |next_state| prev_state = next_state;
},
.condition_wait => |condition_wait| {
const prev_fiber: *Fiber = @alignCast(@fieldParentPtr("context", message.contexts.prev));
assert(prev_fiber.queue_next == null);
const cond_impl = prev_fiber.resultPointer(Condition);
cond_impl.* = .{
.tail = prev_fiber,
.event = .queued,
};
if (@cmpxchgStrong(
?*Fiber,
@as(*?*Fiber, @ptrCast(&condition_wait.cond.state)),
null,
prev_fiber,
.release,
.acquire,
)) |waiting_fiber| {
const waiting_cond_impl = waiting_fiber.?.resultPointer(Condition);
assert(waiting_cond_impl.tail.queue_next == null);
waiting_cond_impl.tail.queue_next = prev_fiber;
waiting_cond_impl.tail = prev_fiber;
}
condition_wait.mutex.unlock(k.io());
},
.exit => for (k.threads.allocated[0..@atomicLoad(u32, &k.threads.active, .acquire)]) |*each_thread| {
const changes = [_]posix.Kevent{
.{
@ -578,8 +540,9 @@ const SwitchMessage = struct {
.udata = @intFromEnum(Completion.UserData.exit),
},
};
_ = posix.kevent(each_thread.kq_fd, &changes, &.{}, null) catch |err| {
@panic(@errorName(err));
_ = kevent(each_thread.kq_fd, &changes, &.{}, null) catch |err| {
// TODO handle EINTR for cancellation purposes
@panic(@errorName(err)); // TODO
};
},
}
@ -854,21 +817,13 @@ pub fn io(k: *Kqueue) Io {
.concurrent = concurrent,
.await = await,
.cancel = cancel,
.cancelRequested = cancelRequested,
.select = select,
.groupAsync = groupAsync,
.groupWait = groupWait,
.groupConcurrent = groupConcurrent,
.groupAwait = groupAwait,
.groupCancel = groupCancel,
.mutexLock = mutexLock,
.mutexLockUncancelable = mutexLockUncancelable,
.mutexUnlock = mutexUnlock,
.conditionWait = conditionWait,
.conditionWaitUncancelable = conditionWaitUncancelable,
.conditionWake = conditionWake,
.dirCreateDir = dirCreateDir,
.dirCreateDirPath = dirCreateDirPath,
.dirCreateDirPathOpen = dirCreateDirPathOpen,
@ -888,7 +843,6 @@ pub fn io(k: *Kqueue) Io {
.fileReadPositional = fileReadPositional,
.fileSeekBy = fileSeekBy,
.fileSeekTo = fileSeekTo,
.openExecutable = openExecutable,
.now = now,
.sleep = sleep,
@ -1013,25 +967,41 @@ fn cancelRequested(userdata: ?*anyopaque) bool {
fn groupAsync(
userdata: ?*anyopaque,
group: *Io.Group,
type_erased: *Io.Group,
context: []const u8,
context_alignment: std.mem.Alignment,
start: *const fn (*Io.Group, context: *const anyopaque) void,
context_alignment: Alignment,
start: *const fn (context: *const anyopaque) Io.Cancelable!void,
) void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = group;
_ = type_erased;
_ = context;
_ = context_alignment;
_ = start;
@panic("TODO");
}
fn groupWait(userdata: ?*anyopaque, group: *Io.Group, token: *anyopaque) void {
fn groupConcurrent(
userdata: ?*anyopaque,
type_erased: *Io.Group,
context: []const u8,
context_alignment: Alignment,
start: *const fn (context: *const anyopaque) Io.Cancelable!void,
) Io.ConcurrentError!void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = group;
_ = token;
_ = type_erased;
_ = context;
_ = context_alignment;
_ = start;
@panic("TODO");
}
fn groupAwait(userdata: ?*anyopaque, type_erased: *Io.Group, initial_token: *anyopaque) Io.Cancelable!void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = type_erased;
_ = initial_token;
@panic("TODO");
}
@ -1050,102 +1020,58 @@ fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) Io.Cancelable!u
@panic("TODO");
}
fn mutexLock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) Io.Cancelable!void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = prev_state;
_ = mutex;
@panic("TODO");
}
fn mutexLockUncancelable(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = prev_state;
_ = mutex;
@panic("TODO");
}
fn mutexUnlock(userdata: ?*anyopaque, prev_state: Io.Mutex.State, mutex: *Io.Mutex) void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = prev_state;
_ = mutex;
@panic("TODO");
}
fn conditionWait(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) Io.Cancelable!void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
k.yield(null, .{ .condition_wait = .{ .cond = cond, .mutex = mutex } });
const thread = Thread.current();
const fiber = thread.currentFiber();
const cond_impl = fiber.resultPointer(Condition);
try mutex.lock(k.io());
switch (cond_impl.event) {
.queued => {},
.wake => |wake| if (fiber.queue_next) |next_fiber| switch (wake) {
.one => if (@cmpxchgStrong(
?*Fiber,
@as(*?*Fiber, @ptrCast(&cond.state)),
null,
next_fiber,
.release,
.acquire,
)) |old_fiber| {
const old_cond_impl = old_fiber.?.resultPointer(Condition);
assert(old_cond_impl.tail.queue_next == null);
old_cond_impl.tail.queue_next = next_fiber;
old_cond_impl.tail = cond_impl.tail;
},
.all => k.schedule(thread, .{ .head = next_fiber, .tail = cond_impl.tail }),
},
}
fiber.queue_next = null;
}
fn conditionWaitUncancelable(userdata: ?*anyopaque, cond: *Io.Condition, mutex: *Io.Mutex) void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = cond;
_ = mutex;
@panic("TODO");
}
fn conditionWake(userdata: ?*anyopaque, cond: *Io.Condition, wake: Io.Condition.Wake) void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
const waiting_fiber = @atomicRmw(?*Fiber, @as(*?*Fiber, @ptrCast(&cond.state)), .Xchg, null, .acquire) orelse return;
waiting_fiber.resultPointer(Condition).event = .{ .wake = wake };
k.yield(waiting_fiber, .reschedule);
}
fn dirCreateDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.CreateDirError!void {
fn dirCreateDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = dir;
_ = sub_path;
_ = mode;
_ = permissions;
@panic("TODO");
}
fn dirCreateDirPath(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, mode: Dir.Mode) Dir.CreateDirError!void {
fn dirCreateDirPath(
userdata: ?*anyopaque,
dir: Dir,
sub_path: []const u8,
permissions: Dir.Permissions,
) Dir.CreateDirPathError!Dir.CreatePathStatus {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = dir;
_ = sub_path;
_ = mode;
_ = permissions;
@panic("TODO");
}
fn dirCreateDirPathOpen(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.OpenOptions) Dir.CreateDirPathOpenError!Dir {
fn dirCreateDirPathOpen(
userdata: ?*anyopaque,
dir: Dir,
sub_path: []const u8,
permissions: Dir.Permissions,
options: Dir.OpenOptions,
) Dir.CreateDirPathOpenError!Dir {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = dir;
_ = sub_path;
_ = permissions;
_ = options;
@panic("TODO");
}
fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = dir;
@panic("TODO");
}
fn dirStatFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.StatPathOptions) Dir.StatFileError!File.Stat {
fn dirStatFile(
userdata: ?*anyopaque,
dir: Dir,
sub_path: []const u8,
options: Dir.StatFileOptions,
) Dir.StatFileError!File.Stat {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = dir;
@ -1185,10 +1111,10 @@ fn dirOpenDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Di
_ = options;
@panic("TODO");
}
fn dirClose(userdata: ?*anyopaque, dir: Dir) void {
fn dirClose(userdata: ?*anyopaque, dirs: []const Dir) void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = dir;
_ = dirs;
@panic("TODO");
}
fn fileStat(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
@ -1197,35 +1123,57 @@ fn fileStat(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
_ = file;
@panic("TODO");
}
fn fileClose(userdata: ?*anyopaque, file: File) void {
fn fileClose(userdata: ?*anyopaque, files: []const File) void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = file;
_ = files;
@panic("TODO");
}
fn fileWriteStreaming(userdata: ?*anyopaque, file: File, buffer: [][]const u8) File.WriteStreamingError!usize {
fn fileWriteStreaming(
userdata: ?*anyopaque,
file: File,
header: []const u8,
data: []const []const u8,
splat: usize,
) File.Writer.Error!usize {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = file;
_ = buffer;
_ = header;
_ = data;
_ = splat;
@panic("TODO");
}
fn fileWritePositional(userdata: ?*anyopaque, file: File, buffer: [][]const u8, offset: u64) File.WritePositionalError!usize {
fn fileWritePositional(
userdata: ?*anyopaque,
file: File,
header: []const u8,
data: []const []const u8,
splat: usize,
offset: u64,
) File.WritePositionalError!usize {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = file;
_ = buffer;
_ = header;
_ = data;
_ = splat;
_ = offset;
@panic("TODO");
}
fn fileReadStreaming(userdata: ?*anyopaque, file: File, data: [][]u8) File.Reader.Error!usize {
fn fileReadStreaming(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = file;
_ = data;
@panic("TODO");
}
fn fileReadPositional(userdata: ?*anyopaque, file: File, data: [][]u8, offset: u64) File.ReadPositionalError!usize {
fn fileReadPositional(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = file;
@ -1247,12 +1195,6 @@ fn fileSeekTo(userdata: ?*anyopaque, file: File, absolute_offset: u64) File.Seek
_ = absolute_offset;
@panic("TODO");
}
fn openExecutable(userdata: ?*anyopaque, file: File.OpenFlags) File.OpenExecutableError!File {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = file;
@panic("TODO");
}
fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
@ -1518,7 +1460,8 @@ fn netRead(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Strea
.udata = @intFromPtr(fiber),
},
};
assert(0 == (posix.kevent(thread.kq_fd, &changes, &.{}, null) catch |err| {
assert(0 == (kevent(thread.kq_fd, &changes, &.{}, null) catch |err| {
// TODO handle EINTR for cancellation purposes
@panic(@errorName(err)); // TODO
}));
}
@ -1551,10 +1494,10 @@ fn netWrite(userdata: ?*anyopaque, dest: net.Socket.Handle, header: []const u8,
@panic("TODO");
}
fn netClose(userdata: ?*anyopaque, handle: net.Socket.Handle) void {
fn netClose(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = handle;
_ = handles;
@panic("TODO");
}
@ -1586,13 +1529,13 @@ fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interfa
fn netLookup(
userdata: ?*anyopaque,
host_name: net.HostName,
result: *Io.Queue(net.HostName.LookupResult),
resolved: *Io.Queue(net.HostName.LookupResult),
options: net.HostName.LookupOptions,
) void {
) net.HostName.LookupError!void {
const k: *Kqueue = @ptrCast(@alignCast(userdata));
_ = k;
_ = host_name;
_ = result;
_ = resolved;
_ = options;
@panic("TODO");
}
@ -1747,10 +1690,46 @@ fn checkCancel(k: *Kqueue) error{Canceled}!void {
if (cancelRequested(k)) return error.Canceled;
}
const Condition = struct {
tail: *Fiber,
event: union(enum) {
queued,
wake: Io.Condition.Wake,
},
pub const KEventError = error{
/// The process does not have permission to register a filter.
AccessDenied,
/// The event could not be found to be modified or deleted.
EventNotFound,
/// No memory was available to register the event.
SystemResources,
/// The specified process to attach to does not exist.
ProcessNotFound,
/// changelist or eventlist had too many items on it.
/// TODO remove this possibility
Overflow,
};
pub fn kevent(
kq: i32,
changelist: []const posix.Kevent,
eventlist: []posix.Kevent,
timeout: ?*const posix.timespec,
) KEventError!usize {
while (true) {
const rc = posix.system.kevent(
kq,
changelist.ptr,
std.math.cast(c_int, changelist.len) orelse return error.Overflow,
eventlist.ptr,
std.math.cast(c_int, eventlist.len) orelse return error.Overflow,
timeout,
);
switch (posix.errno(rc)) {
.SUCCESS => return @intCast(rc),
.ACCES => return error.AccessDenied,
.FAULT => unreachable, // TODO use error.Unexpected for these
.BADF => unreachable, // Always a race condition.
.INTR => continue, // TODO handle cancelation
.INVAL => unreachable,
.NOENT => return error.EventNotFound,
.NOMEM => return error.SystemResources,
.SRCH => return error.ProcessNotFound,
else => unreachable,
}
}
}

View file

@ -310,11 +310,11 @@ test "listen on a unix socket, send bytes, receive bytes" {
fn generateFileName(io: Io, base_name: []const u8) ![]const u8 {
const random_bytes_count = 12;
const sub_path_len = comptime std.fs.base64_encoder.calcSize(random_bytes_count);
const sub_path_len = comptime std.base64.url_safe.Encoder.calcSize(random_bytes_count);
var random_bytes: [12]u8 = undefined;
io.random(&random_bytes);
var sub_path: [sub_path_len]u8 = undefined;
_ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
_ = std.base64.url_safe.Encoder.encode(&sub_path, &random_bytes);
return std.fmt.allocPrint(testing.allocator, "{s}-{s}", .{ sub_path[0..], base_name });
}

View file

@ -175,7 +175,7 @@ pub const SetNameError = error{
Unsupported,
Unexpected,
InvalidWtf8,
} || posix.PrctlError || posix.WriteError || Io.File.OpenError || std.fmt.BufPrintError;
} || posix.PrctlError || Io.File.Writer.Error || Io.File.OpenError || std.fmt.BufPrintError;
pub fn setName(self: Thread, io: Io, name: []const u8) SetNameError!void {
if (name.len > max_name_len) return error.NameTooLong;

View file

@ -148,7 +148,7 @@ const ElfDynLibError = error{
ElfHashTableNotFound,
Canceled,
Streaming,
} || posix.OpenError || posix.MMapError;
} || Io.File.OpenError || posix.MMapError;
pub const ElfDynLib = struct {
strings: [*:0]u8,
@ -177,27 +177,20 @@ pub const ElfDynLib = struct {
return parent;
}
fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t {
fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?Io.File {
var paths = std.mem.tokenizeScalar(u8, search_path, delim);
while (paths.next()) |p| {
var dir = openPath(io, p) catch continue;
defer dir.close(io);
const fd = posix.openat(dir.handle, file_name, .{
.ACCMODE = .RDONLY,
.CLOEXEC = true,
}, 0) catch continue;
return fd;
return dir.openFile(io, file_name, .{}) catch continue;
}
return null;
}
fn resolveFromParent(io: Io, dir_path: []const u8, file_name: []const u8) ?posix.fd_t {
fn resolveFromParent(io: Io, dir_path: []const u8, file_name: []const u8) ?Io.File {
var dir = Io.Dir.cwd().openDir(io, dir_path, .{}) catch return null;
defer dir.close(io);
return posix.openat(dir.handle, file_name, .{
.ACCMODE = .RDONLY,
.CLOEXEC = true,
}, 0) catch null;
return dir.openFile(io, file_name, .{}) catch null;
}
// This implements enough to be able to load system libraries in general
@ -205,10 +198,10 @@ pub const ElfDynLib = struct {
// - DT_RPATH of the calling binary is not used as a search path
// - DT_RUNPATH of the calling binary is not used as a search path
// - /etc/ld.so.cache is not read
fn resolveFromName(io: Io, path_or_name: []const u8, LD_LIBRARY_PATH: ?[]const u8) !posix.fd_t {
fn resolveFromName(io: Io, path_or_name: []const u8, LD_LIBRARY_PATH: ?[]const u8) !Io.File {
// If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname
if (std.mem.findScalarPos(u8, path_or_name, 0, '/')) |_| {
return posix.open(path_or_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
return Io.Dir.cwd().openFile(io, path_or_name, .{});
}
// Only read LD_LIBRARY_PATH if the binary is not setuid/setgid
@ -216,15 +209,15 @@ pub const ElfDynLib = struct {
std.os.linux.getegid() == std.os.linux.getgid())
{
if (LD_LIBRARY_PATH) |ld_library_path| {
if (resolveFromSearchPath(io, ld_library_path, path_or_name, ':')) |fd| {
return fd;
if (resolveFromSearchPath(io, ld_library_path, path_or_name, ':')) |file| {
return file;
}
}
}
// Lastly the directories /lib and /usr/lib are searched (in this exact order)
if (resolveFromParent(io, "/lib", path_or_name)) |fd| return fd;
if (resolveFromParent(io, "/usr/lib", path_or_name)) |fd| return fd;
if (resolveFromParent(io, "/lib", path_or_name)) |file| return file;
if (resolveFromParent(io, "/usr/lib", path_or_name)) |file| return file;
return error.FileNotFound;
}
@ -232,10 +225,9 @@ pub const ElfDynLib = struct {
pub fn open(path: []const u8, LD_LIBRARY_PATH: ?[]const u8) Error!ElfDynLib {
const io = std.Options.debug_io;
const fd = try resolveFromName(io, path, LD_LIBRARY_PATH);
defer posix.close(fd);
const file = try resolveFromName(io, path, LD_LIBRARY_PATH);
defer file.close(io);
const file: Io.File = .{ .handle = fd };
const stat = try file.stat(io);
const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig;
@ -248,7 +240,7 @@ pub const ElfDynLib = struct {
mem.alignForward(usize, size, page_size),
posix.PROT.READ,
.{ .TYPE = .PRIVATE },
fd,
file.handle,
0,
);
defer posix.munmap(file_bytes);
@ -318,7 +310,7 @@ pub const ElfDynLib = struct {
extended_memsz,
prot,
.{ .TYPE = .PRIVATE, .FIXED = true },
fd,
file.handle,
ph.p_offset - extra_bytes,
);
} else {

View file

@ -4,15 +4,12 @@ const std = @import("std.zig");
/// Deprecated, use `std.Io.Dir.path`.
pub const path = @import("fs/path.zig");
pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;
/// Base64 encoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.
pub const base64_encoder = std.base64.Base64Encoder.init(base64_alphabet, null);
/// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.
pub const base64_decoder = std.base64.Base64Decoder.init(base64_alphabet, null);
/// Deprecated, use `std.base64.url_safe_alphabet_chars`.
pub const base64_alphabet = std.base64.url_safe_alphabet_chars;
/// Deprecated, use `std.base64.url_safe.Encoder`.
pub const base64_encoder = std.base64.url_safe.Encoder;
/// Deprecated, use `std.base64.url_safe.Decoder`.
pub const base64_decoder = std.base64.url_safe.Decoder;
/// Deprecated, use `std.Io.Dir.max_path_bytes`.
pub const max_path_bytes = std.Io.Dir.max_path_bytes;
/// Deprecated, use `std.Io.Dir.max_name_bytes`.

View file

@ -1777,8 +1777,8 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
var random_bytes: [12]u8 = undefined;
io.random(&random_bytes);
var random_b64: [std.fs.base64_encoder.calcSize(random_bytes.len)]u8 = undefined;
_ = std.fs.base64_encoder.encode(&random_b64, &random_bytes);
var random_b64: [std.base64.url_safe.Encoder.calcSize(random_bytes.len)]u8 = undefined;
_ = std.base64.url_safe.Encoder.encode(&random_b64, &random_bytes);
const sub_path = random_b64 ++ "-zig-test-absolute-paths.txt";

View file

@ -529,17 +529,17 @@ test "sendmsg/recvmsg" {
.addr = @bitCast([4]u8{ 127, 0, 0, 1 }),
};
const server = try posix.socket(address_server.family, posix.SOCK.DGRAM, 0);
const server = try socket(address_server.family, posix.SOCK.DGRAM, 0);
defer posix.close(server);
try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEPORT, &mem.toBytes(@as(c_int, 1)));
try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
try posix.bind(server, addrAny(&address_server), @sizeOf(linux.sockaddr.in));
try bind(server, addrAny(&address_server), @sizeOf(linux.sockaddr.in));
// set address_server to the OS-chosen IP/port.
var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);
try posix.getsockname(server, addrAny(&address_server), &slen);
try getsockname(server, addrAny(&address_server), &slen);
const client = try posix.socket(address_server.family, posix.SOCK.DGRAM, 0);
const client = try socket(address_server.family, posix.SOCK.DGRAM, 0);
defer posix.close(client);
const buffer_send = [_]u8{42} ** 128;
@ -932,6 +932,8 @@ test "accept/connect/recv/cancel" {
}
test "register_files_update" {
const io = testing.io;
var ring = IoUring.init(1, 0) catch |err| switch (err) {
error.SystemOutdated => return error.SkipZigTest,
error.PermissionDenied => return error.SkipZigTest,
@ -939,13 +941,13 @@ test "register_files_update" {
};
defer ring.deinit();
const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
defer posix.close(fd);
const file = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{});
defer file.close(io);
var registered_fds = [_]linux.fd_t{0} ** 2;
const fd_index = 0;
const fd_index2 = 1;
registered_fds[fd_index] = fd;
registered_fds[fd_index] = file.handle;
registered_fds[fd_index2] = -1;
ring.register_files(registered_fds[0..]) catch |err| switch (err) {
@ -957,10 +959,10 @@ test "register_files_update" {
// Test IORING_REGISTER_FILES_UPDATE
// Only available since Linux 5.5
const fd2 = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
defer posix.close(fd2);
const file2 = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{});
defer file2.close(io);
registered_fds[fd_index] = fd2;
registered_fds[fd_index] = file2.handle;
registered_fds[fd_index2] = -1;
try ring.register_files_update(0, registered_fds[0..]);
@ -1031,15 +1033,15 @@ test "shutdown" {
// Socket bound, expect shutdown to work
{
const server = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
const server = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
defer posix.close(server);
try posix.setsockopt(server, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
try posix.bind(server, addrAny(&address), @sizeOf(linux.sockaddr.in));
try posix.listen(server, 1);
try bind(server, addrAny(&address), @sizeOf(linux.sockaddr.in));
try listen(server, 1);
// set address to the OS-chosen IP/port.
var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);
try posix.getsockname(server, addrAny(&address), &slen);
try getsockname(server, addrAny(&address), &slen);
const shutdown_sqe = try ring.shutdown(0x445445445, server, linux.SHUT.RD);
try testing.expectEqual(linux.IORING_OP.SHUTDOWN, shutdown_sqe.opcode);
@ -1064,7 +1066,7 @@ test "shutdown" {
// Socket not bound, expect to fail with ENOTCONN
{
const server = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
const server = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
defer posix.close(server);
const shutdown_sqe = ring.shutdown(0x445445445, server, linux.SHUT.RD) catch |err| switch (err) {
@ -1339,6 +1341,8 @@ test "linkat" {
}
test "provide_buffers: read" {
const io = testing.io;
var ring = IoUring.init(1, 0) catch |err| switch (err) {
error.SystemOutdated => return error.SkipZigTest,
error.PermissionDenied => return error.SkipZigTest,
@ -1346,8 +1350,8 @@ test "provide_buffers: read" {
};
defer ring.deinit();
const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
defer posix.close(fd);
const file = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{});
defer file.close(io);
const group_id = 1337;
const buffer_id = 0;
@ -1380,9 +1384,9 @@ test "provide_buffers: read" {
var i: usize = 0;
while (i < buffers.len) : (i += 1) {
const sqe = try ring.read(0xdededede, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
const sqe = try ring.read(0xdededede, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);
try testing.expectEqual(@as(i32, fd), sqe.fd);
try testing.expectEqual(@as(i32, file.handle), sqe.fd);
try testing.expectEqual(@as(u64, 0), sqe.addr);
try testing.expectEqual(@as(u32, buffer_len), sqe.len);
try testing.expectEqual(@as(u16, group_id), sqe.buf_index);
@ -1406,9 +1410,9 @@ test "provide_buffers: read" {
// This read should fail
{
const sqe = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
const sqe = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);
try testing.expectEqual(@as(i32, fd), sqe.fd);
try testing.expectEqual(@as(i32, file.handle), sqe.fd);
try testing.expectEqual(@as(u64, 0), sqe.addr);
try testing.expectEqual(@as(u32, buffer_len), sqe.len);
try testing.expectEqual(@as(u16, group_id), sqe.buf_index);
@ -1445,9 +1449,9 @@ test "provide_buffers: read" {
// Final read which should work
{
const sqe = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
const sqe = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
try testing.expectEqual(linux.IORING_OP.READ, sqe.opcode);
try testing.expectEqual(@as(i32, fd), sqe.fd);
try testing.expectEqual(@as(i32, file.handle), sqe.fd);
try testing.expectEqual(@as(u64, 0), sqe.addr);
try testing.expectEqual(@as(u32, buffer_len), sqe.len);
try testing.expectEqual(@as(u16, group_id), sqe.buf_index);
@ -1469,6 +1473,8 @@ test "provide_buffers: read" {
}
test "remove_buffers" {
const io = testing.io;
var ring = IoUring.init(1, 0) catch |err| switch (err) {
error.SystemOutdated => return error.SkipZigTest,
error.PermissionDenied => return error.SkipZigTest,
@ -1476,8 +1482,8 @@ test "remove_buffers" {
};
defer ring.deinit();
const fd = try posix.openZ("/dev/zero", .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
defer posix.close(fd);
const file = try Io.Dir.openFileAbsolute(io, "/dev/zero", .{});
defer file.close(io);
const group_id = 1337;
const buffer_id = 0;
@ -1522,7 +1528,7 @@ test "remove_buffers" {
// This read should work
{
_ = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
_ = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
try testing.expectEqual(@as(u32, 1), try ring.submit());
const cqe = try ring.copy_cqe();
@ -1542,7 +1548,7 @@ test "remove_buffers" {
// Final read should _not_ work
{
_ = try ring.read(0xdfdfdfdf, fd, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
_ = try ring.read(0xdfdfdfdf, file.handle, .{ .buffer_selection = .{ .group_id = group_id, .len = buffer_len } }, 0);
try testing.expectEqual(@as(u32, 1), try ring.submit());
const cqe = try ring.copy_cqe();
@ -1747,7 +1753,7 @@ test "accept multishot" {
var nr: usize = 4; // number of clients to connect
while (nr > 0) : (nr -= 1) {
// connect client
const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
errdefer posix.close(client);
try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
@ -1856,7 +1862,7 @@ test "accept_direct" {
try testing.expectEqual(@as(u32, 1), try ring.submit());
// connect
const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
defer posix.close(client);
@ -1868,7 +1874,7 @@ test "accept_direct" {
try testing.expect(cqe_accept.user_data == accept_userdata);
// send data
_ = try posix.send(client, buffer_send, 0);
_ = try send(client, buffer_send, 0);
// Example of how to use registered fd:
// Submit receive to fixed file returned by accept (fd_index).
@ -1890,7 +1896,7 @@ test "accept_direct" {
_ = try ring.accept_direct(accept_userdata, listener_socket, null, null, 0);
try testing.expectEqual(@as(u32, 1), try ring.submit());
// connect
const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
defer posix.close(client);
// completion with error
@ -1940,7 +1946,7 @@ test "accept_multishot_direct" {
for (registered_fds) |_| {
// connect
const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
defer posix.close(client);
@ -1955,7 +1961,7 @@ test "accept_multishot_direct" {
// Multishot is terminated (more flag is not set).
{
// connect
const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
try posix.connect(client, addrAny(&address), @sizeOf(linux.sockaddr.in));
defer posix.close(client);
// completion with error
@ -2456,7 +2462,7 @@ test "bind/listen/connect" {
// Read system assigned port into addr
var addr_len: posix.socklen_t = @sizeOf(linux.sockaddr.in);
try posix.getsockname(listen_fd, addrAny(&addr), &addr_len);
try getsockname(listen_fd, addrAny(&addr), &addr_len);
break :brk listen_fd;
};
@ -2611,7 +2617,7 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
_ = try ring.accept(0xaaaaaaaa, listener_socket, &accept_addr, &accept_addr_len, 0);
// Create a TCP client socket
const client = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
const client = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
errdefer posix.close(client);
_ = try ring.connect(0xcccccccc, client, addrAny(&address), @sizeOf(linux.sockaddr.in));
@ -2651,16 +2657,16 @@ pub fn createSocketTestHarness(ring: *IoUring) !SocketTestHarness {
fn createListenerSocket(address: *linux.sockaddr.in) !posix.socket_t {
const kernel_backlog = 1;
const listener_socket = try posix.socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
const listener_socket = try socket(address.family, posix.SOCK.STREAM | posix.SOCK.CLOEXEC, 0);
errdefer posix.close(listener_socket);
try posix.setsockopt(listener_socket, posix.SOL.SOCKET, posix.SO.REUSEADDR, &mem.toBytes(@as(c_int, 1)));
try posix.bind(listener_socket, addrAny(address), @sizeOf(linux.sockaddr.in));
try posix.listen(listener_socket, kernel_backlog);
try bind(listener_socket, addrAny(address), @sizeOf(linux.sockaddr.in));
try listen(listener_socket, kernel_backlog);
// set address to the OS-chosen IP/port.
var slen: posix.socklen_t = @sizeOf(linux.sockaddr.in);
try posix.getsockname(listener_socket, addrAny(address), &slen);
try getsockname(listener_socket, addrAny(address), &slen);
return listener_socket;
}
@ -2689,3 +2695,40 @@ inline fn skipKernelLessThan(required: std.SemanticVersion) !void {
fn addrAny(addr: *linux.sockaddr.in) *linux.sockaddr {
return @ptrCast(addr);
}
fn socket(domain: u32, socket_type: u32, protocol: u32) !posix.socket_t {
const rc = posix.system.socket(domain, socket_type, protocol);
switch (posix.errno(rc)) {
.SUCCESS => return @intCast(rc),
else => return error.SocketCreationFailure,
}
}
fn bind(sock: posix.socket_t, addr: *const posix.sockaddr, len: posix.socklen_t) !void {
switch (posix.errno(posix.system.bind(sock, addr, len))) {
.SUCCESS => return,
else => return error.BindFailure,
}
}
fn listen(sock: posix.socket_t, backlog: u31) !void {
switch (posix.errno(posix.system.listen(sock, backlog))) {
.SUCCESS => return,
else => return error.ListenFailure,
}
}
fn getsockname(sock: posix.socket_t, addr: *posix.sockaddr, addrlen: *posix.socklen_t) !void {
switch (posix.errno(posix.system.getsockname(sock, addr, addrlen))) {
.SUCCESS => return,
else => return error.GetSockNameFailure,
}
}
fn send(sockfd: posix.socket_t, buf: []const u8, flags: u32) !usize {
const rc = posix.system.sendto(sockfd, buf.ptr, buf.len, flags, null, 0);
switch (posix.errno(rc)) {
.SUCCESS => return @intCast(rc),
else => return error.SendFailed,
}
}

File diff suppressed because it is too large Load diff

View file

@ -35,14 +35,14 @@ test "check WASI CWD" {
test "getuid" {
if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
_ = posix.getuid();
_ = posix.geteuid();
_ = posix.system.getuid();
_ = posix.system.geteuid();
}
test "getgid" {
if (native_os == .windows or native_os == .wasi) return error.SkipZigTest;
_ = posix.getgid();
_ = posix.getegid();
_ = posix.system.getgid();
_ = posix.system.getegid();
}
test "sigaltstack" {
@ -121,13 +121,18 @@ test "pipe" {
if (native_os == .windows or native_os == .wasi)
return error.SkipZigTest;
const io = testing.io;
const fds = try std.Io.Threaded.pipe2(.{});
try expect((try posix.write(fds[1], "hello")) == 5);
const out: Io.File = .{ .handle = fds[0] };
const in: Io.File = .{ .handle = fds[1] };
try in.writeStreamingAll(io, "hello");
var buf: [16]u8 = undefined;
try expect((try posix.read(fds[0], buf[0..])) == 5);
try expect((try out.readStreaming(io, &.{&buf})) == 5);
try expectEqualSlices(u8, buf[0..5], "hello");
posix.close(fds[1]);
posix.close(fds[0]);
out.close(io);
in.close(io);
}
test "memfd_create" {
@ -458,8 +463,12 @@ test "rename smoke test" {
// Create some file using `open`.
const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });
defer gpa.free(file_path);
const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR, .CREAT = true, .EXCL = true }, mode);
posix.close(fd);
const file = try Io.Dir.cwd().createFile(io, file_path, .{
.read = true,
.exclusive = true,
.permissions = .fromMode(mode),
});
file.close(io);
// Rename the file
const new_file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" });
@ -471,15 +480,15 @@ test "rename smoke test" {
// Try opening renamed file
const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_file" });
defer gpa.free(file_path);
const fd = try posix.open(file_path, .{ .ACCMODE = .RDWR }, mode);
posix.close(fd);
const file = try Io.Dir.cwd().openFile(io, file_path, .{ .mode = .read_write });
file.close(io);
}
{
// Try opening original file - should fail with error.FileNotFound
const file_path = try Dir.path.join(gpa, &.{ base_path, "some_file" });
defer gpa.free(file_path);
try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDWR }, mode));
try expectError(error.FileNotFound, Io.Dir.cwd().openFile(io, file_path, .{ .mode = .read_write }));
}
{
@ -498,15 +507,15 @@ test "rename smoke test" {
// Try opening renamed directory
const file_path = try Dir.path.join(gpa, &.{ base_path, "some_other_dir" });
defer gpa.free(file_path);
const fd = try posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode);
posix.close(fd);
const dir = try Io.Dir.cwd().openDir(io, file_path, .{});
dir.close(io);
}
{
// Try opening original directory - should fail with error.FileNotFound
const file_path = try Dir.path.join(gpa, &.{ base_path, "some_dir" });
defer gpa.free(file_path);
try expectError(error.FileNotFound, posix.open(file_path, .{ .ACCMODE = .RDONLY, .DIRECTORY = true }, mode));
try expectError(error.FileNotFound, Io.Dir.cwd().openDir(io, file_path, .{}));
}
}

View file

@ -618,7 +618,7 @@ pub const TmpDir = struct {
sub_path: [sub_path_len]u8,
const random_bytes_count = 12;
const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
const sub_path_len = std.base64.url_safe.Encoder.calcSize(random_bytes_count);
pub fn cleanup(self: *TmpDir) void {
self.dir.close(io);
@ -633,7 +633,7 @@ pub fn tmpDir(opts: Io.Dir.OpenOptions) TmpDir {
var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
io.random(&random_bytes);
var sub_path: [TmpDir.sub_path_len]u8 = undefined;
_ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
_ = std.base64.url_safe.Encoder.encode(&sub_path, &random_bytes);
const cwd = Io.Dir.cwd();
var cache_dir = cwd.createDirPathOpen(io, ".zig-cache", .{}) catch

View file

@ -1,30 +0,0 @@
const std = @import("std");
const PrintFn = *const fn () void;
pub fn main() void {
var printFn: PrintFn = stopSayingThat;
var i: u32 = 0;
while (i < 4) : (i += 1) printFn();
printFn = moveEveryZig;
printFn();
}
fn stopSayingThat() void {
_ = std.posix.write(1, "Hello, my name is Inigo Montoya; you killed my father, prepare to die.\n") catch {};
}
fn moveEveryZig() void {
_ = std.posix.write(1, "All your codebase are belong to us\n") catch {};
}
// run
// target=x86_64-macos
//
// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
// Hello, my name is Inigo Montoya; you killed my father, prepare to die.
// All your codebase are belong to us
//

View file

@ -1,28 +0,0 @@
const std = @import("std");
pub fn main() void {
printNumberHex(0x00000000);
printNumberHex(0xaaaaaaaa);
printNumberHex(0xdeadbeef);
printNumberHex(0x31415926);
}
fn printNumberHex(x: u32) void {
const digit_chars = "0123456789abcdef";
var i: u5 = 28;
while (true) : (i -= 4) {
const digit = (x >> i) & 0xf;
_ = std.posix.write(1, &.{digit_chars[digit]}) catch {};
if (i == 0) break;
}
_ = std.posix.write(1, "\n") catch {};
}
// run
// target=x86_64-macos
//
// 00000000
// aaaaaaaa
// deadbeef
// 31415926
//