authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-08 16:13:51-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-12-23 22:15:08-08:00
log9ccd68de0b79c3723bd11071fd836bc24ff25b33
tree3441f2a7030f40a6b625f4ff9fc7d719a60a32d3
parent7f5bb118d4d90e2b883ee66e17592ac8d7808ac8

std: move abort and exit from posix into process

and delete the unit tests that called fork() no forking allowed in the std lib, including unit tests, except to implement child process spawning.

21 files changed, 167 insertions(+), 248 deletions(-)

lib/compiler/translate-c/main.zig+1-1
......@@ -253,7 +253,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: [][:0]u8, zig_integration
253253 if (d.output_name) |path| blk: {
254254 if (std.mem.eql(u8, path, "-")) break :blk;
255255 if (std.fs.path.dirname(path)) |dirname| {
256 Io.Dir.cwd().makePath(dirname) catch |err|
256 Io.Dir.cwd().makePath(io, dirname) catch |err|
257257 return d.fatal("failed to create path to '{s}': {s}", .{ path, aro.Driver.errorDescription(err) });
258258 }
259259 out_file = Io.Dir.cwd().createFile(io, path, .{}) catch |err| {
lib/std/Build.zig+4-5
......@@ -1706,7 +1706,7 @@ pub fn truncateFile(b: *Build, dest_path: []const u8) (Io.Dir.MakeError || Io.Di
17061706 var src_file = cwd.createFile(io, dest_path, .{}) catch |err| switch (err) {
17071707 error.FileNotFound => blk: {
17081708 if (fs.path.dirname(dest_path)) |dirname| {
1709 try cwd.makePath(dirname);
1709 try cwd.makePath(io, dirname);
17101710 }
17111711 break :blk try cwd.createFile(io, dest_path, .{});
17121712 },
......@@ -2634,13 +2634,12 @@ pub const InstallDir = union(enum) {
26342634/// source of API breakage in the future, so keep that in mind when using this
26352635/// function.
26362636pub fn makeTempPath(b: *Build) []const u8 {
2637 const io = b.graph.io;
26372638 const rand_int = std.crypto.random.int(u64);
26382639 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ std.fmt.hex(rand_int);
26392640 const result_path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM");
2640 b.cache_root.handle.makePath(tmp_dir_sub_path) catch |err| {
2641 std.debug.print("unable to make tmp path '{s}': {s}\n", .{
2642 result_path, @errorName(err),
2643 });
2641 b.cache_root.handle.makePath(io, tmp_dir_sub_path) catch |err| {
2642 std.debug.print("unable to make tmp path '{s}': {t}\n", .{ result_path, err });
26442643 };
26452644 return result_path;
26462645}
lib/std/Build/Cache/Path.zig+2-2
......@@ -128,14 +128,14 @@ pub fn access(p: Path, sub_path: []const u8, flags: Io.Dir.AccessOptions) !void
128128 return p.root_dir.handle.access(joined_path, flags);
129129}
130130
131pub fn makePath(p: Path, sub_path: []const u8) !void {
131pub fn makePath(p: Path, io: Io, sub_path: []const u8) !void {
132132 var buf: [fs.max_path_bytes]u8 = undefined;
133133 const joined_path = if (p.sub_path.len == 0) sub_path else p: {
134134 break :p std.fmt.bufPrint(&buf, "{s}" ++ fs.path.sep_str ++ "{s}", .{
135135 p.sub_path, sub_path,
136136 }) catch return error.NameTooLong;
137137 };
138 return p.root_dir.handle.makePath(joined_path);
138 return p.root_dir.handle.makePath(io, joined_path);
139139}
140140
141141pub fn toString(p: Path, allocator: Allocator) Allocator.Error![]u8 {
lib/std/Build/Step/ConfigHeader.zig+2-1
......@@ -184,6 +184,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
184184
185185 const gpa = b.allocator;
186186 const arena = b.allocator;
187 const io = b.graph.io;
187188
188189 var man = b.graph.cache.obtain();
189190 defer man.deinit();
......@@ -257,7 +258,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
257258 const sub_path = b.pathJoin(&.{ "o", &digest, config_header.include_path });
258259 const sub_path_dirname = std.fs.path.dirname(sub_path).?;
259260
260 b.cache_root.handle.makePath(sub_path_dirname) catch |err| {
261 b.cache_root.handle.makePath(io, sub_path_dirname) catch |err| {
261262 return step.fail("unable to make path '{f}{s}': {s}", .{
262263 b.cache_root, sub_path_dirname, @errorName(err),
263264 });
lib/std/Build/Step/ObjCopy.zig+2-1
......@@ -143,6 +143,7 @@ pub fn getOutputSeparatedDebug(objcopy: *const ObjCopy) ?std.Build.LazyPath {
143143fn make(step: *Step, options: Step.MakeOptions) !void {
144144 const prog_node = options.progress_node;
145145 const b = step.owner;
146 const io = b.graph.io;
146147 const objcopy: *ObjCopy = @fieldParentPtr("step", step);
147148 try step.singleUnchangingWatchInput(objcopy.input_file);
148149
......@@ -176,7 +177,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
176177 const cache_path = "o" ++ fs.path.sep_str ++ digest;
177178 const full_dest_path = try b.cache_root.join(b.allocator, &.{ cache_path, objcopy.basename });
178179 const full_dest_path_debug = try b.cache_root.join(b.allocator, &.{ cache_path, b.fmt("{s}.debug", .{objcopy.basename}) });
179 b.cache_root.handle.makePath(cache_path) catch |err| {
180 b.cache_root.handle.makePath(io, cache_path) catch |err| {
180181 return step.fail("unable to make path {s}: {s}", .{ cache_path, @errorName(err) });
181182 };
182183
lib/std/Build/Step/Run.zig+4-3
......@@ -973,7 +973,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
973973 .output_directory => output_sub_path,
974974 else => unreachable,
975975 };
976 b.cache_root.handle.makePath(output_sub_dir_path) catch |err| {
976 b.cache_root.handle.makePath(io, output_sub_dir_path) catch |err| {
977977 return step.fail("unable to make path '{f}{s}': {s}", .{
978978 b.cache_root, output_sub_dir_path, @errorName(err),
979979 });
......@@ -1005,7 +1005,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
10051005 .output_directory => output_sub_path,
10061006 else => unreachable,
10071007 };
1008 b.cache_root.handle.makePath(output_sub_dir_path) catch |err| {
1008 b.cache_root.handle.makePath(io, output_sub_dir_path) catch |err| {
10091009 return step.fail("unable to make path '{f}{s}': {s}", .{
10101010 b.cache_root, output_sub_dir_path, @errorName(err),
10111011 });
......@@ -1241,6 +1241,7 @@ fn runCommand(
12411241 const b = step.owner;
12421242 const arena = b.allocator;
12431243 const gpa = options.gpa;
1244 const io = b.graph.io;
12441245
12451246 const cwd: ?[]const u8 = if (run.cwd) |lazy_cwd| lazy_cwd.getPath2(b, step) else null;
12461247
......@@ -1470,7 +1471,7 @@ fn runCommand(
14701471
14711472 const sub_path = b.pathJoin(&output_components);
14721473 const sub_path_dirname = fs.path.dirname(sub_path).?;
1473 b.cache_root.handle.makePath(sub_path_dirname) catch |err| {
1474 b.cache_root.handle.makePath(io, sub_path_dirname) catch |err| {
14741475 return step.fail("unable to make path '{f}{s}': {s}", .{
14751476 b.cache_root, sub_path_dirname, @errorName(err),
14761477 });
lib/std/Build/Step/UpdateSourceFiles.zig+1-1
......@@ -78,7 +78,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
7878 var any_miss = false;
7979 for (usf.output_source_files.items) |output_source_file| {
8080 if (fs.path.dirname(output_source_file.sub_path)) |dirname| {
81 b.build_root.handle.makePath(dirname) catch |err| {
81 b.build_root.handle.makePath(io, dirname) catch |err| {
8282 return step.fail("unable to make path '{f}{s}': {t}", .{ b.build_root, dirname, err });
8383 };
8484 }
lib/std/Build/Step/WriteFile.zig+3-3
......@@ -268,7 +268,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
268268
269269 for (write_file.files.items) |file| {
270270 if (fs.path.dirname(file.sub_path)) |dirname| {
271 cache_dir.makePath(dirname) catch |err| {
271 cache_dir.makePath(io, dirname) catch |err| {
272272 return step.fail("unable to make path '{f}{s}{c}{s}': {t}", .{
273273 b.cache_root, cache_path, fs.path.sep, dirname, err,
274274 });
......@@ -303,7 +303,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
303303 const dest_dirname = dir.sub_path;
304304
305305 if (dest_dirname.len != 0) {
306 cache_dir.makePath(dest_dirname) catch |err| {
306 cache_dir.makePath(io, dest_dirname) catch |err| {
307307 return step.fail("unable to make path '{f}{s}{c}{s}': {s}", .{
308308 b.cache_root, cache_path, fs.path.sep, dest_dirname, @errorName(err),
309309 });
......@@ -318,7 +318,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void {
318318 const src_entry_path = try src_dir_path.join(arena, entry.path);
319319 const dest_path = b.pathJoin(&.{ dest_dirname, entry.path });
320320 switch (entry.kind) {
321 .directory => try cache_dir.makePath(dest_path),
321 .directory => try cache_dir.makePath(io, dest_path),
322322 .file => {
323323 const prev_status = Io.Dir.updateFile(
324324 src_entry_path.root_dir.handle,
lib/std/debug.zig+7-5
......@@ -522,7 +522,7 @@ pub fn defaultPanic(
522522 }
523523 @trap();
524524 },
525 .cuda, .amdhsa => std.posix.abort(),
525 .cuda, .amdhsa => std.process.abort(),
526526 .plan9 => {
527527 var status: [std.os.plan9.ERRMAX]u8 = undefined;
528528 const len = @min(msg.len, status.len - 1);
......@@ -575,12 +575,13 @@ pub fn defaultPanic(
575575 // A panic happened while trying to print a previous panic message.
576576 // We're still holding the mutex but that's fine as we're going to
577577 // call abort().
578 File.stderr().writeStreamingAll("aborting due to recursive panic\n") catch {};
578 const stderr, _ = lockStderrWriter(&.{});
579 stderr.writeAll("aborting due to recursive panic\n") catch {};
579580 },
580581 else => {}, // Panicked while printing the recursive panic message.
581582 }
582583
583 posix.abort();
584 std.process.abort();
584585}
585586
586587/// Must be called only after adding 1 to `panicking`. There are three callsites.
......@@ -1596,7 +1597,8 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex
15961597 // A segfault happened while trying to print a previous panic message.
15971598 // We're still holding the mutex but that's fine as we're going to
15981599 // call abort().
1599 File.stderr().writeAll("aborting due to recursive panic\n") catch {};
1600 const stderr, _ = lockStderrWriter(&.{});
1601 stderr().writeAll("aborting due to recursive panic\n") catch {};
16001602 },
16011603 else => {}, // Panicked while printing the recursive panic message.
16021604 }
......@@ -1604,7 +1606,7 @@ pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContex
16041606 // We cannot allow the signal handler to return because when it runs the original instruction
16051607 // again, the memory may be mapped and undefined behavior would occur rather than repeating
16061608 // the segfault. So we simply abort here.
1607 posix.abort();
1609 std.process.abort();
16081610}
16091611
16101612pub fn dumpStackPointerAddr(prefix: []const u8) void {
lib/std/fs/test.zig+27-18
......@@ -674,7 +674,7 @@ test "Dir.Iterator but dir is deleted during iteration" {
674674 var iterator = subdir.iterate();
675675
676676 // Create something to iterate over within the subdir
677 try tmp.dir.makePath("subdir" ++ fs.path.sep_str ++ "b");
677 try tmp.dir.makePath(io, "subdir" ++ fs.path.sep_str ++ "b");
678678
679679 // Then, before iterating, delete the directory that we're iterating.
680680 // This is a contrived reproduction, but this could happen outside of the program, in another thread, etc.
......@@ -1196,7 +1196,7 @@ test "deleteTree does not follow symlinks" {
11961196 var tmp = tmpDir(.{});
11971197 defer tmp.cleanup();
11981198
1199 try tmp.dir.makePath("b");
1199 try tmp.dir.makePath(io, "b");
12001200 {
12011201 var a = try tmp.dir.makeOpenPath("a", .{});
12021202 defer a.close(io);
......@@ -1211,6 +1211,8 @@ test "deleteTree does not follow symlinks" {
12111211}
12121212
12131213test "deleteTree on a symlink" {
1214 const io = testing.io;
1215
12141216 var tmp = tmpDir(.{});
12151217 defer tmp.cleanup();
12161218
......@@ -1223,7 +1225,7 @@ test "deleteTree on a symlink" {
12231225 try tmp.dir.access("file", .{});
12241226
12251227 // Symlink to a directory
1226 try tmp.dir.makePath("dir");
1228 try tmp.dir.makePath(io, "dir");
12271229 try setupSymlink(tmp.dir, "dir", "dirlink", .{ .is_directory = true });
12281230
12291231 try tmp.dir.deleteTree("dirlink");
......@@ -1238,7 +1240,7 @@ test "makePath, put some files in it, deleteTree" {
12381240 const allocator = ctx.arena.allocator();
12391241 const dir_path = try ctx.transformPath("os_test_tmp");
12401242
1241 try ctx.dir.makePath(try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c" }));
1243 try ctx.dir.makePath(io, try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c" }));
12421244 try ctx.dir.writeFile(.{
12431245 .sub_path = try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c", "file.txt" }),
12441246 .data = "nonsense",
......@@ -1261,7 +1263,7 @@ test "makePath, put some files in it, deleteTreeMinStackSize" {
12611263 const allocator = ctx.arena.allocator();
12621264 const dir_path = try ctx.transformPath("os_test_tmp");
12631265
1264 try ctx.dir.makePath(try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c" }));
1266 try ctx.dir.makePath(io, try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c" }));
12651267 try ctx.dir.writeFile(.{
12661268 .sub_path = try fs.path.join(allocator, &.{ "os_test_tmp", "b", "c", "file.txt" }),
12671269 .data = "nonsense",
......@@ -1280,21 +1282,25 @@ test "makePath, put some files in it, deleteTreeMinStackSize" {
12801282test "makePath in a directory that no longer exists" {
12811283 if (native_os == .windows) return error.SkipZigTest; // Windows returns FileBusy if attempting to remove an open dir
12821284
1285 const io = testing.io;
1286
12831287 var tmp = tmpDir(.{});
12841288 defer tmp.cleanup();
12851289 try tmp.parent_dir.deleteTree(&tmp.sub_path);
12861290
1287 try testing.expectError(error.FileNotFound, tmp.dir.makePath("sub-path"));
1291 try testing.expectError(error.FileNotFound, tmp.dir.makePath(io, "sub-path"));
12881292}
12891293
12901294test "makePath but sub_path contains pre-existing file" {
1295 const io = testing.io;
1296
12911297 var tmp = tmpDir(.{});
12921298 defer tmp.cleanup();
12931299
12941300 try tmp.dir.makeDir("foo");
12951301 try tmp.dir.writeFile(.{ .sub_path = "foo/bar", .data = "" });
12961302
1297 try testing.expectError(error.NotDir, tmp.dir.makePath("foo/bar/baz"));
1303 try testing.expectError(error.NotDir, tmp.dir.makePath(io, "foo/bar/baz"));
12981304}
12991305
13001306fn expectDir(io: Io, dir: Dir, path: []const u8) !void {
......@@ -1314,7 +1320,7 @@ test "makepath existing directories" {
13141320 try tmpA.makeDir("B");
13151321
13161322 const testPath = "A" ++ fs.path.sep_str ++ "B" ++ fs.path.sep_str ++ "C";
1317 try tmp.dir.makePath(testPath);
1323 try tmp.dir.makePath(io, testPath);
13181324
13191325 try expectDir(io, tmp.dir, testPath);
13201326}
......@@ -1328,7 +1334,7 @@ test "makepath through existing valid symlink" {
13281334 try tmp.dir.makeDir("realfolder");
13291335 try setupSymlink(tmp.dir, "." ++ fs.path.sep_str ++ "realfolder", "working-symlink", .{});
13301336
1331 try tmp.dir.makePath("working-symlink" ++ fs.path.sep_str ++ "in-realfolder");
1337 try tmp.dir.makePath(io, "working-symlink" ++ fs.path.sep_str ++ "in-realfolder");
13321338
13331339 try expectDir(io, tmp.dir, "realfolder" ++ fs.path.sep_str ++ "in-realfolder");
13341340}
......@@ -1344,7 +1350,7 @@ test "makepath relative walks" {
13441350 });
13451351 defer testing.allocator.free(relPath);
13461352
1347 try tmp.dir.makePath(relPath);
1353 try tmp.dir.makePath(io, relPath);
13481354
13491355 // How .. is handled is different on Windows than non-Windows
13501356 switch (native_os) {
......@@ -1383,7 +1389,7 @@ test "makepath ignores '.'" {
13831389 });
13841390 defer testing.allocator.free(expectedPath);
13851391
1386 try tmp.dir.makePath(dotPath);
1392 try tmp.dir.makePath(io, dotPath);
13871393
13881394 try expectDir(io, tmp.dir, expectedPath);
13891395}
......@@ -1550,10 +1556,11 @@ test "setEndPos" {
15501556test "access file" {
15511557 try testWithAllSupportedPathTypes(struct {
15521558 fn impl(ctx: *TestContext) !void {
1559 const io = ctx.io;
15531560 const dir_path = try ctx.transformPath("os_test_tmp");
15541561 const file_path = try ctx.transformPath("os_test_tmp" ++ fs.path.sep_str ++ "file.txt");
15551562
1556 try ctx.dir.makePath(dir_path);
1563 try ctx.dir.makePath(io, dir_path);
15571564 try testing.expectError(error.FileNotFound, ctx.dir.access(file_path, .{}));
15581565
15591566 try ctx.dir.writeFile(.{ .sub_path = file_path, .data = "" });
......@@ -1569,7 +1576,7 @@ test "sendfile" {
15691576 var tmp = tmpDir(.{});
15701577 defer tmp.cleanup();
15711578
1572 try tmp.dir.makePath("os_test_tmp");
1579 try tmp.dir.makePath(io, "os_test_tmp");
15731580
15741581 var dir = try tmp.dir.openDir(io, "os_test_tmp", .{});
15751582 defer dir.close(io);
......@@ -1616,7 +1623,7 @@ test "sendfile with buffered data" {
16161623 var tmp = tmpDir(.{});
16171624 defer tmp.cleanup();
16181625
1619 try tmp.dir.makePath("os_test_tmp");
1626 try tmp.dir.makePath(io, "os_test_tmp");
16201627
16211628 var dir = try tmp.dir.openDir(io, "os_test_tmp", .{});
16221629 defer dir.close(io);
......@@ -1894,7 +1901,7 @@ test "walker" {
18941901 });
18951902
18961903 for (expected_paths.keys()) |key| {
1897 try tmp.dir.makePath(key);
1904 try tmp.dir.makePath(io, key);
18981905 }
18991906
19001907 var walker = try tmp.dir.walk(testing.allocator);
......@@ -1956,7 +1963,7 @@ test "selective walker, skip entries that start with ." {
19561963 });
19571964
19581965 for (paths_to_create) |path| {
1959 try tmp.dir.makePath(path);
1966 try tmp.dir.makePath(io, path);
19601967 }
19611968
19621969 var walker = try tmp.dir.walkSelectively(testing.allocator);
......@@ -1991,6 +1998,8 @@ test "selective walker, skip entries that start with ." {
19911998}
19921999
19932000test "walker without fully iterating" {
2001 const io = testing.io;
2002
19942003 var tmp = tmpDir(.{ .iterate = true });
19952004 defer tmp.cleanup();
19962005
......@@ -2000,8 +2009,8 @@ test "walker without fully iterating" {
20002009 // Create 2 directories inside the tmp directory, but then only iterate once before breaking.
20012010 // This ensures that walker doesn't try to close the initial directory when not fully iterating.
20022011
2003 try tmp.dir.makePath("a");
2004 try tmp.dir.makePath("b");
2012 try tmp.dir.makePath(io, "a");
2013 try tmp.dir.makePath(io, "b");
20052014
20062015 var num_walked: usize = 0;
20072016 while (try walker.next()) |_| {
lib/std/os/linux/IoUring.zig-26
......@@ -4090,32 +4090,6 @@ test "openat_direct/close_direct" {
40904090 try ring.unregister_files();
40914091}
40924092
4093test "waitid" {
4094 try skipKernelLessThan(.{ .major = 6, .minor = 7, .patch = 0 });
4095
4096 var ring = IoUring.init(16, 0) catch |err| switch (err) {
4097 error.SystemOutdated => return error.SkipZigTest,
4098 error.PermissionDenied => return error.SkipZigTest,
4099 else => return err,
4100 };
4101 defer ring.deinit();
4102
4103 const pid = try posix.fork();
4104 if (pid == 0) {
4105 posix.exit(7);
4106 }
4107
4108 var siginfo: posix.siginfo_t = undefined;
4109 _ = try ring.waitid(0, .PID, pid, &siginfo, posix.W.EXITED, 0);
4110
4111 try testing.expectEqual(1, try ring.submit());
4112
4113 const cqe_waitid = try ring.copy_cqe();
4114 try testing.expectEqual(0, cqe_waitid.res);
4115 try testing.expectEqual(pid, siginfo.fields.common.first.piduid.pid);
4116 try testing.expectEqual(7, siginfo.fields.common.second.sigchld.status);
4117}
4118
41194093/// For use in tests. Returns SkipZigTest if kernel version is less than required.
41204094inline fn skipKernelLessThan(required: std.SemanticVersion) !void {
41214095 if (!is_linux) return error.SkipZigTest;
lib/std/posix.zig-87
......@@ -615,66 +615,6 @@ fn getRandomBytesDevURandom(buf: []u8) GetRandomError!void {
615615 }
616616}
617617
618/// Causes abnormal process termination.
619/// If linking against libc, this calls the abort() libc function. Otherwise
620/// it raises SIGABRT followed by SIGKILL and finally lo
621/// Invokes the current signal handler for SIGABRT, if any.
622pub fn abort() noreturn {
623 @branchHint(.cold);
624 // MSVCRT abort() sometimes opens a popup window which is undesirable, so
625 // even when linking libc on Windows we use our own abort implementation.
626 // See https://github.com/ziglang/zig/issues/2071 for more details.
627 if (native_os == .windows) {
628 if (builtin.mode == .Debug and windows.peb().BeingDebugged != 0) {
629 @breakpoint();
630 }
631 windows.ntdll.RtlExitUserProcess(3);
632 }
633 if (!builtin.link_libc and native_os == .linux) {
634 // The Linux man page says that the libc abort() function
635 // "first unblocks the SIGABRT signal", but this is a footgun
636 // for user-defined signal handlers that want to restore some state in
637 // some program sections and crash in others.
638 // So, the user-installed SIGABRT handler is run, if present.
639 raise(.ABRT) catch {};
640
641 // Disable all signal handlers.
642 const filledset = linux.sigfillset();
643 sigprocmask(SIG.BLOCK, &filledset, null);
644
645 // Only one thread may proceed to the rest of abort().
646 if (!builtin.single_threaded) {
647 const global = struct {
648 var abort_entered: bool = false;
649 };
650 while (@cmpxchgWeak(bool, &global.abort_entered, false, true, .seq_cst, .seq_cst)) |_| {}
651 }
652
653 // Install default handler so that the tkill below will terminate.
654 const sigact = Sigaction{
655 .handler = .{ .handler = SIG.DFL },
656 .mask = sigemptyset(),
657 .flags = 0,
658 };
659 sigaction(.ABRT, &sigact, null);
660
661 _ = linux.tkill(linux.gettid(), .ABRT);
662
663 var sigabrtmask = sigemptyset();
664 sigaddset(&sigabrtmask, .ABRT);
665 sigprocmask(SIG.UNBLOCK, &sigabrtmask, null);
666
667 // Beyond this point should be unreachable.
668 @as(*allowzero volatile u8, @ptrFromInt(0)).* = 0;
669 raise(.KILL) catch {};
670 exit(127); // Pid 1 might not be signalled in some containers.
671 }
672 switch (native_os) {
673 .uefi, .wasi, .emscripten, .cuda, .amdhsa => @trap(),
674 else => system.abort(),
675 }
676}
677
678618pub const RaiseError = UnexpectedError;
679619
680620pub fn raise(sig: SIG) RaiseError!void {
......@@ -715,33 +655,6 @@ pub fn kill(pid: pid_t, sig: SIG) KillError!void {
715655 }
716656}
717657
718/// Exits all threads of the program with the specified status code.
719pub fn exit(status: u8) noreturn {
720 if (builtin.link_libc) {
721 std.c.exit(status);
722 }
723 if (native_os == .windows) {
724 windows.ntdll.RtlExitUserProcess(status);
725 }
726 if (native_os == .wasi) {
727 wasi.proc_exit(status);
728 }
729 if (native_os == .linux and !builtin.single_threaded) {
730 linux.exit_group(status);
731 }
732 if (native_os == .uefi) {
733 const uefi = std.os.uefi;
734 // exit() is only available if exitBootServices() has not been called yet.
735 // This call to exit should not fail, so we catch-ignore errors.
736 if (uefi.system_table.boot_services) |bs| {
737 bs.exit(uefi.handle, @enumFromInt(status), null) catch {};
738 }
739 // If we can't exit, reboot the system instead.
740 uefi.system_table.runtime_services.resetSystem(.cold, @enumFromInt(status), null);
741 }
742 system.exit(status);
743}
744
745658pub const ReadError = std.Io.File.Reader.Error;
746659
747660/// Returns the number of bytes that were read, which can be less than
lib/std/posix/test.zig-67
......@@ -667,73 +667,6 @@ test "writev longer than IOV_MAX" {
667667 try testing.expectEqual(@as(usize, posix.IOV_MAX), amt);
668668}
669669
670test "POSIX file locking with fcntl" {
671 if (native_os == .windows or native_os == .wasi) {
672 // Not POSIX.
673 return error.SkipZigTest;
674 }
675
676 if (true) {
677 // https://github.com/ziglang/zig/issues/11074
678 return error.SkipZigTest;
679 }
680
681 const io = testing.io;
682
683 var tmp = tmpDir(.{});
684 defer tmp.cleanup();
685
686 // Create a temporary lock file
687 var file = try tmp.dir.createFile(io, "lock", .{ .read = true });
688 defer file.close(io);
689 try file.setEndPos(2);
690 const fd = file.handle;
691
692 // Place an exclusive lock on the first byte, and a shared lock on the second byte:
693 var struct_flock = std.mem.zeroInit(posix.Flock, .{ .type = posix.F.WRLCK });
694 _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock));
695 struct_flock.start = 1;
696 struct_flock.type = posix.F.RDLCK;
697 _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock));
698
699 // Check the locks in a child process:
700 const pid = try posix.fork();
701 if (pid == 0) {
702 // child expects be denied the exclusive lock:
703 struct_flock.start = 0;
704 struct_flock.type = posix.F.WRLCK;
705 try expectError(error.Locked, posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock)));
706 // child expects to get the shared lock:
707 struct_flock.start = 1;
708 struct_flock.type = posix.F.RDLCK;
709 _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock));
710 // child waits for the exclusive lock in order to test deadlock:
711 struct_flock.start = 0;
712 struct_flock.type = posix.F.WRLCK;
713 _ = try posix.fcntl(fd, posix.F.SETLKW, @intFromPtr(&struct_flock));
714 // child exits without continuing:
715 posix.exit(0);
716 } else {
717 // parent waits for child to get shared lock:
718 std.Thread.sleep(1 * std.time.ns_per_ms);
719 // parent expects deadlock when attempting to upgrade the shared lock to exclusive:
720 struct_flock.start = 1;
721 struct_flock.type = posix.F.WRLCK;
722 try expectError(error.DeadLock, posix.fcntl(fd, posix.F.SETLKW, @intFromPtr(&struct_flock)));
723 // parent releases exclusive lock:
724 struct_flock.start = 0;
725 struct_flock.type = posix.F.UNLCK;
726 _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock));
727 // parent releases shared lock:
728 struct_flock.start = 1;
729 struct_flock.type = posix.F.UNLCK;
730 _ = try posix.fcntl(fd, posix.F.SETLK, @intFromPtr(&struct_flock));
731 // parent waits for child:
732 const result = posix.waitpid(pid, 0);
733 try expect(result.status == 0 * 256);
734 }
735}
736
737670test "rename smoke test" {
738671 if (native_os == .wasi) return error.SkipZigTest;
739672 if (native_os == .windows) return error.SkipZigTest;
lib/std/process.zig+84-2
......@@ -16,8 +16,6 @@ const unicode = std.unicode;
1616const max_path_bytes = std.fs.max_path_bytes;
1717
1818pub const Child = @import("process/Child.zig");
19pub const abort = posix.abort;
20pub const exit = posix.exit;
2119pub const changeCurDir = posix.chdir;
2220pub const changeCurDirZ = posix.chdirZ;
2321
......@@ -2208,3 +2206,87 @@ pub const OpenExecutableError = File.OpenError || ExecutablePathError || File.Lo
22082206pub fn openExecutable(io: Io, flags: File.OpenFlags) OpenExecutableError!File {
22092207 return io.vtable.processExecutableOpen(io.userdata, flags);
22102208}
2209
2210/// Causes abnormal process termination.
2211///
2212/// If linking against libc, this calls `std.c.abort`. Otherwise it raises
2213/// SIGABRT followed by SIGKILL.
2214///
2215/// Invokes the current signal handler for SIGABRT, if any.
2216pub fn abort() noreturn {
2217 @branchHint(.cold);
2218 // MSVCRT abort() sometimes opens a popup window which is undesirable, so
2219 // even when linking libc on Windows we use our own abort implementation.
2220 // See https://github.com/ziglang/zig/issues/2071 for more details.
2221 if (native_os == .windows) {
2222 if (builtin.mode == .Debug and windows.peb().BeingDebugged != 0) {
2223 @breakpoint();
2224 }
2225 windows.ntdll.RtlExitUserProcess(3);
2226 }
2227 if (!builtin.link_libc and native_os == .linux) {
2228 // The Linux man page says that the libc abort() function
2229 // "first unblocks the SIGABRT signal", but this is a footgun
2230 // for user-defined signal handlers that want to restore some state in
2231 // some program sections and crash in others.
2232 // So, the user-installed SIGABRT handler is run, if present.
2233 posix.raise(.ABRT) catch {};
2234
2235 // Disable all signal handlers.
2236 const filledset = std.os.linux.sigfillset();
2237 posix.sigprocmask(posix.SIG.BLOCK, &filledset, null);
2238
2239 // Only one thread may proceed to the rest of abort().
2240 if (!builtin.single_threaded) {
2241 const global = struct {
2242 var abort_entered: bool = false;
2243 };
2244 while (@cmpxchgWeak(bool, &global.abort_entered, false, true, .seq_cst, .seq_cst)) |_| {}
2245 }
2246
2247 // Install default handler so that the tkill below will terminate.
2248 const sigact: posix.Sigaction = .{
2249 .handler = .{ .handler = posix.SIG.DFL },
2250 .mask = posix.sigemptyset(),
2251 .flags = 0,
2252 };
2253 posix.sigaction(.ABRT, &sigact, null);
2254
2255 _ = std.os.linux.tkill(std.os.linux.gettid(), .ABRT);
2256
2257 var sigabrtmask = posix.sigemptyset();
2258 posix.sigaddset(&sigabrtmask, .ABRT);
2259 posix.sigprocmask(posix.SIG.UNBLOCK, &sigabrtmask, null);
2260
2261 // Beyond this point should be unreachable.
2262 @as(*allowzero volatile u8, @ptrFromInt(0)).* = 0;
2263 posix.raise(.KILL) catch {};
2264 exit(127); // Pid 1 might not be signalled in some containers.
2265 }
2266 switch (native_os) {
2267 .uefi, .wasi, .emscripten, .cuda, .amdhsa => @trap(),
2268 else => posix.system.abort(),
2269 }
2270}
2271
2272/// Exits all threads of the program with the specified status code.
2273pub fn exit(status: u8) noreturn {
2274 if (builtin.link_libc) {
2275 std.c.exit(status);
2276 } else switch (native_os) {
2277 .windows => windows.ntdll.RtlExitUserProcess(status),
2278 .wasi => std.os.wasi.proc_exit(status),
2279 .linux => if (!builtin.single_threaded) std.os.linux.exit_group(status),
2280 .uefi => {
2281 const uefi = std.os.uefi;
2282 // exit() is only available if exitBootServices() has not been called yet.
2283 // This call to exit should not fail, so we catch-ignore errors.
2284 if (uefi.system_table.boot_services) |bs| {
2285 bs.exit(uefi.handle, @enumFromInt(status), null) catch {};
2286 }
2287 // If we can't exit, reboot the system instead.
2288 uefi.system_table.runtime_services.resetSystem(.cold, @enumFromInt(status), null);
2289 },
2290 else => posix.system.exit(status),
2291 }
2292}
lib/std/process/Child.zig+1-1
......@@ -1050,7 +1050,7 @@ fn forkChildErrReport(fd: i32, err: ChildProcess.SpawnError) noreturn {
10501050 // The _exit(2) function does nothing but make the exit syscall, unlike exit(3)
10511051 std.c._exit(1);
10521052 }
1053 posix.exit(1);
1053 posix.system.exit(1);
10541054}
10551055
10561056fn writeIntFd(fd: i32, value: ErrInt) !void {
lib/std/start.zig+3-3
......@@ -110,7 +110,7 @@ fn main2() callconv(.c) c_int {
110110}
111111
112112fn _start2() callconv(.withStackAlign(.c, 1)) noreturn {
113 std.posix.exit(callMain());
113 std.process.exit(callMain());
114114}
115115
116116fn spirvMain2() callconv(.kernel) void {
......@@ -118,7 +118,7 @@ fn spirvMain2() callconv(.kernel) void {
118118}
119119
120120fn wWinMainCRTStartup2() callconv(.c) noreturn {
121 std.posix.exit(callMain());
121 std.process.exit(callMain());
122122}
123123
124124////////////////////////////////////////////////////////////////////////////////
......@@ -627,7 +627,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn {
627627 for (slice) |func| func();
628628 }
629629
630 std.posix.exit(callMainWithArgs(argc, argv, envp));
630 std.process.exit(callMainWithArgs(argc, argv, envp));
631631}
632632
633633fn expandStackSize(phdrs: []elf.Phdr) void {
lib/std/tar.zig+8-8
......@@ -606,7 +606,7 @@ pub fn pipeToFileSystem(io: Io, dir: Io.Dir, reader: *Io.Reader, options: PipeOp
606606 switch (file.kind) {
607607 .directory => {
608608 if (file_name.len > 0 and !options.exclude_empty_directories) {
609 try dir.makePath(file_name);
609 try dir.makePath(io, file_name);
610610 }
611611 },
612612 .file => {
......@@ -625,7 +625,7 @@ pub fn pipeToFileSystem(io: Io, dir: Io.Dir, reader: *Io.Reader, options: PipeOp
625625 },
626626 .sym_link => {
627627 const link_name = file.link_name;
628 createDirAndSymlink(dir, link_name, file_name) catch |err| {
628 createDirAndSymlink(io, dir, link_name, file_name) catch |err| {
629629 const d = options.diagnostics orelse return error.UnableToCreateSymLink;
630630 try d.errors.append(d.allocator, .{ .unable_to_create_sym_link = .{
631631 .code = err,
......@@ -642,7 +642,7 @@ fn createDirAndFile(io: Io, dir: Io.Dir, file_name: []const u8, mode: Io.File.Mo
642642 const fs_file = dir.createFile(io, file_name, .{ .exclusive = true, .mode = mode }) catch |err| {
643643 if (err == error.FileNotFound) {
644644 if (std.fs.path.dirname(file_name)) |dir_name| {
645 try dir.makePath(dir_name);
645 try dir.makePath(io, dir_name);
646646 return try dir.createFile(io, file_name, .{ .exclusive = true, .mode = mode });
647647 }
648648 }
......@@ -652,11 +652,11 @@ fn createDirAndFile(io: Io, dir: Io.Dir, file_name: []const u8, mode: Io.File.Mo
652652}
653653
654654// Creates a symbolic link at path `file_name` which points to `link_name`.
655fn createDirAndSymlink(dir: Io.Dir, link_name: []const u8, file_name: []const u8) !void {
655fn createDirAndSymlink(io: Io, dir: Io.Dir, link_name: []const u8, file_name: []const u8) !void {
656656 dir.symLink(link_name, file_name, .{}) catch |err| {
657657 if (err == error.FileNotFound) {
658658 if (std.fs.path.dirname(file_name)) |dir_name| {
659 try dir.makePath(dir_name);
659 try dir.makePath(io, dir_name);
660660 return try dir.symLink(link_name, file_name, .{});
661661 }
662662 }
......@@ -885,15 +885,15 @@ test "create file and symlink" {
885885 file = try createDirAndFile(io, root.dir, "a/b/c/file2", default_mode);
886886 file.close(io);
887887
888 createDirAndSymlink(root.dir, "a/b/c/file2", "symlink1") catch |err| {
888 createDirAndSymlink(io, root.dir, "a/b/c/file2", "symlink1") catch |err| {
889889 // On Windows when developer mode is not enabled
890890 if (err == error.AccessDenied) return error.SkipZigTest;
891891 return err;
892892 };
893 try createDirAndSymlink(root.dir, "../../../file1", "d/e/f/symlink2");
893 try createDirAndSymlink(io, root.dir, "../../../file1", "d/e/f/symlink2");
894894
895895 // Danglink symlnik, file created later
896 try createDirAndSymlink(root.dir, "../../../g/h/i/file4", "j/k/l/symlink3");
896 try createDirAndSymlink(io, root.dir, "../../../g/h/i/file4", "j/k/l/symlink3");
897897 file = try createDirAndFile(io, root.dir, "g/h/i/file4", default_mode);
898898 file.close(io);
899899}
lib/std/zip.zig+3-3
......@@ -464,6 +464,8 @@ pub const Iterator = struct {
464464 filename_buf: []u8,
465465 dest: Io.Dir,
466466 ) !void {
467 const io = stream.io;
468
467469 if (filename_buf.len < self.filename_len)
468470 return error.ZipInsufficientBuffer;
469471 switch (self.compression_method) {
......@@ -552,12 +554,10 @@ pub const Iterator = struct {
552554 if (filename[filename.len - 1] == '/') {
553555 if (self.uncompressed_size != 0)
554556 return error.ZipBadDirectorySize;
555 try dest.makePath(filename[0 .. filename.len - 1]);
557 try dest.makePath(io, filename[0 .. filename.len - 1]);
556558 return;
557559 }
558560
559 const io = stream.io;
560
561561 const out_file = blk: {
562562 if (std.fs.path.dirname(filename)) |dirname| {
563563 var parent_dir = try dest.makeOpenPath(dirname, .{});
src/Compilation.zig+6-4
......@@ -3180,7 +3180,7 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) UpdateE
31803180 const s = fs.path.sep_str;
31813181 const tmp_dir_sub_path = "tmp" ++ s ++ std.fmt.hex(tmp_dir_rand_int);
31823182 const o_sub_path = "o" ++ s ++ hex_digest;
3183 renameTmpIntoCache(comp.dirs.local_cache, tmp_dir_sub_path, o_sub_path) catch |err| {
3183 renameTmpIntoCache(io, comp.dirs.local_cache, tmp_dir_sub_path, o_sub_path) catch |err| {
31843184 return comp.setMiscFailure(
31853185 .rename_results,
31863186 "failed to rename compilation results ('{f}{s}') into local cache ('{f}{s}'): {t}",
......@@ -3399,17 +3399,19 @@ fn flush(
33993399/// implementation at the bottom of this function.
34003400/// This function is only called when CacheMode is `whole`.
34013401fn renameTmpIntoCache(
3402 io: Io,
34023403 cache_directory: Cache.Directory,
34033404 tmp_dir_sub_path: []const u8,
34043405 o_sub_path: []const u8,
34053406) !void {
34063407 var seen_eaccess = false;
34073408 while (true) {
3408 fs.rename(
3409 Io.Dir.rename(
34093410 cache_directory.handle,
34103411 tmp_dir_sub_path,
34113412 cache_directory.handle,
34123413 o_sub_path,
3414 io,
34133415 ) catch |err| switch (err) {
34143416 // On Windows, rename fails with `AccessDenied` rather than `PathAlreadyExists`.
34153417 // See https://github.com/ziglang/zig/issues/8362
......@@ -3427,7 +3429,7 @@ fn renameTmpIntoCache(
34273429 continue;
34283430 },
34293431 error.FileNotFound => {
3430 try cache_directory.handle.makePath("o");
3432 try cache_directory.handle.makePath(io, "o");
34313433 continue;
34323434 },
34333435 else => |e| return e,
......@@ -5816,7 +5818,7 @@ pub fn translateC(
58165818 const o_sub_path = "o" ++ fs.path.sep_str ++ hex_digest;
58175819
58185820 if (comp.verbose_cimport) log.info("renaming {s} to {s}", .{ tmp_sub_path, o_sub_path });
5819 try renameTmpIntoCache(comp.dirs.local_cache, tmp_sub_path, o_sub_path);
5821 try renameTmpIntoCache(io, comp.dirs.local_cache, tmp_sub_path, o_sub_path);
58205822
58215823 return .{
58225824 .digest = bin_digest,
src/Package/Fetch.zig+3-2
......@@ -1414,6 +1414,7 @@ fn unpackGitPack(f: *Fetch, out_dir: Io.Dir, resource: *Resource.Git) anyerror!U
14141414
14151415fn recursiveDirectoryCopy(f: *Fetch, dir: Io.Dir, tmp_dir: Io.Dir) anyerror!void {
14161416 const gpa = f.arena.child_allocator;
1417 const io = f.job_queue.io;
14171418 // Recursive directory copy.
14181419 var it = try dir.walk(gpa);
14191420 defer it.deinit();
......@@ -1428,7 +1429,7 @@ fn recursiveDirectoryCopy(f: *Fetch, dir: Io.Dir, tmp_dir: Io.Dir) anyerror!void
14281429 .{},
14291430 ) catch |err| switch (err) {
14301431 error.FileNotFound => {
1431 if (fs.path.dirname(entry.path)) |dirname| try tmp_dir.makePath(dirname);
1432 if (fs.path.dirname(entry.path)) |dirname| try tmp_dir.makePath(io, dirname);
14321433 try dir.copyFile(entry.path, tmp_dir, entry.path, .{});
14331434 },
14341435 else => |e| return e,
......@@ -1441,7 +1442,7 @@ fn recursiveDirectoryCopy(f: *Fetch, dir: Io.Dir, tmp_dir: Io.Dir) anyerror!void
14411442 // the destination directory, fail with an error instead.
14421443 tmp_dir.symLink(link_name, entry.path, .{}) catch |err| switch (err) {
14431444 error.FileNotFound => {
1444 if (fs.path.dirname(entry.path)) |dirname| try tmp_dir.makePath(dirname);
1445 if (fs.path.dirname(entry.path)) |dirname| try tmp_dir.makePath(io, dirname);
14451446 try tmp_dir.symLink(link_name, entry.path, .{});
14461447 },
14471448 else => |e| return e,
src/main.zig+6-5
......@@ -3382,7 +3382,7 @@ fn buildOutputType(
33823382 const dump_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-dump-stdin{s}", .{
33833383 std.crypto.random.int(u64), ext.canonicalName(target),
33843384 });
3385 try dirs.local_cache.handle.makePath("tmp");
3385 try dirs.local_cache.handle.makePath(io, "tmp");
33863386
33873387 // Note that in one of the happy paths, execve() is used to switch to
33883388 // clang in which case any cleanup logic that exists for this temporary
......@@ -4773,7 +4773,7 @@ fn cmdInit(gpa: Allocator, arena: Allocator, io: Io, args: []const []const u8) !
47734773 var ok_count: usize = 0;
47744774
47754775 for (template_paths) |template_path| {
4776 if (templates.write(arena, Io.Dir.cwd(), sanitized_root_name, template_path, fingerprint)) |_| {
4776 if (templates.write(arena, io, Io.Dir.cwd(), sanitized_root_name, template_path, fingerprint)) |_| {
47774777 std.log.info("created {s}", .{template_path});
47784778 ok_count += 1;
47794779 } else |err| switch (err) {
......@@ -7394,20 +7394,21 @@ const Templates = struct {
73947394 fn write(
73957395 templates: *Templates,
73967396 arena: Allocator,
7397 io: Io,
73977398 out_dir: Io.Dir,
73987399 root_name: []const u8,
73997400 template_path: []const u8,
74007401 fingerprint: Package.Fingerprint,
74017402 ) !void {
74027403 if (fs.path.dirname(template_path)) |dirname| {
7403 out_dir.makePath(dirname) catch |err| {
7404 fatal("unable to make path '{s}': {s}", .{ dirname, @errorName(err) });
7404 out_dir.makePath(io, dirname) catch |err| {
7405 fatal("unable to make path '{s}': {t}", .{ dirname, err });
74057406 };
74067407 }
74077408
74087409 const max_bytes = 10 * 1024 * 1024;
74097410 const contents = templates.dir.readFileAlloc(template_path, arena, .limited(max_bytes)) catch |err| {
7410 fatal("unable to read template file '{s}': {s}", .{ template_path, @errorName(err) });
7411 fatal("unable to read template file '{s}': {t}", .{ template_path, err });
74117412 };
74127413 templates.buffer.clearRetainingCapacity();
74137414 try templates.buffer.ensureUnusedCapacity(contents.len);