std: [breaking] move errno to become an nonexhaustive enum
The primary purpose of this change is to eliminate one usage of `usingnamespace` in the standard library - specifically the usage for errno values in `std.os.linux`. This is accomplished by truncating the `E` prefix from error values, and making errno a proper enum. A similar strategy can be used to eliminate some other `usingnamespace` sites in the std lib.
This commit is contained in:
@@ -9,9 +9,10 @@
|
||||
//! both evented I/O and async I/O, see the respective names in the top level std namespace.
|
||||
|
||||
const std = @import("std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const os = std.os;
|
||||
const assert = std.debug.assert;
|
||||
const target = std.Target.current;
|
||||
const target = builtin.target;
|
||||
const Atomic = std.atomic.Atomic;
|
||||
|
||||
pub const AutoResetEvent = @import("Thread/AutoResetEvent.zig");
|
||||
@@ -24,7 +25,8 @@ pub const Condition = @import("Thread/Condition.zig");
|
||||
|
||||
pub const spinLoopHint = @compileError("deprecated: use std.atomic.spinLoopHint");
|
||||
|
||||
pub const use_pthreads = target.os.tag != .windows and std.Target.current.os.tag != .wasi and std.builtin.link_libc;
|
||||
pub const use_pthreads = target.os.tag != .windows and target.os.tag != .wasi and builtin.link_libc;
|
||||
const is_gnu = target.abi.isGnu();
|
||||
|
||||
const Thread = @This();
|
||||
const Impl = if (target.os.tag == .windows)
|
||||
@@ -38,7 +40,7 @@ else
|
||||
|
||||
impl: Impl,
|
||||
|
||||
pub const max_name_len = switch (std.Target.current.os.tag) {
|
||||
pub const max_name_len = switch (target.os.tag) {
|
||||
.linux => 15,
|
||||
.windows => 31,
|
||||
.macos, .ios, .watchos, .tvos => 63,
|
||||
@@ -64,20 +66,21 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
|
||||
break :blk name_buf[0..name.len :0];
|
||||
};
|
||||
|
||||
switch (std.Target.current.os.tag) {
|
||||
switch (target.os.tag) {
|
||||
.linux => if (use_pthreads) {
|
||||
const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr);
|
||||
return switch (err) {
|
||||
0 => {},
|
||||
os.ERANGE => unreachable,
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (err) {
|
||||
.SUCCESS => return,
|
||||
.RANGE => unreachable,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
} else if (use_pthreads and self.getHandle() == std.c.pthread_self()) {
|
||||
// TODO: this is dead code. what did the author of this code intend to happen here?
|
||||
const err = try os.prctl(.SET_NAME, .{@ptrToInt(name_with_terminator.ptr)});
|
||||
return switch (err) {
|
||||
0 => {},
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (@intToEnum(os.E, err)) {
|
||||
.SUCCESS => return,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
} else {
|
||||
var buf: [32]u8 = undefined;
|
||||
const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()});
|
||||
@@ -87,7 +90,7 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
|
||||
|
||||
try file.writer().writeAll(name);
|
||||
},
|
||||
.windows => if (std.Target.current.os.isAtLeast(.windows, .win10_rs1)) |res| {
|
||||
.windows => if (target.os.isAtLeast(.windows, .win10_rs1)) |res| {
|
||||
// SetThreadDescription is only available since version 1607, which is 10.0.14393.795
|
||||
// See https://en.wikipedia.org/wiki/Microsoft_Windows_SDK
|
||||
if (!res) {
|
||||
@@ -110,24 +113,25 @@ pub fn setName(self: Thread, name: []const u8) SetNameError!void {
|
||||
if (self.getHandle() != std.c.pthread_self()) return error.Unsupported;
|
||||
|
||||
const err = std.c.pthread_setname_np(name_with_terminator.ptr);
|
||||
return switch (err) {
|
||||
0 => {},
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (err) {
|
||||
.SUCCESS => return,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
},
|
||||
.netbsd => if (use_pthreads) {
|
||||
const err = std.c.pthread_setname_np(self.getHandle(), name_with_terminator.ptr, null);
|
||||
return switch (err) {
|
||||
0 => {},
|
||||
os.EINVAL => unreachable,
|
||||
os.ESRCH => unreachable,
|
||||
os.ENOMEM => unreachable,
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (err) {
|
||||
.SUCCESS => return,
|
||||
.INVAL => unreachable,
|
||||
.SRCH => unreachable,
|
||||
.NOMEM => unreachable,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
},
|
||||
.freebsd, .openbsd => if (use_pthreads) {
|
||||
// Use pthread_set_name_np for FreeBSD because pthread_setname_np is FreeBSD 12.2+ only.
|
||||
// TODO maybe revisit this if depending on FreeBSD 12.2+ is acceptable because pthread_setname_np can return an error.
|
||||
// TODO maybe revisit this if depending on FreeBSD 12.2+ is acceptable because
|
||||
// pthread_setname_np can return an error.
|
||||
|
||||
std.c.pthread_set_name_np(self.getHandle(), name_with_terminator.ptr);
|
||||
},
|
||||
@@ -151,20 +155,20 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
|
||||
buffer_ptr[max_name_len] = 0;
|
||||
var buffer = std.mem.span(buffer_ptr);
|
||||
|
||||
switch (std.Target.current.os.tag) {
|
||||
.linux => if (use_pthreads and comptime std.Target.current.abi.isGnu()) {
|
||||
switch (target.os.tag) {
|
||||
.linux => if (use_pthreads and is_gnu) {
|
||||
const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1);
|
||||
return switch (err) {
|
||||
0 => std.mem.sliceTo(buffer, 0),
|
||||
os.ERANGE => unreachable,
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (err) {
|
||||
.SUCCESS => return std.mem.sliceTo(buffer, 0),
|
||||
.RANGE => unreachable,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
} else if (use_pthreads and self.getHandle() == std.c.pthread_self()) {
|
||||
const err = try os.prctl(.GET_NAME, .{@ptrToInt(buffer.ptr)});
|
||||
return switch (err) {
|
||||
0 => std.mem.sliceTo(buffer, 0),
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (@intToEnum(os.E, err)) {
|
||||
.SUCCESS => return std.mem.sliceTo(buffer, 0),
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
} else if (!use_pthreads) {
|
||||
var buf: [32]u8 = undefined;
|
||||
const path = try std.fmt.bufPrint(&buf, "/proc/self/task/{d}/comm", .{self.getHandle()});
|
||||
@@ -179,7 +183,7 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
|
||||
// musl doesn't provide pthread_getname_np and there's no way to retrieve the thread id of an arbitrary thread.
|
||||
return error.Unsupported;
|
||||
},
|
||||
.windows => if (std.Target.current.os.isAtLeast(.windows, .win10_rs1)) |res| {
|
||||
.windows => if (target.os.isAtLeast(.windows, .win10_rs1)) |res| {
|
||||
// GetThreadDescription is only available since version 1607, which is 10.0.14393.795
|
||||
// See https://en.wikipedia.org/wiki/Microsoft_Windows_SDK
|
||||
if (!res) {
|
||||
@@ -198,20 +202,20 @@ pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]co
|
||||
},
|
||||
.macos, .ios, .watchos, .tvos => if (use_pthreads) {
|
||||
const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1);
|
||||
return switch (err) {
|
||||
0 => std.mem.sliceTo(buffer, 0),
|
||||
os.ESRCH => unreachable,
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (err) {
|
||||
.SUCCESS => return std.mem.sliceTo(buffer, 0),
|
||||
.SRCH => unreachable,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
},
|
||||
.netbsd => if (use_pthreads) {
|
||||
const err = std.c.pthread_getname_np(self.getHandle(), buffer.ptr, max_name_len + 1);
|
||||
return switch (err) {
|
||||
0 => std.mem.sliceTo(buffer, 0),
|
||||
os.EINVAL => unreachable,
|
||||
os.ESRCH => unreachable,
|
||||
else => return os.unexpectedErrno(err),
|
||||
};
|
||||
switch (err) {
|
||||
.SUCCESS => return std.mem.sliceTo(buffer, 0),
|
||||
.INVAL => unreachable,
|
||||
.SRCH => unreachable,
|
||||
else => |e| return os.unexpectedErrno(e),
|
||||
}
|
||||
},
|
||||
.freebsd, .openbsd => if (use_pthreads) {
|
||||
// Use pthread_get_name_np for FreeBSD because pthread_getname_np is FreeBSD 12.2+ only.
|
||||
@@ -288,7 +292,7 @@ pub const SpawnError = error{
|
||||
/// The caller must eventually either call `join()` to wait for the thread to finish and free its resources
|
||||
/// or call `detach()` to excuse the caller from calling `join()` and have the thread clean up its resources on completion`.
|
||||
pub fn spawn(config: SpawnConfig, comptime function: anytype, args: anytype) SpawnError!Thread {
|
||||
if (std.builtin.single_threaded) {
|
||||
if (builtin.single_threaded) {
|
||||
@compileError("Cannot spawn thread when building in single-threaded mode");
|
||||
}
|
||||
|
||||
@@ -611,13 +615,13 @@ const PosixThreadImpl = struct {
|
||||
errdefer allocator.destroy(args_ptr);
|
||||
|
||||
var attr: c.pthread_attr_t = undefined;
|
||||
if (c.pthread_attr_init(&attr) != 0) return error.SystemResources;
|
||||
defer assert(c.pthread_attr_destroy(&attr) == 0);
|
||||
if (c.pthread_attr_init(&attr) != .SUCCESS) return error.SystemResources;
|
||||
defer assert(c.pthread_attr_destroy(&attr) == .SUCCESS);
|
||||
|
||||
// Use the same set of parameters used by the libc-less impl.
|
||||
const stack_size = std.math.max(config.stack_size, 16 * 1024);
|
||||
assert(c.pthread_attr_setstacksize(&attr, stack_size) == 0);
|
||||
assert(c.pthread_attr_setguardsize(&attr, std.mem.page_size) == 0);
|
||||
assert(c.pthread_attr_setstacksize(&attr, stack_size) == .SUCCESS);
|
||||
assert(c.pthread_attr_setguardsize(&attr, std.mem.page_size) == .SUCCESS);
|
||||
|
||||
var handle: c.pthread_t = undefined;
|
||||
switch (c.pthread_create(
|
||||
@@ -626,10 +630,10 @@ const PosixThreadImpl = struct {
|
||||
Instance.entryFn,
|
||||
if (@sizeOf(Args) > 1) @ptrCast(*c_void, args_ptr) else undefined,
|
||||
)) {
|
||||
0 => return Impl{ .handle = handle },
|
||||
os.EAGAIN => return error.SystemResources,
|
||||
os.EPERM => unreachable,
|
||||
os.EINVAL => unreachable,
|
||||
.SUCCESS => return Impl{ .handle = handle },
|
||||
.AGAIN => return error.SystemResources,
|
||||
.PERM => unreachable,
|
||||
.INVAL => unreachable,
|
||||
else => |err| return os.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
@@ -640,19 +644,19 @@ const PosixThreadImpl = struct {
|
||||
|
||||
fn detach(self: Impl) void {
|
||||
switch (c.pthread_detach(self.handle)) {
|
||||
0 => {},
|
||||
os.EINVAL => unreachable, // thread handle is not joinable
|
||||
os.ESRCH => unreachable, // thread handle is invalid
|
||||
.SUCCESS => {},
|
||||
.INVAL => unreachable, // thread handle is not joinable
|
||||
.SRCH => unreachable, // thread handle is invalid
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn join(self: Impl) void {
|
||||
switch (c.pthread_join(self.handle, null)) {
|
||||
0 => {},
|
||||
os.EINVAL => unreachable, // thread handle is not joinable (or another thread is already joining in)
|
||||
os.ESRCH => unreachable, // thread handle is invalid
|
||||
os.EDEADLK => unreachable, // two threads tried to join each other
|
||||
.SUCCESS => {},
|
||||
.INVAL => unreachable, // thread handle is not joinable (or another thread is already joining in)
|
||||
.SRCH => unreachable, // thread handle is invalid
|
||||
.DEADLK => unreachable, // two threads tried to join each other
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
@@ -937,13 +941,13 @@ const LinuxThreadImpl = struct {
|
||||
tls_ptr,
|
||||
&instance.thread.child_tid.value,
|
||||
))) {
|
||||
0 => return Impl{ .thread = &instance.thread },
|
||||
os.EAGAIN => return error.ThreadQuotaExceeded,
|
||||
os.EINVAL => unreachable,
|
||||
os.ENOMEM => return error.SystemResources,
|
||||
os.ENOSPC => unreachable,
|
||||
os.EPERM => unreachable,
|
||||
os.EUSERS => unreachable,
|
||||
.SUCCESS => return Impl{ .thread = &instance.thread },
|
||||
.AGAIN => return error.ThreadQuotaExceeded,
|
||||
.INVAL => unreachable,
|
||||
.NOMEM => return error.SystemResources,
|
||||
.NOSPC => unreachable,
|
||||
.PERM => unreachable,
|
||||
.USERS => unreachable,
|
||||
else => |err| return os.unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
@@ -982,9 +986,9 @@ const LinuxThreadImpl = struct {
|
||||
tid,
|
||||
null,
|
||||
))) {
|
||||
0 => continue,
|
||||
os.EINTR => continue,
|
||||
os.EAGAIN => continue,
|
||||
.SUCCESS => continue,
|
||||
.INTR => continue,
|
||||
.AGAIN => continue,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
@@ -1011,7 +1015,7 @@ fn testThreadName(thread: *Thread) !void {
|
||||
}
|
||||
|
||||
test "setName, getName" {
|
||||
if (std.builtin.single_threaded) return error.SkipZigTest;
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
const Context = struct {
|
||||
start_wait_event: ResetEvent = undefined,
|
||||
@@ -1029,7 +1033,7 @@ test "setName, getName" {
|
||||
// Wait for the main thread to have set the thread field in the context.
|
||||
ctx.start_wait_event.wait();
|
||||
|
||||
switch (std.Target.current.os.tag) {
|
||||
switch (target.os.tag) {
|
||||
.windows => testThreadName(&ctx.thread) catch |err| switch (err) {
|
||||
error.Unsupported => return error.SkipZigTest,
|
||||
else => return err,
|
||||
@@ -1054,7 +1058,7 @@ test "setName, getName" {
|
||||
context.start_wait_event.set();
|
||||
context.test_done_event.wait();
|
||||
|
||||
switch (std.Target.current.os.tag) {
|
||||
switch (target.os.tag) {
|
||||
.macos, .ios, .watchos, .tvos => {
|
||||
const res = thread.setName("foobar");
|
||||
try std.testing.expectError(error.Unsupported, res);
|
||||
@@ -1063,7 +1067,7 @@ test "setName, getName" {
|
||||
error.Unsupported => return error.SkipZigTest,
|
||||
else => return err,
|
||||
},
|
||||
else => |tag| if (tag == .linux and use_pthreads and comptime std.Target.current.abi.isMusl()) {
|
||||
else => |tag| if (tag == .linux and use_pthreads and comptime target.abi.isMusl()) {
|
||||
try thread.setName("foobar");
|
||||
|
||||
var name_buffer: [max_name_len:0]u8 = undefined;
|
||||
@@ -1096,7 +1100,7 @@ fn testIncrementNotify(value: *usize, event: *ResetEvent) void {
|
||||
}
|
||||
|
||||
test "Thread.join" {
|
||||
if (std.builtin.single_threaded) return error.SkipZigTest;
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var value: usize = 0;
|
||||
var event: ResetEvent = undefined;
|
||||
@@ -1110,7 +1114,7 @@ test "Thread.join" {
|
||||
}
|
||||
|
||||
test "Thread.detach" {
|
||||
if (std.builtin.single_threaded) return error.SkipZigTest;
|
||||
if (builtin.single_threaded) return error.SkipZigTest;
|
||||
|
||||
var value: usize = 0;
|
||||
var event: ResetEvent = undefined;
|
||||
|
||||
Reference in New Issue
Block a user