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 }