lib/sys/src/errno.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const builtin = @import("builtin");
3 const capabilities = @import("capabilities.zig");
4
5 threadlocal var local_errno: c_int = 0;
6
7 pub const required_capabilities = capabilities.noLibc(&.{.libc_interop});
8
9 pub const StoragePolicy = enum {
10 local_thread,
11 libc_thread,
12 };
13
14 const LibcErrno = switch (builtin.os.tag) {
15 .linux => switch (builtin.abi) {
16 .android, .androideabi => Function("__errno", "c"),
17 else => Function("__errno_location", "c"),
18 },
19 .emscripten, .serenity => Function("__errno_location", "c"),
20 .wasi, .dragonfly => ThreadLocal,
21 .windows => Function("_errno", "c"),
22 .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .freebsd => Function("__error", "c"),
23 .illumos => Function("___errno", "c"),
24 .openbsd, .netbsd => Function("__errno", "c"),
25 .haiku => Function("_errnop", "root"),
26 else => Local,
27 };
28
29 pub fn set(value: c_int) void {
30 localStorage().* = value;
31 }
32
33 pub fn current() c_int {
34 return localStorage().*;
35 }
36
37 pub fn setHostInterop(value: c_int) void {
38 hostInteropStorage().* = value;
39 }
40
41 pub fn currentHostInterop() c_int {
42 return hostInteropStorage().*;
43 }
44
45 pub fn storagePolicy() StoragePolicy {
46 return .local_thread;
47 }
48
49 pub fn hostInteropStoragePolicy() StoragePolicy {
50 if (comptime builtin.link_libc) return LibcErrno.storage_policy;
51 return .local_thread;
52 }
53
54 fn localStorage() *c_int {
55 return &local_errno;
56 }
57
58 fn hostInteropStorage() *c_int {
59 if (comptime builtin.link_libc) return LibcErrno.storage();
60 return localStorage();
61 }
62
63 fn Function(comptime name: []const u8, comptime library_name: ?[]const u8) type {
64 return struct {
65 const storage_policy: StoragePolicy = .libc_thread;
66
67 fn storage() *c_int {
68 return @extern(*const fn () callconv(.c) *c_int, .{
69 .name = name,
70 .library_name = library_name,
71 })();
72 }
73 };
74 }
75
76 const ThreadLocal = struct {
77 const storage_policy: StoragePolicy = .libc_thread;
78
79 extern threadlocal var errno: c_int;
80
81 fn storage() *c_int {
82 return &errno;
83 }
84 };
85
86 const Local = struct {
87 const storage_policy: StoragePolicy = .local_thread;
88
89 fn storage() *c_int {
90 return &local_errno;
91 }
92 };
93
94 test "errno access stores nonzero values" {
95 set(123);
96 try std.testing.expectEqual(@as(c_int, 123), current());
97 set(0);
98 }
99
100 test "errno access is explicit process policy" {
101 set(0);
102 try std.testing.expectEqual(@as(c_int, 0), current());
103 try std.testing.expectEqual(StoragePolicy.local_thread, storagePolicy());
104 }
105
106 test "errno host interop sharing is explicit" {
107 set(0);
108 setHostInterop(0);
109
110 set(77);
111 try std.testing.expectEqual(@as(c_int, 77), current());
112 if (comptime builtin.link_libc) {
113 try std.testing.expectEqual(@as(c_int, 0), currentHostInterop());
114 } else {
115 try std.testing.expectEqual(@as(c_int, 77), currentHostInterop());
116 }
117
118 setHostInterop(88);
119 try std.testing.expectEqual(@as(c_int, 88), currentHostInterop());
120 if (comptime builtin.link_libc and LibcErrno.storage_policy == .libc_thread) {
121 try std.testing.expectEqual(@as(c_int, 77), current());
122 } else {
123 try std.testing.expectEqual(@as(c_int, 88), current());
124 }
125 const expected_policy = if (comptime builtin.link_libc) LibcErrno.storage_policy else StoragePolicy.local_thread;
126 try std.testing.expectEqual(expected_policy, hostInteropStoragePolicy());
127
128 set(0);
129 setHostInterop(0);
130 }