authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-18 21:32:34-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:10-08:00
log21d0264c61ac29724b98187aa87d192f97b52425
tree4c6a174456c70245d4a5b98ea02c8fcf0512379b
parent8632a28ca95a5a491bde57fdc45d9a0ce3cf597e

std.dynamic_library: use a global static single threaded Io

See #30150

7 files changed, 41 insertions(+), 41 deletions(-)

lib/std/Build/Watch.zig+1-1
...@@ -684,7 +684,7 @@ const Os = switch (builtin.os.tag) {...@@ -684,7 +684,7 @@ const Os = switch (builtin.os.tag) {
684 path.root_dir.handle.handle684 path.root_dir.handle.handle
685 else685 else
686 posix.openat(path.root_dir.handle.handle, path.sub_path, dir_open_flags, 0) catch |err| {686 posix.openat(path.root_dir.handle.handle, path.sub_path, dir_open_flags, 0) catch |err| {
687 fatal("failed to open directory {f}: {s}", .{ path, @errorName(err) });687 fatal("failed to open directory {f}: {t}", .{ path, err });
688 };688 };
689 // Empirically the dir has to stay open or else no events are triggered.689 // Empirically the dir has to stay open or else no events are triggered.
690 errdefer if (!skip_open_dir) posix.close(dir_fd);690 errdefer if (!skip_open_dir) posix.close(dir_fd);
lib/std/Io/File/Reader.zig+3-1
...@@ -43,7 +43,9 @@ pub const Error = error{...@@ -43,7 +43,9 @@ pub const Error = error{
43 /// In WASI, this error occurs when the file descriptor does43 /// In WASI, this error occurs when the file descriptor does
44 /// not hold the required rights to read from it.44 /// not hold the required rights to read from it.
45 AccessDenied,45 AccessDenied,
46 /// Unable to read file due to lock.46 /// Unable to read file due to lock. Depending on the `Io` implementation,
47 /// reading from a locked file may return this error, or may ignore the
48 /// lock.
47 LockViolation,49 LockViolation,
48} || Io.Cancelable || Io.UnexpectedError;50} || Io.Cancelable || Io.UnexpectedError;
4951
lib/std/Io/Writer.zig+3-3
...@@ -2838,7 +2838,7 @@ test "discarding sendFile" {...@@ -2838,7 +2838,7 @@ test "discarding sendFile" {
2838 const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true });2838 const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true });
2839 defer file.close(io);2839 defer file.close(io);
2840 var r_buffer: [256]u8 = undefined;2840 var r_buffer: [256]u8 = undefined;
2841 var file_writer: File.Writer = .init(file, &r_buffer);2841 var file_writer: File.Writer = .init(file, io, &r_buffer);
2842 try file_writer.interface.writeByte('h');2842 try file_writer.interface.writeByte('h');
2843 try file_writer.interface.flush();2843 try file_writer.interface.flush();
28442844
...@@ -2860,7 +2860,7 @@ test "allocating sendFile" {...@@ -2860,7 +2860,7 @@ test "allocating sendFile" {
2860 const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true });2860 const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true });
2861 defer file.close(io);2861 defer file.close(io);
2862 var r_buffer: [2]u8 = undefined;2862 var r_buffer: [2]u8 = undefined;
2863 var file_writer: File.Writer = .init(file, &r_buffer);2863 var file_writer: File.Writer = .init(file, io, &r_buffer);
2864 try file_writer.interface.writeAll("abcd");2864 try file_writer.interface.writeAll("abcd");
2865 try file_writer.interface.flush();2865 try file_writer.interface.flush();
28662866
...@@ -2884,7 +2884,7 @@ test sendFileReading {...@@ -2884,7 +2884,7 @@ test sendFileReading {
2884 const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true });2884 const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true });
2885 defer file.close(io);2885 defer file.close(io);
2886 var r_buffer: [2]u8 = undefined;2886 var r_buffer: [2]u8 = undefined;
2887 var file_writer: File.Writer = .init(file, &r_buffer);2887 var file_writer: File.Writer = .init(file, io, &r_buffer);
2888 try file_writer.interface.writeAll("abcd");2888 try file_writer.interface.writeAll("abcd");
2889 try file_writer.interface.flush();2889 try file_writer.interface.flush();
28902890
lib/std/debug.zig+2-8
...@@ -1610,14 +1610,8 @@ test "manage resources correctly" {...@@ -1610,14 +1610,8 @@ test "manage resources correctly" {
1610 var discarding: Writer.Discarding = .init(&.{});1610 var discarding: Writer.Discarding = .init(&.{});
1611 var di: SelfInfo = .init;1611 var di: SelfInfo = .init;
1612 defer di.deinit(gpa);1612 defer di.deinit(gpa);
1613 try printSourceAtAddress(1613 const t: Io.Terminal = .{ .writer = &discarding.writer, .mode = .no_color };
1614 gpa,1614 try printSourceAtAddress(gpa, io, &di, t, S.showMyTrace());
1615 io,
1616 &di,
1617 &discarding.writer,
1618 S.showMyTrace(),
1619 .no_color,
1620 );
1621}1615}
16221616
1623/// This API helps you track where a value originated and where it was mutated,1617/// This API helps you track where a value originated and where it was mutated,
lib/std/dynamic_library.zig+21-19
...@@ -55,11 +55,11 @@ pub const DynLib = struct {...@@ -55,11 +55,11 @@ pub const DynLib = struct {
55// An iterator is provided in order to traverse the linked list in a idiomatic55// An iterator is provided in order to traverse the linked list in a idiomatic
56// fashion.56// fashion.
57const LinkMap = extern struct {57const LinkMap = extern struct {
58 l_addr: usize,58 addr: usize,
59 l_name: [*:0]const u8,59 name: [*:0]const u8,
60 l_ld: ?*elf.Dyn,60 ld: ?*elf.Dyn,
61 l_next: ?*LinkMap,61 next: ?*LinkMap,
62 l_prev: ?*LinkMap,62 prev: ?*LinkMap,
6363
64 pub const Iterator = struct {64 pub const Iterator = struct {
65 current: ?*LinkMap,65 current: ?*LinkMap,
...@@ -70,7 +70,7 @@ const LinkMap = extern struct {...@@ -70,7 +70,7 @@ const LinkMap = extern struct {
7070
71 pub fn next(self: *Iterator) ?*LinkMap {71 pub fn next(self: *Iterator) ?*LinkMap {
72 if (self.current) |it| {72 if (self.current) |it| {
73 self.current = it.l_next;73 self.current = it.next;
74 return it;74 return it;
75 }75 }
76 return null;76 return null;
...@@ -79,10 +79,10 @@ const LinkMap = extern struct {...@@ -79,10 +79,10 @@ const LinkMap = extern struct {
79};79};
8080
81const RDebug = extern struct {81const RDebug = extern struct {
82 r_version: i32,82 version: i32,
83 r_map: ?*LinkMap,83 map: ?*LinkMap,
84 r_brk: usize,84 brk: usize,
85 r_ldbase: usize,85 ldbase: usize,
86};86};
8787
88/// TODO fix comparisons of extern symbol pointers so we don't need this helper function.88/// TODO fix comparisons of extern symbol pointers so we don't need this helper function.
...@@ -107,8 +107,8 @@ pub fn linkmap_iterator() error{InvalidExe}!LinkMap.Iterator {...@@ -107,8 +107,8 @@ pub fn linkmap_iterator() error{InvalidExe}!LinkMap.Iterator {
107 elf.DT_DEBUG => {107 elf.DT_DEBUG => {
108 const ptr = @as(?*RDebug, @ptrFromInt(_DYNAMIC[i].d_val));108 const ptr = @as(?*RDebug, @ptrFromInt(_DYNAMIC[i].d_val));
109 if (ptr) |r_debug| {109 if (ptr) |r_debug| {
110 if (r_debug.r_version != 1) return error.InvalidExe;110 if (r_debug.version != 1) return error.InvalidExe;
111 break :init r_debug.r_map;111 break :init r_debug.map;
112 }112 }
113 },113 },
114 elf.DT_PLTGOT => {114 elf.DT_PLTGOT => {
...@@ -142,6 +142,8 @@ const ElfDynLibError = error{...@@ -142,6 +142,8 @@ const ElfDynLibError = error{
142 Streaming,142 Streaming,
143} || posix.OpenError || posix.MMapError;143} || posix.OpenError || posix.MMapError;
144144
145var static_single_threaded_io: Io.Threaded = .init_single_threaded;
146
145pub const ElfDynLib = struct {147pub const ElfDynLib = struct {
146 strings: [*:0]u8,148 strings: [*:0]u8,
147 syms: [*]elf.Sym,149 syms: [*]elf.Sym,
...@@ -157,7 +159,7 @@ pub const ElfDynLib = struct {...@@ -157,7 +159,7 @@ pub const ElfDynLib = struct {
157 dt_gnu_hash: *elf.gnu_hash.Header,159 dt_gnu_hash: *elf.gnu_hash.Header,
158 };160 };
159161
160 fn openPath(path: []const u8, io: Io) !Io.Dir {162 fn openPath(io: Io, path: []const u8) !Io.Dir {
161 if (path.len == 0) return error.NotDir;163 if (path.len == 0) return error.NotDir;
162 var parts = std.mem.tokenizeScalar(u8, path, '/');164 var parts = std.mem.tokenizeScalar(u8, path, '/');
163 var parent = if (path[0] == '/') try Io.Dir.cwd().openDir(io, "/", .{}) else Io.Dir.cwd();165 var parent = if (path[0] == '/') try Io.Dir.cwd().openDir(io, "/", .{}) else Io.Dir.cwd();
...@@ -172,7 +174,7 @@ pub const ElfDynLib = struct {...@@ -172,7 +174,7 @@ pub const ElfDynLib = struct {
172 fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t {174 fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t {
173 var paths = std.mem.tokenizeScalar(u8, search_path, delim);175 var paths = std.mem.tokenizeScalar(u8, search_path, delim);
174 while (paths.next()) |p| {176 while (paths.next()) |p| {
175 var dir = openPath(p) catch continue;177 var dir = openPath(io, p) catch continue;
176 defer dir.close(io);178 defer dir.close(io);
177 const fd = posix.openat(dir.handle, file_name, .{179 const fd = posix.openat(dir.handle, file_name, .{
178 .ACCMODE = .RDONLY,180 .ACCMODE = .RDONLY,
...@@ -221,7 +223,9 @@ pub const ElfDynLib = struct {...@@ -221,7 +223,9 @@ pub const ElfDynLib = struct {
221 }223 }
222224
223 /// Trusts the file. Malicious file will be able to execute arbitrary code.225 /// Trusts the file. Malicious file will be able to execute arbitrary code.
224 pub fn open(io: Io, path: []const u8) Error!ElfDynLib {226 pub fn open(path: []const u8) Error!ElfDynLib {
227 const io = static_single_threaded_io.ioBasic();
228
225 const fd = try resolveFromName(io, path);229 const fd = try resolveFromName(io, path);
226 defer posix.close(fd);230 defer posix.close(fd);
227231
...@@ -551,11 +555,9 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: elf.Versym, vername: []const u8, str...@@ -551,11 +555,9 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: elf.Versym, vername: []const u8, str
551}555}
552556
553test "ElfDynLib" {557test "ElfDynLib" {
554 if (native_os != .linux) {558 if (native_os != .linux) return error.SkipZigTest;
555 return error.SkipZigTest;
556 }
557
558 try testing.expectError(error.FileNotFound, ElfDynLib.open("invalid_so.so"));559 try testing.expectError(error.FileNotFound, ElfDynLib.open("invalid_so.so"));
560 try testing.expectError(error.FileNotFound, ElfDynLib.openZ("invalid_so.so"));
559}561}
560562
561/// Separated to avoid referencing `WindowsDynLib`, because its field types may not563/// Separated to avoid referencing `WindowsDynLib`, because its field types may not
lib/std/fs/test.zig+5-3
...@@ -1796,7 +1796,7 @@ test "read from locked file" {...@@ -1796,7 +1796,7 @@ test "read from locked file" {
1796 const f = try ctx.dir.createFile(io, filename, .{ .read = true });1796 const f = try ctx.dir.createFile(io, filename, .{ .read = true });
1797 defer f.close(io);1797 defer f.close(io);
1798 var buffer: [1]u8 = undefined;1798 var buffer: [1]u8 = undefined;
1799 _ = try f.read(&buffer);1799 _ = try f.readPositional(io, &.{&buffer}, 0);
1800 }1800 }
1801 {1801 {
1802 const f = try ctx.dir.createFile(io, filename, .{1802 const f = try ctx.dir.createFile(io, filename, .{
...@@ -1806,11 +1806,13 @@ test "read from locked file" {...@@ -1806,11 +1806,13 @@ test "read from locked file" {
1806 defer f.close(io);1806 defer f.close(io);
1807 const f2 = try ctx.dir.openFile(io, filename, .{});1807 const f2 = try ctx.dir.openFile(io, filename, .{});
1808 defer f2.close(io);1808 defer f2.close(io);
1809 // On POSIX locks may be ignored, however on Windows they cause
1810 // LockViolation.
1809 var buffer: [1]u8 = undefined;1811 var buffer: [1]u8 = undefined;
1810 if (builtin.os.tag == .windows) {1812 if (builtin.os.tag == .windows) {
1811 try expectError(error.LockViolation, f2.read(&buffer));1813 try expectError(error.LockViolation, f2.readPositional(io, &.{&buffer}, 0));
1812 } else {1814 } else {
1813 try expectEqual(0, f2.read(&buffer));1815 try expectEqual(0, f2.readPositional(io, &.{&buffer}, 0));
1814 }1816 }
1815 }1817 }
1816 }1818 }
lib/std/posix.zig+6-6
...@@ -777,7 +777,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) O...@@ -777,7 +777,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) O
777 .NFILE => return error.SystemFdQuotaExceeded,777 .NFILE => return error.SystemFdQuotaExceeded,
778 .NODEV => return error.NoDevice,778 .NODEV => return error.NoDevice,
779 .NOENT => return error.FileNotFound,779 .NOENT => return error.FileNotFound,
780 .SRCH => return error.ProcessNotFound,780 .SRCH => return error.FileNotFound,
781 .NOMEM => return error.SystemResources,781 .NOMEM => return error.SystemResources,
782 .NOSPC => return error.NoSpaceLeft,782 .NOSPC => return error.NoSpaceLeft,
783 .NOTDIR => return error.NotDir,783 .NOTDIR => return error.NotDir,
...@@ -2759,16 +2759,16 @@ pub fn dl_iterate_phdr(...@@ -2759,16 +2759,16 @@ pub fn dl_iterate_phdr(
27592759
2760 // Last return value from the callback function.2760 // Last return value from the callback function.
2761 while (it.next()) |entry| {2761 while (it.next()) |entry| {
2762 const phdrs: []elf.ElfN.Phdr = if (entry.l_addr != 0) phdrs: {2762 const phdrs: []elf.ElfN.Phdr = if (entry.addr != 0) phdrs: {
2763 const ehdr: *elf.ElfN.Ehdr = @ptrFromInt(entry.l_addr);2763 const ehdr: *elf.ElfN.Ehdr = @ptrFromInt(entry.addr);
2764 assert(mem.eql(u8, ehdr.ident[0..4], elf.MAGIC));2764 assert(mem.eql(u8, ehdr.ident[0..4], elf.MAGIC));
2765 const phdrs: [*]elf.ElfN.Phdr = @ptrFromInt(entry.l_addr + ehdr.phoff);2765 const phdrs: [*]elf.ElfN.Phdr = @ptrFromInt(entry.addr + ehdr.phoff);
2766 break :phdrs phdrs[0..ehdr.phnum];2766 break :phdrs phdrs[0..ehdr.phnum];
2767 } else getSelfPhdrs();2767 } else getSelfPhdrs();
27682768
2769 var info: dl_phdr_info = .{2769 var info: dl_phdr_info = .{
2770 .addr = entry.l_addr,2770 .addr = entry.addr,
2771 .name = entry.l_name,2771 .name = entry.name,
2772 .phdr = phdrs.ptr,2772 .phdr = phdrs.ptr,
2773 .phnum = @intCast(phdrs.len),2773 .phnum = @intCast(phdrs.len),
2774 };2774 };