authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-25 17:40:22-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-09-26 19:08:50-04:00
log2cdf284af96ea016a5553ae9f6516b9b2c8f3f8e
treed90d6c7747c55be094282790f26dbe7b37637b5a
parent4dd645fdddec2c56f8eaabce9f358f95b54f8eec

Threaded: implement new process spawning API for windows


5 files changed, 249 insertions(+), 141 deletions(-)

lib/std/Io/Threaded.zig+192-105
...@@ -85,6 +85,8 @@ csprng: Csprng = .uninitialized,...@@ -85,6 +85,8 @@ csprng: Csprng = .uninitialized,
8585
86system_basic_information: SystemBasicInformation = .{},86system_basic_information: SystemBasicInformation = .{},
8787
88process_spawn_mutex: if (is_windows) Io.Mutex else void = if (is_windows) .init else {},
89
88const SystemBasicInformation = if (!is_windows) struct {} else struct {90const SystemBasicInformation = if (!is_windows) struct {} else struct {
89 buffer: windows.SYSTEM.BASIC_INFORMATION = undefined,91 buffer: windows.SYSTEM.BASIC_INFORMATION = undefined,
90 initialized: std.atomic.Value(bool) = .{ .raw = false },92 initialized: std.atomic.Value(bool) = .{ .raw = false },
...@@ -1706,7 +1708,7 @@ var global_single_threaded_instance: Threaded = .init_single_threaded;...@@ -1706,7 +1708,7 @@ var global_single_threaded_instance: Threaded = .init_single_threaded;
1706pub const global_single_threaded: *Threaded = &global_single_threaded_instance;1708pub const global_single_threaded: *Threaded = &global_single_threaded_instance;
17071709
1708pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void {1710pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void {
1709 mutexLock(&t.mutex);1711 mutexLockUncancelable(&t.mutex);
1710 defer mutexUnlock(&t.mutex);1712 defer mutexUnlock(&t.mutex);
1711 t.async_limit = new_limit;1713 t.async_limit = new_limit;
1712}1714}
...@@ -1727,7 +1729,7 @@ pub fn deinit(t: *Threaded) void {...@@ -1727,7 +1729,7 @@ pub fn deinit(t: *Threaded) void {
1727fn join(t: *Threaded) void {1729fn join(t: *Threaded) void {
1728 if (builtin.single_threaded) return;1730 if (builtin.single_threaded) return;
1729 {1731 {
1730 mutexLock(&t.mutex);1732 mutexLockUncancelable(&t.mutex);
1731 defer mutexUnlock(&t.mutex);1733 defer mutexUnlock(&t.mutex);
1732 t.join_requested = true;1734 t.join_requested = true;
1733 }1735 }
...@@ -1789,7 +1791,7 @@ fn worker(t: *Threaded) void {...@@ -1789,7 +1791,7 @@ fn worker(t: *Threaded) void {
17891791
1790 defer t.wait_group.finish();1792 defer t.wait_group.finish();
17911793
1792 mutexLock(&t.mutex);1794 mutexLockUncancelable(&t.mutex);
1793 defer mutexUnlock(&t.mutex);1795 defer mutexUnlock(&t.mutex);
17941796
1795 while (true) {1797 while (true) {
...@@ -1798,7 +1800,7 @@ fn worker(t: *Threaded) void {...@@ -1798,7 +1800,7 @@ fn worker(t: *Threaded) void {
1798 thread.cancel_protection = .unblocked;1800 thread.cancel_protection = .unblocked;
1799 const runnable: *Runnable = @fieldParentPtr("node", runnable_node);1801 const runnable: *Runnable = @fieldParentPtr("node", runnable_node);
1800 runnable.startFn(runnable, &thread, t);1802 runnable.startFn(runnable, &thread, t);
1801 mutexLock(&t.mutex);1803 mutexLockUncancelable(&t.mutex);
1802 t.busy_count -= 1;1804 t.busy_count -= 1;
1803 }1805 }
1804 if (t.join_requested) break;1806 if (t.join_requested) break;
...@@ -2088,7 +2090,7 @@ fn async(...@@ -2088,7 +2090,7 @@ fn async(
2088 },2090 },
2089 };2091 };
20902092
2091 mutexLock(&t.mutex);2093 mutexLockUncancelable(&t.mutex);
20922094
2093 const busy_count = t.busy_count;2095 const busy_count = t.busy_count;
20942096
...@@ -2140,7 +2142,7 @@ fn concurrent(...@@ -2140,7 +2142,7 @@ fn concurrent(
2140 };2142 };
2141 errdefer future.destroy(gpa);2143 errdefer future.destroy(gpa);
21422144
2143 mutexLock(&t.mutex);2145 mutexLockUncancelable(&t.mutex);
2144 defer mutexUnlock(&t.mutex);2146 defer mutexUnlock(&t.mutex);
21452147
2146 const busy_count = t.busy_count;2148 const busy_count = t.busy_count;
...@@ -2185,7 +2187,7 @@ fn groupAsync(...@@ -2185,7 +2187,7 @@ fn groupAsync(
2185 error.OutOfMemory => return groupAsyncEager(start, context.ptr),2187 error.OutOfMemory => return groupAsyncEager(start, context.ptr),
2186 };2188 };
21872189
2188 mutexLock(&t.mutex);2190 mutexLockUncancelable(&t.mutex);
21892191
2190 const busy_count = t.busy_count;2192 const busy_count = t.busy_count;
21912193
...@@ -2248,7 +2250,7 @@ fn groupConcurrent(...@@ -2248,7 +2250,7 @@ fn groupConcurrent(
2248 };2250 };
2249 errdefer task.destroy(gpa);2251 errdefer task.destroy(gpa);
22502252
2251 mutexLock(&t.mutex);2253 mutexLockUncancelable(&t.mutex);
2252 defer mutexUnlock(&t.mutex);2254 defer mutexUnlock(&t.mutex);
22532255
2254 const busy_count = t.busy_count;2256 const busy_count = t.busy_count;
...@@ -4118,7 +4120,7 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {...@@ -4118,7 +4120,7 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
41184120
4119fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM.BASIC_INFORMATION {4121fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM.BASIC_INFORMATION {
4120 if (!t.system_basic_information.initialized.load(.acquire)) {4122 if (!t.system_basic_information.initialized.load(.acquire)) {
4121 mutexLock(&t.mutex);4123 mutexLockUncancelable(&t.mutex);
4122 defer mutexUnlock(&t.mutex);4124 defer mutexUnlock(&t.mutex);
41234125
4124 switch (windows.ntdll.NtQuerySystemInformation(4126 switch (windows.ntdll.NtQuerySystemInformation(
...@@ -10011,7 +10013,7 @@ fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {...@@ -10011,7 +10013,7 @@ fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
10011 .PENDING => unreachable,10013 .PENDING => unreachable,
10012 .CANCELLED => unreachable,10014 .CANCELLED => unreachable,
10013 .SUCCESS => return io_status_block.Information,10015 .SUCCESS => return io_status_block.Information,
10014 .END_OF_FILE, .PIPE_BROKEN => return error.EndOfStream,10016 .END_OF_FILE, .PIPE_BROKEN, .PIPE_CLOSING => return error.EndOfStream,
10015 .INVALID_HANDLE => return error.NotOpenForReading,10017 .INVALID_HANDLE => return error.NotOpenForReading,
10016 .INVALID_DEVICE_REQUEST => return error.IsDir,10018 .INVALID_DEVICE_REQUEST => return error.IsDir,
10017 .FILE_LOCK_CONFLICT => return error.LockViolation,10019 .FILE_LOCK_CONFLICT => return error.LockViolation,
...@@ -10028,7 +10030,7 @@ fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {...@@ -10028,7 +10030,7 @@ fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
10028 .INVALID_USER_BUFFER => return error.SystemResources,10030 .INVALID_USER_BUFFER => return error.SystemResources,
10029 .NO_MEMORY => return error.SystemResources,10031 .NO_MEMORY => return error.SystemResources,
10030 .QUOTA_EXCEEDED => return error.SystemResources,10032 .QUOTA_EXCEEDED => return error.SystemResources,
10031 .PIPE_BROKEN => return error.BrokenPipe,10033 .PIPE_BROKEN, .PIPE_CLOSING => return error.BrokenPipe,
10032 .INVALID_HANDLE => return error.NotOpenForWriting,10034 .INVALID_HANDLE => return error.NotOpenForWriting,
10033 .FILE_LOCK_CONFLICT => return error.LockViolation,10035 .FILE_LOCK_CONFLICT => return error.LockViolation,
10034 .ACCESS_DENIED => return error.AccessDenied,10036 .ACCESS_DENIED => return error.AccessDenied,
...@@ -14091,7 +14093,7 @@ fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancel...@@ -14091,7 +14093,7 @@ fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancel
14091 const current_thread_id = Thread.currentId();14093 const current_thread_id = Thread.currentId();
1409214094
14093 if (@atomicLoad(std.Thread.Id, &t.stderr_mutex_locker, .unordered) != current_thread_id) {14095 if (@atomicLoad(std.Thread.Id, &t.stderr_mutex_locker, .unordered) != current_thread_id) {
14094 mutexLock(&t.stderr_mutex);14096 try mutexLock(&t.stderr_mutex);
14095 assert(t.stderr_mutex_lock_count == 0);14097 assert(t.stderr_mutex_lock_count == 0);
14096 @atomicStore(std.Thread.Id, &t.stderr_mutex_locker, current_thread_id, .unordered);14098 @atomicStore(std.Thread.Id, &t.stderr_mutex_locker, current_thread_id, .unordered);
14097 }14099 }
...@@ -15117,7 +15119,7 @@ const WindowsEnvironStrings = struct {...@@ -15117,7 +15119,7 @@ const WindowsEnvironStrings = struct {
15117};15119};
1511815120
15119fn scanEnviron(t: *Threaded) void {15121fn scanEnviron(t: *Threaded) void {
15120 mutexLock(&t.mutex);15122 mutexLockUncancelable(&t.mutex);
15121 defer mutexUnlock(&t.mutex);15123 defer mutexUnlock(&t.mutex);
15122 if (t.environ_initialized) return;15124 if (t.environ_initialized) return;
15123 t.environ.scan(t.allocator);15125 t.environ.scan(t.allocator);
...@@ -15416,7 +15418,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp...@@ -15416,7 +15418,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp
1541615418
15417fn getDevNullFd(t: *Threaded) !posix.fd_t {15419fn getDevNullFd(t: *Threaded) !posix.fd_t {
15418 {15420 {
15419 mutexLock(&t.mutex);15421 try mutexLock(&t.mutex);
15420 defer mutexUnlock(&t.mutex);15422 defer mutexUnlock(&t.mutex);
15421 if (t.null_file.fd != -1) return t.null_file.fd;15423 if (t.null_file.fd != -1) return t.null_file.fd;
15422 }15424 }
...@@ -15428,7 +15430,7 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t {...@@ -15428,7 +15430,7 @@ fn getDevNullFd(t: *Threaded) !posix.fd_t {
15428 .SUCCESS => {15430 .SUCCESS => {
15429 syscall.finish();15431 syscall.finish();
15430 const fresh_fd: posix.fd_t = @intCast(rc);15432 const fresh_fd: posix.fd_t = @intCast(rc);
15431 mutexLock(&t.mutex); // Another thread might have won the race.15433 mutexLockUncancelable(&t.mutex); // Another thread might have won the race.
15432 defer mutexUnlock(&t.mutex);15434 defer mutexUnlock(&t.mutex);
15433 if (t.null_file.fd != -1) {15435 if (t.null_file.fd != -1) {
15434 closeFd(fresh_fd);15436 closeFd(fresh_fd);
...@@ -15856,6 +15858,84 @@ fn processSpawnDarwin(userdata: ?*anyopaque, options: process.SpawnOptions) proc...@@ -15856,6 +15858,84 @@ fn processSpawnDarwin(userdata: ?*anyopaque, options: process.SpawnOptions) proc
15856fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {15858fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
15857 const t: *Threaded = @ptrCast(@alignCast(userdata));15859 const t: *Threaded = @ptrCast(@alignCast(userdata));
1585815860
15861 var arena_allocator = std.heap.ArenaAllocator.init(t.allocator);
15862 defer arena_allocator.deinit();
15863 const arena = arena_allocator.allocator();
15864
15865 const cwd_w = cwd_w: {
15866 switch (options.cwd) {
15867 .inherit => break :cwd_w null,
15868 .dir => |cwd_dir| {
15869 var dir_path_buffer = try arena.alloc(u16, windows.PATH_MAX_WIDE + 1);
15870 const dir_path = try GetFinalPathNameByHandle(
15871 cwd_dir.handle,
15872 .{},
15873 dir_path_buffer[0..windows.PATH_MAX_WIDE],
15874 );
15875 dir_path_buffer[dir_path.len] = 0;
15876 // Shrink the allocation down to just the path buffer + sentinel
15877 dir_path_buffer = try arena.realloc(dir_path_buffer, dir_path.len + 1);
15878 break :cwd_w dir_path_buffer[0..dir_path.len :0];
15879 },
15880 .path => |cwd| {
15881 break :cwd_w try std.unicode.wtf8ToWtf16LeAllocZ(arena, cwd);
15882 },
15883 }
15884 };
15885 const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null;
15886
15887 // If the app name has more than just a filename, then we need to separate
15888 // that into the basename and dirname and use the dirname as an addition to
15889 // the cwd path. This is because NtQueryDirectoryFile cannot accept
15890 // FileName params with path separators.
15891 var app_w_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
15892 // If the app name is absolute, then the cwd will already have the app's dirname in it,
15893 // so only populate app_dir if app name is a relative path with > 0 path separators.
15894 const app_path_w, const app_dir_w, const app_name_w = app: {
15895 const path = switch (options.exe) {
15896 .detect, .search, .path => options.argv[0],
15897 .file => |file| {
15898 const path_w = try GetFinalPathNameByHandle(file.handle, .{}, &app_w_buf);
15899 var path_it: Dir.path.ComponentIterator(.windows, u16) = .init(path_w);
15900 const base = path_it.last().?;
15901 break :app .{
15902 null,
15903 if (path_it.previous()) |dir| dir.path else path_it.root(),
15904 base.name,
15905 };
15906 },
15907 .explicit => |explicit| explicit.path,
15908 };
15909 break :app .{
15910 if (Dir.path.isAbsolute(path)) null else switch (options.exe) {
15911 // The cwd provided by options is in effect when choosing the executable
15912 // path to match POSIX semantics.
15913 .detect, .search => cwd_w,
15914 .path => |dir| try GetFinalPathNameByHandle(dir.handle, .{}, &app_w_buf),
15915 .file => unreachable,
15916 .explicit => |explicit| try GetFinalPathNameByHandle(
15917 explicit.dir.handle,
15918 .{},
15919 &app_w_buf,
15920 ),
15921 },
15922 if (Dir.path.dirname(path)) |app_dir|
15923 try std.unicode.wtf8ToWtf16LeAllocZ(arena, app_dir)
15924 else
15925 null,
15926 try std.unicode.wtf8ToWtf16LeAllocZ(arena, Dir.path.basename(path)),
15927 };
15928 };
15929
15930 const flags: windows.CreateProcessFlags = .{
15931 .create_suspended = options.start_suspended,
15932 .create_unicode_environment = true,
15933 .create_no_window = options.create_no_window,
15934 };
15935
15936 try mutexLock(&t.process_spawn_mutex);
15937 defer mutexUnlock(&t.process_spawn_mutex);
15938
15859 const any_ignore =15939 const any_ignore =
15860 options.stdin == .ignore or15940 options.stdin == .ignore or
15861 options.stdout == .ignore or15941 options.stdout == .ignore or
...@@ -15897,6 +15977,43 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro...@@ -15897,6 +15977,43 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
15897 }) else undefined;15977 }) else undefined;
15898 errdefer if (options.progress_node.index != .none) for (prog_pipe) |handle| windows.CloseHandle(handle);15978 errdefer if (options.progress_node.index != .none) for (prog_pipe) |handle| windows.CloseHandle(handle);
1589915979
15980 defer for (options.inherit_dirs) |inherit_dir| switch (windows.ntdll.NtSetInformationObject(
15981 inherit_dir.handle,
15982 .HandleFlag,
15983 &windows.OBJECT.HANDLE_FLAG{ .INHERIT = false },
15984 @sizeOf(windows.OBJECT.HANDLE_FLAG),
15985 )) {
15986 .SUCCESS => {},
15987 else => |status| windows.unexpectedStatus(status) catch {},
15988 };
15989 for (options.inherit_dirs) |inherit_dir| switch (windows.ntdll.NtSetInformationObject(
15990 inherit_dir.handle,
15991 .HandleFlag,
15992 &windows.OBJECT.HANDLE_FLAG{ .INHERIT = true },
15993 @sizeOf(windows.OBJECT.HANDLE_FLAG),
15994 )) {
15995 .SUCCESS => {},
15996 else => |status| return windows.unexpectedStatus(status),
15997 };
15998 defer for (options.inherit_files) |inherit_file| switch (windows.ntdll.NtSetInformationObject(
15999 inherit_file.handle,
16000 .HandleFlag,
16001 &windows.OBJECT.HANDLE_FLAG{ .INHERIT = false },
16002 @sizeOf(windows.OBJECT.HANDLE_FLAG),
16003 )) {
16004 .SUCCESS => {},
16005 else => |status| windows.unexpectedStatus(status) catch {},
16006 };
16007 for (options.inherit_files) |inherit_file| switch (windows.ntdll.NtSetInformationObject(
16008 inherit_file.handle,
16009 .HandleFlag,
16010 &windows.OBJECT.HANDLE_FLAG{ .INHERIT = true },
16011 @sizeOf(windows.OBJECT.HANDLE_FLAG),
16012 )) {
16013 .SUCCESS => {},
16014 else => |status| return windows.unexpectedStatus(status),
16015 };
16016
15900 var siStartInfo: windows.STARTUPINFOW = .{16017 var siStartInfo: windows.STARTUPINFOW = .{
15901 .cb = @sizeOf(windows.STARTUPINFOW),16018 .cb = @sizeOf(windows.STARTUPINFOW),
15902 .dwFlags = windows.STARTF_USESTDHANDLES,16019 .dwFlags = windows.STARTF_USESTDHANDLES,
...@@ -15974,32 +16091,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro...@@ -15974,32 +16091,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
15974 };16091 };
15975 var piProcInfo: windows.PROCESS.INFORMATION = undefined;16092 var piProcInfo: windows.PROCESS.INFORMATION = undefined;
1597616093
15977 var arena_allocator = std.heap.ArenaAllocator.init(t.allocator);
15978 defer arena_allocator.deinit();
15979 const arena = arena_allocator.allocator();
15980
15981 const cwd_w = cwd_w: {
15982 switch (options.cwd) {
15983 .inherit => break :cwd_w null,
15984 .dir => |cwd_dir| {
15985 var dir_path_buffer = try arena.alloc(u16, windows.PATH_MAX_WIDE + 1);
15986 const dir_path = try GetFinalPathNameByHandle(
15987 cwd_dir.handle,
15988 .{},
15989 dir_path_buffer[0..windows.PATH_MAX_WIDE],
15990 );
15991 dir_path_buffer[dir_path.len] = 0;
15992 // Shrink the allocation down to just the path buffer + sentinel
15993 dir_path_buffer = try arena.realloc(dir_path_buffer, dir_path.len + 1);
15994 break :cwd_w dir_path_buffer[0..dir_path.len :0];
15995 },
15996 .path => |cwd| {
15997 break :cwd_w try std.unicode.wtf8ToWtf16LeAllocZ(arena, cwd);
15998 },
15999 }
16000 };
16001 const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null;
16002
16003 const env_block = env_block: {16094 const env_block = env_block: {
16004 const prog_handle = if (options.progress_node.index != .none)16095 const prog_handle = if (options.progress_node.index != .none)
16005 prog_pipe[1]16096 prog_pipe[1]
...@@ -16013,45 +16104,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro...@@ -16013,45 +16104,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
16013 });16104 });
16014 };16105 };
1601516106
16016 const app_name_wtf8 = options.argv[0];
16017 const app_name_is_absolute = Dir.path.isAbsolute(app_name_wtf8);
16018
16019 // The cwd provided by options is in effect when choosing the executable
16020 // path to match POSIX semantics.
16021 const cwd_path_w = x: {
16022 // If the app name is absolute, then we need to use its dirname as the cwd
16023 if (app_name_is_absolute) {
16024 const dir = Dir.path.dirname(app_name_wtf8).?;
16025 break :x try std.unicode.wtf8ToWtf16LeAllocZ(arena, dir);
16026 } else if (cwd_w) |cwd| {
16027 break :x cwd;
16028 } else {
16029 break :x &[_:0]u16{}; // empty for cwd
16030 }
16031 };
16032
16033 // If the app name has more than just a filename, then we need to separate
16034 // that into the basename and dirname and use the dirname as an addition to
16035 // the cwd path. This is because NtQueryDirectoryFile cannot accept
16036 // FileName params with path separators.
16037 const app_basename_wtf8 = Dir.path.basename(app_name_wtf8);
16038 // If the app name is absolute, then the cwd will already have the app's dirname in it,
16039 // so only populate app_dirname if app name is a relative path with > 0 path separators.
16040 const maybe_app_dirname_wtf8 = if (!app_name_is_absolute) Dir.path.dirname(app_name_wtf8) else null;
16041 const app_dirname_w: ?[:0]u16 = x: {
16042 if (maybe_app_dirname_wtf8) |app_dirname_wtf8| {
16043 break :x try std.unicode.wtf8ToWtf16LeAllocZ(arena, app_dirname_wtf8);
16044 }
16045 break :x null;
16046 };
16047 const app_name_w = try std.unicode.wtf8ToWtf16LeAllocZ(arena, app_basename_wtf8);
16048
16049 const flags: windows.CreateProcessFlags = .{
16050 .create_suspended = options.start_suspended,
16051 .create_unicode_environment = true,
16052 .create_no_window = options.create_no_window,
16053 };
16054
16055 run: {16107 run: {
16056 // We have to scan each time because the PEB environment pointer is not stable.16108 // We have to scan each time because the PEB environment pointer is not stable.
16057 const env_strings: WindowsEnvironStrings = .scan();16109 const env_strings: WindowsEnvironStrings = .scan();
...@@ -16072,10 +16124,8 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro...@@ -16072,10 +16124,8 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
1607216124
16073 var dir_buf: std.ArrayList(u16) = .empty;16125 var dir_buf: std.ArrayList(u16) = .empty;
1607416126
16075 if (cwd_path_w.len > 0) {16127 if (app_path_w) |app_path| try dir_buf.appendSlice(arena, app_path);
16076 try dir_buf.appendSlice(arena, cwd_path_w);16128 if (app_dir_w) |app_dir| {
16077 }
16078 if (app_dirname_w) |app_dir| {
16079 if (dir_buf.items.len > 0) try dir_buf.append(arena, Dir.path.sep);16129 if (dir_buf.items.len > 0) try dir_buf.append(arena, Dir.path.sep);
16080 try dir_buf.appendSlice(arena, app_dir);16130 try dir_buf.appendSlice(arena, app_dir);
16081 }16131 }
...@@ -16100,15 +16150,18 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro...@@ -16100,15 +16150,18 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
16100 else => |e| return e,16150 else => |e| return e,
16101 };16151 };
1610216152
16103 // If the app name had path separators, that disallows PATH searching,
16104 // and there's no need to search the PATH if the app name is absolute.
16105 // We still search the path if the cwd is absolute because of the16153 // We still search the path if the cwd is absolute because of the
16106 // "cwd provided by options is in effect when choosing the executable path16154 // "cwd provided by options is in effect when choosing the executable path
16107 // to match posix semantics" behavior--we don't want to skip searching16155 // to match posix semantics" behavior--we don't want to skip searching
16108 // the PATH just because we were trying to set the cwd of the child process.16156 // the PATH just because we were trying to set the cwd of the child process.
16109 if (app_dirname_w != null or app_name_is_absolute) {16157 if (switch (options.exe) {
16110 return original_err;16158 // If the app name had path separators, that disallows PATH searching.
16111 }16159 .detect => app_dir_w != null,
16160 // There's no need to search the PATH if the app name is absolute.
16161 .search => app_path_w == null,
16162 // PATH searching is disallowed.
16163 .path, .file, .explicit => true,
16164 }) return original_err;
1611216165
16113 var it = std.mem.tokenizeScalar(u16, PATH, ';');16166 var it = std.mem.tokenizeScalar(u16, PATH, ';');
16114 while (it.next()) |search_path| {16167 while (it.next()) |search_path| {
...@@ -16186,11 +16239,9 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro...@@ -16186,11 +16239,9 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro
16186 };16239 };
16187}16240}
1618816241
16189fn inheritFile() windows.HANDLE {}
16190
16191fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE {16242fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
16192 {16243 {
16193 mutexLock(&t.mutex);16244 try mutexLock(&t.mutex);
16194 defer mutexUnlock(&t.mutex);16245 defer mutexUnlock(&t.mutex);
16195 if (t.random_file.handle) |handle| return handle;16246 if (t.random_file.handle) |handle| return handle;
16196 }16247 }
...@@ -16213,7 +16264,7 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE {...@@ -16213,7 +16264,7 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
16213 )) {16264 )) {
16214 .SUCCESS => {16265 .SUCCESS => {
16215 syscall.finish();16266 syscall.finish();
16216 mutexLock(&t.mutex); // Another thread might have won the race.16267 mutexLockUncancelable(&t.mutex); // Another thread might have won the race.
16217 defer mutexUnlock(&t.mutex);16268 defer mutexUnlock(&t.mutex);
16218 if (t.random_file.handle) |prev_handle| {16269 if (t.random_file.handle) |prev_handle| {
16219 windows.CloseHandle(fresh_handle);16270 windows.CloseHandle(fresh_handle);
...@@ -16234,7 +16285,7 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE {...@@ -16234,7 +16285,7 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
1623416285
16235fn getNulDevice(t: *Threaded) !windows.HANDLE {16286fn getNulDevice(t: *Threaded) !windows.HANDLE {
16236 {16287 {
16237 mutexLock(&t.mutex);16288 try mutexLock(&t.mutex);
16238 defer mutexUnlock(&t.mutex);16289 defer mutexUnlock(&t.mutex);
16239 if (t.null_file.handle) |handle| return handle;16290 if (t.null_file.handle) |handle| return handle;
16240 }16291 }
...@@ -16260,7 +16311,7 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE {...@@ -16260,7 +16311,7 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE {
16260 )) {16311 )) {
16261 .SUCCESS => {16312 .SUCCESS => {
16262 syscall.finish();16313 syscall.finish();
16263 mutexLock(&t.mutex); // Another thread might have won the race.16314 mutexLockUncancelable(&t.mutex); // Another thread might have won the race.
16264 defer mutexUnlock(&t.mutex);16315 defer mutexUnlock(&t.mutex);
16265 if (t.null_file.handle) |prev_handle| {16316 if (t.null_file.handle) |prev_handle| {
16266 windows.CloseHandle(fresh_handle);16317 windows.CloseHandle(fresh_handle);
...@@ -16293,7 +16344,7 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE {...@@ -16293,7 +16344,7 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE {
1629316344
16294fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE {16345fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE {
16295 {16346 {
16296 mutexLock(&t.mutex);16347 try mutexLock(&t.mutex);
16297 defer mutexUnlock(&t.mutex);16348 defer mutexUnlock(&t.mutex);
16298 if (t.pipe_file.handle) |handle| return handle;16349 if (t.pipe_file.handle) |handle| return handle;
16299 }16350 }
...@@ -16315,7 +16366,7 @@ fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE {...@@ -16315,7 +16366,7 @@ fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE {
16315 )) {16366 )) {
16316 .SUCCESS => {16367 .SUCCESS => {
16317 syscall.finish();16368 syscall.finish();
16318 mutexLock(&t.mutex); // Another thread might have won the race.16369 mutexLockUncancelable(&t.mutex); // Another thread might have won the race.
16319 defer mutexUnlock(&t.mutex);16370 defer mutexUnlock(&t.mutex);
16320 if (t.pipe_file.handle) |prev_handle| {16371 if (t.pipe_file.handle) |prev_handle| {
16321 windows.CloseHandle(fresh_handle);16372 windows.CloseHandle(fresh_handle);
...@@ -16600,7 +16651,15 @@ fn windowsCreateProcessPathExt(...@@ -16600,7 +16651,15 @@ fn windowsCreateProcessPathExt(
16600 else16651 else
16601 full_app_name;16652 full_app_name;
1660216653
16603 if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, env_block, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| {16654 if (windowsCreateProcess(
16655 app_name_w.ptr,
16656 cmd_line_w.ptr,
16657 env_block,
16658 cwd_ptr,
16659 flags,
16660 lpStartupInfo,
16661 lpProcessInformation,
16662 )) |_| {
16604 return;16663 return;
16605 } else |err| switch (err) {16664 } else |err| switch (err) {
16606 error.FileNotFound => continue,16665 error.FileNotFound => continue,
...@@ -17716,6 +17775,15 @@ fn inheritParentHandle(handle: posix.fd_t) Io.InheritParentHandleError!void {...@@ -17716,6 +17775,15 @@ fn inheritParentHandle(handle: posix.fd_t) Io.InheritParentHandleError!void {
17716 .visionos,17775 .visionos,
17717 .watchos,17776 .watchos,
17718 => return setCloexec(handle),17777 => return setCloexec(handle),
17778 .windows => switch (windows.ntdll.NtSetInformationObject(
17779 handle,
17780 .HandleFlag,
17781 &windows.OBJECT.HANDLE_FLAG{ .INHERIT = false },
17782 @sizeOf(windows.OBJECT.HANDLE_FLAG),
17783 )) {
17784 .SUCCESS => return,
17785 else => |status| return windows.unexpectedStatus(status),
17786 },
17719 else => return error.UnsupportedOperation,17787 else => return error.UnsupportedOperation,
17720 }17788 }
17721}17789}
...@@ -17756,7 +17824,7 @@ fn random(userdata: ?*anyopaque, buffer: []u8) void {...@@ -17756,7 +17824,7 @@ fn random(userdata: ?*anyopaque, buffer: []u8) void {
17756}17824}
1775717825
17758fn randomMainThread(t: *Threaded, buffer: []u8) void {17826fn randomMainThread(t: *Threaded, buffer: []u8) void {
17759 mutexLock(&t.mutex);17827 mutexLockUncancelable(&t.mutex);
17760 defer mutexUnlock(&t.mutex);17828 defer mutexUnlock(&t.mutex);
1776117829
17762 if (!t.csprng.isInitialized()) {17830 if (!t.csprng.isInitialized()) {
...@@ -17764,7 +17832,7 @@ fn randomMainThread(t: *Threaded, buffer: []u8) void {...@@ -17764,7 +17832,7 @@ fn randomMainThread(t: *Threaded, buffer: []u8) void {
17764 var seed: [Csprng.seed_len]u8 = undefined;17832 var seed: [Csprng.seed_len]u8 = undefined;
17765 {17833 {
17766 mutexUnlock(&t.mutex);17834 mutexUnlock(&t.mutex);
17767 defer mutexLock(&t.mutex);17835 defer mutexLockUncancelable(&t.mutex);
1776817836
17769 const prev = swapCancelProtection(t, .blocked);17837 const prev = swapCancelProtection(t, .blocked);
17770 defer _ = swapCancelProtection(t, prev);17838 defer _ = swapCancelProtection(t, prev);
...@@ -17951,7 +18019,7 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void {...@@ -17951,7 +18019,7 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void {
1795118019
17952fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {18020fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
17953 {18021 {
17954 mutexLock(&t.mutex);18022 try mutexLock(&t.mutex);
17955 defer mutexUnlock(&t.mutex);18023 defer mutexUnlock(&t.mutex);
1795618024
17957 if (t.random_file.fd == -2) return error.EntropyUnavailable;18025 if (t.random_file.fd == -2) return error.EntropyUnavailable;
...@@ -17992,7 +18060,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {...@@ -17992,7 +18060,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
17992 .SUCCESS => {18060 .SUCCESS => {
17993 syscall.finish();18061 syscall.finish();
17994 if (!statx.mask.TYPE) return error.EntropyUnavailable;18062 if (!statx.mask.TYPE) return error.EntropyUnavailable;
17995 mutexLock(&t.mutex); // Another thread might have won the race.18063 mutexLockUncancelable(&t.mutex); // Another thread might have won the race.
17996 defer mutexUnlock(&t.mutex);18064 defer mutexUnlock(&t.mutex);
17997 if (t.random_file.fd >= 0) {18065 if (t.random_file.fd >= 0) {
17998 closeFd(fd);18066 closeFd(fd);
...@@ -18020,7 +18088,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {...@@ -18020,7 +18088,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
18020 switch (posix.errno(fstat_sym(fd, &stat))) {18088 switch (posix.errno(fstat_sym(fd, &stat))) {
18021 .SUCCESS => {18089 .SUCCESS => {
18022 syscall.finish();18090 syscall.finish();
18023 mutexLock(&t.mutex); // Another thread might have won the race.18091 mutexLockUncancelable(&t.mutex); // Another thread might have won the race.
18024 defer mutexUnlock(&t.mutex);18092 defer mutexUnlock(&t.mutex);
18025 if (t.random_file.fd >= 0) {18093 if (t.random_file.fd >= 0) {
18026 closeFd(fd);18094 closeFd(fd);
...@@ -19576,7 +19644,7 @@ fn condWait(cond: *Io.Condition, mutex: *Io.Mutex) void {...@@ -19576,7 +19644,7 @@ fn condWait(cond: *Io.Condition, mutex: *Io.Mutex) void {
19576 }19644 }
1957719645
19578 mutexUnlock(mutex);19646 mutexUnlock(mutex);
19579 defer mutexLock(mutex);19647 defer mutexLockUncancelable(mutex);
1958019648
19581 while (true) {19649 while (true) {
19582 Thread.futexWaitUncancelable(&cond.epoch.raw, epoch, null);19650 Thread.futexWaitUncancelable(&cond.epoch.raw, epoch, null);
...@@ -19596,8 +19664,27 @@ fn condWait(cond: *Io.Condition, mutex: *Io.Mutex) void {...@@ -19596,8 +19664,27 @@ fn condWait(cond: *Io.Condition, mutex: *Io.Mutex) void {
19596 }19664 }
19597}19665}
1959819666
19667/// Same as `Io.Mutex.lock` but avoids the VTable.
19668pub fn mutexLock(m: *Io.Mutex) Io.Cancelable!void {
19669 const initial_state = m.state.cmpxchgStrong(
19670 .unlocked,
19671 .locked_once,
19672 .acquire,
19673 .monotonic,
19674 ) orelse {
19675 @branchHint(.likely);
19676 return;
19677 };
19678 if (initial_state == .contended) {
19679 try Thread.futexWait(@ptrCast(&m.state.raw), @backingInt(Io.Mutex.State.contended), null);
19680 }
19681 while (m.state.swap(.contended, .acquire) != .unlocked) {
19682 try Thread.futexWait(@ptrCast(&m.state.raw), @backingInt(Io.Mutex.State.contended), null);
19683 }
19684}
19685
19599/// Same as `Io.Mutex.lockUncancelable` but avoids the VTable.19686/// Same as `Io.Mutex.lockUncancelable` but avoids the VTable.
19600pub fn mutexLock(m: *Io.Mutex) void {19687pub fn mutexLockUncancelable(m: *Io.Mutex) void {
19601 const initial_state = m.state.cmpxchgStrong(19688 const initial_state = m.state.cmpxchgStrong(
19602 .unlocked,19689 .unlocked,
19603 .locked_once,19690 .locked_once,
lib/std/fs/path.zig+32-32
...@@ -1438,7 +1438,7 @@ pub fn dirnamePosix(path: []const u8) ?[]const u8 {...@@ -1438,7 +1438,7 @@ pub fn dirnamePosix(path: []const u8) ?[]const u8 {
1438}1438}
14391439
1440fn dirnameInner(comptime path_type: PathType, path: []const u8) ?[]const u8 {1440fn dirnameInner(comptime path_type: PathType, path: []const u8) ?[]const u8 {
1441 var it = ComponentIterator(path_type, u8).init(path);1441 var it: ComponentIterator(path_type, u8) = .init(path);
1442 _ = it.last() orelse return null;1442 _ = it.last() orelse return null;
1443 const up = it.previous() orelse return it.root();1443 const up = it.previous() orelse return it.root();
1444 return up.path;1444 return up.path;
...@@ -1539,7 +1539,7 @@ pub fn basenameWindows(path: []const u8) []const u8 {...@@ -1539,7 +1539,7 @@ pub fn basenameWindows(path: []const u8) []const u8 {
1539}1539}
15401540
1541fn basenameInner(comptime path_type: PathType, path: []const u8) []const u8 {1541fn basenameInner(comptime path_type: PathType, path: []const u8) []const u8 {
1542 var it = ComponentIterator(path_type, u8).init(path);1542 var it: ComponentIterator(path_type, u8) = .init(path);
1543 const last = it.last() orelse return &[_]u8{};1543 const last = it.last() orelse return &[_]u8{};
1544 return last.name;1544 return last.name;
1545}1545}
...@@ -2338,14 +2338,14 @@ pub const NativeComponentIterator = ComponentIterator(switch (native_os) {...@@ -2338,14 +2338,14 @@ pub const NativeComponentIterator = ComponentIterator(switch (native_os) {
2338}, u8);2338}, u8);
23392339
2340pub fn componentIterator(path: []const u8) NativeComponentIterator {2340pub fn componentIterator(path: []const u8) NativeComponentIterator {
2341 return NativeComponentIterator.init(path);2341 return .init(path);
2342}2342}
23432343
2344test "ComponentIterator posix" {2344test "ComponentIterator posix" {
2345 const PosixComponentIterator = ComponentIterator(.posix, u8);2345 const PosixComponentIterator = ComponentIterator(.posix, u8);
2346 {2346 {
2347 const path = "a/b/c/";2347 const path = "a/b/c/";
2348 var it = PosixComponentIterator.init(path);2348 var it: PosixComponentIterator = .init(path);
2349 try std.testing.expectEqual(0, it.root_len);2349 try std.testing.expectEqual(0, it.root_len);
2350 try std.testing.expectEqual(0, it.root_end_index);2350 try std.testing.expectEqual(0, it.root_end_index);
2351 try std.testing.expect(null == it.root());2351 try std.testing.expect(null == it.root());
...@@ -2393,7 +2393,7 @@ test "ComponentIterator posix" {...@@ -2393,7 +2393,7 @@ test "ComponentIterator posix" {
23932393
2394 {2394 {
2395 const path = "/a/b/c/";2395 const path = "/a/b/c/";
2396 var it = PosixComponentIterator.init(path);2396 var it: PosixComponentIterator = .init(path);
2397 try std.testing.expectEqual(1, it.root_len);2397 try std.testing.expectEqual(1, it.root_len);
2398 try std.testing.expectEqual(1, it.root_end_index);2398 try std.testing.expectEqual(1, it.root_end_index);
2399 try std.testing.expectEqualStrings("/", it.root().?);2399 try std.testing.expectEqualStrings("/", it.root().?);
...@@ -2441,7 +2441,7 @@ test "ComponentIterator posix" {...@@ -2441,7 +2441,7 @@ test "ComponentIterator posix" {
24412441
2442 {2442 {
2443 const path = "////a///b///c////";2443 const path = "////a///b///c////";
2444 var it = PosixComponentIterator.init(path);2444 var it: PosixComponentIterator = .init(path);
2445 try std.testing.expectEqual(1, it.root_len);2445 try std.testing.expectEqual(1, it.root_len);
2446 try std.testing.expectEqual(4, it.root_end_index);2446 try std.testing.expectEqual(4, it.root_end_index);
2447 try std.testing.expectEqualStrings("/", it.root().?);2447 try std.testing.expectEqualStrings("/", it.root().?);
...@@ -2489,7 +2489,7 @@ test "ComponentIterator posix" {...@@ -2489,7 +2489,7 @@ test "ComponentIterator posix" {
24892489
2490 {2490 {
2491 const path = "/";2491 const path = "/";
2492 var it = PosixComponentIterator.init(path);2492 var it: PosixComponentIterator = .init(path);
2493 try std.testing.expectEqual(1, it.root_len);2493 try std.testing.expectEqual(1, it.root_len);
2494 try std.testing.expectEqual(1, it.root_end_index);2494 try std.testing.expectEqual(1, it.root_end_index);
2495 try std.testing.expectEqualStrings("/", it.root().?);2495 try std.testing.expectEqualStrings("/", it.root().?);
...@@ -2507,7 +2507,7 @@ test "ComponentIterator posix" {...@@ -2507,7 +2507,7 @@ test "ComponentIterator posix" {
25072507
2508 {2508 {
2509 const path = "";2509 const path = "";
2510 var it = PosixComponentIterator.init(path);2510 var it: PosixComponentIterator = .init(path);
2511 try std.testing.expectEqual(0, it.root_len);2511 try std.testing.expectEqual(0, it.root_len);
2512 try std.testing.expectEqual(0, it.root_end_index);2512 try std.testing.expectEqual(0, it.root_end_index);
2513 try std.testing.expect(null == it.root());2513 try std.testing.expect(null == it.root());
...@@ -2528,7 +2528,7 @@ test "ComponentIterator windows" {...@@ -2528,7 +2528,7 @@ test "ComponentIterator windows" {
2528 const WindowsComponentIterator = ComponentIterator(.windows, u8);2528 const WindowsComponentIterator = ComponentIterator(.windows, u8);
2529 {2529 {
2530 const path = "a/b\\c//";2530 const path = "a/b\\c//";
2531 var it = WindowsComponentIterator.init(path);2531 var it: WindowsComponentIterator = .init(path);
2532 try std.testing.expectEqual(0, it.root_len);2532 try std.testing.expectEqual(0, it.root_len);
2533 try std.testing.expectEqual(0, it.root_end_index);2533 try std.testing.expectEqual(0, it.root_end_index);
2534 try std.testing.expect(null == it.root());2534 try std.testing.expect(null == it.root());
...@@ -2576,7 +2576,7 @@ test "ComponentIterator windows" {...@@ -2576,7 +2576,7 @@ test "ComponentIterator windows" {
25762576
2577 {2577 {
2578 const path = "C:\\a/b/c/";2578 const path = "C:\\a/b/c/";
2579 var it = WindowsComponentIterator.init(path);2579 var it: WindowsComponentIterator = .init(path);
2580 try std.testing.expectEqual(3, it.root_len);2580 try std.testing.expectEqual(3, it.root_len);
2581 try std.testing.expectEqual(3, it.root_end_index);2581 try std.testing.expectEqual(3, it.root_end_index);
2582 try std.testing.expectEqualStrings("C:\\", it.root().?);2582 try std.testing.expectEqualStrings("C:\\", it.root().?);
...@@ -2614,7 +2614,7 @@ test "ComponentIterator windows" {...@@ -2614,7 +2614,7 @@ test "ComponentIterator windows" {
26142614
2615 {2615 {
2616 const path = "C:\\\\//a/\\/\\b///c////";2616 const path = "C:\\\\//a/\\/\\b///c////";
2617 var it = WindowsComponentIterator.init(path);2617 var it: WindowsComponentIterator = .init(path);
2618 try std.testing.expectEqual(3, it.root_len);2618 try std.testing.expectEqual(3, it.root_len);
2619 try std.testing.expectEqual(6, it.root_end_index);2619 try std.testing.expectEqual(6, it.root_end_index);
2620 try std.testing.expectEqualStrings("C:\\", it.root().?);2620 try std.testing.expectEqualStrings("C:\\", it.root().?);
...@@ -2652,7 +2652,7 @@ test "ComponentIterator windows" {...@@ -2652,7 +2652,7 @@ test "ComponentIterator windows" {
26522652
2653 {2653 {
2654 const path = "/";2654 const path = "/";
2655 var it = WindowsComponentIterator.init(path);2655 var it: WindowsComponentIterator = .init(path);
2656 try std.testing.expectEqual(1, it.root_len);2656 try std.testing.expectEqual(1, it.root_len);
2657 try std.testing.expectEqual(1, it.root_end_index);2657 try std.testing.expectEqual(1, it.root_end_index);
2658 try std.testing.expectEqualStrings("/", it.root().?);2658 try std.testing.expectEqualStrings("/", it.root().?);
...@@ -2670,7 +2670,7 @@ test "ComponentIterator windows" {...@@ -2670,7 +2670,7 @@ test "ComponentIterator windows" {
26702670
2671 {2671 {
2672 const path = "";2672 const path = "";
2673 var it = WindowsComponentIterator.init(path);2673 var it: WindowsComponentIterator = .init(path);
2674 try std.testing.expectEqual(0, it.root_len);2674 try std.testing.expectEqual(0, it.root_len);
2675 try std.testing.expectEqual(0, it.root_end_index);2675 try std.testing.expectEqual(0, it.root_end_index);
2676 try std.testing.expect(null == it.root());2676 try std.testing.expect(null == it.root());
...@@ -2692,7 +2692,7 @@ test "ComponentIterator windows WTF-16" {...@@ -2692,7 +2692,7 @@ test "ComponentIterator windows WTF-16" {
2692 const L = std.unicode.utf8ToUtf16LeStringLiteral;2692 const L = std.unicode.utf8ToUtf16LeStringLiteral;
26932693
2694 const path = L("C:\\a/b/c/");2694 const path = L("C:\\a/b/c/");
2695 var it = WindowsComponentIterator.init(path);2695 var it: WindowsComponentIterator = .init(path);
2696 try std.testing.expectEqual(3, it.root_len);2696 try std.testing.expectEqual(3, it.root_len);
2697 try std.testing.expectEqual(3, it.root_end_index);2697 try std.testing.expectEqual(3, it.root_end_index);
2698 try std.testing.expectEqualSlices(u16, L("C:\\"), it.root().?);2698 try std.testing.expectEqualSlices(u16, L("C:\\"), it.root().?);
...@@ -2731,59 +2731,59 @@ test "ComponentIterator windows WTF-16" {...@@ -2731,59 +2731,59 @@ test "ComponentIterator windows WTF-16" {
2731test "ComponentIterator roots" {2731test "ComponentIterator roots" {
2732 // UEFI2732 // UEFI
2733 {2733 {
2734 var it = ComponentIterator(.uefi, u8).init("\\\\a");2734 var it: ComponentIterator(.uefi, u8) = .init("\\\\a");
2735 try std.testing.expectEqualStrings("\\", it.root().?);2735 try std.testing.expectEqualStrings("\\", it.root().?);
27362736
2737 it = ComponentIterator(.uefi, u8).init("//a");2737 it = .init("//a");
2738 try std.testing.expect(null == it.root());2738 try std.testing.expect(null == it.root());
2739 }2739 }
2740 // POSIX2740 // POSIX
2741 {2741 {
2742 var it = ComponentIterator(.posix, u8).init("//a");2742 var it: ComponentIterator(.posix, u8) = .init("//a");
2743 try std.testing.expectEqualStrings("/", it.root().?);2743 try std.testing.expectEqualStrings("/", it.root().?);
27442744
2745 it = ComponentIterator(.posix, u8).init("\\\\a");2745 it = .init("\\\\a");
2746 try std.testing.expect(null == it.root());2746 try std.testing.expect(null == it.root());
2747 }2747 }
2748 // Windows2748 // Windows
2749 {2749 {
2750 // Drive relative2750 // Drive relative
2751 var it = ComponentIterator(.windows, u8).init("C:a");2751 var it: ComponentIterator(.windows, u8) = .init("C:a");
2752 try std.testing.expectEqualStrings("C:", it.root().?);2752 try std.testing.expectEqualStrings("C:", it.root().?);
27532753
2754 // Drive absolute2754 // Drive absolute
2755 it = ComponentIterator(.windows, u8).init("C:/a");2755 it = .init("C:/a");
2756 try std.testing.expectEqualStrings("C:/", it.root().?);2756 try std.testing.expectEqualStrings("C:/", it.root().?);
2757 it = ComponentIterator(.windows, u8).init("C:\\a");2757 it = .init("C:\\a");
2758 try std.testing.expectEqualStrings("C:\\", it.root().?);2758 try std.testing.expectEqualStrings("C:\\", it.root().?);
2759 it = ComponentIterator(.windows, u8).init("C:///a");2759 it = .init("C:///a");
2760 try std.testing.expectEqualStrings("C:/", it.root().?);2760 try std.testing.expectEqualStrings("C:/", it.root().?);
27612761
2762 // Rooted2762 // Rooted
2763 it = ComponentIterator(.windows, u8).init("\\a");2763 it = .init("\\a");
2764 try std.testing.expectEqualStrings("\\", it.root().?);2764 try std.testing.expectEqualStrings("\\", it.root().?);
2765 it = ComponentIterator(.windows, u8).init("/a");2765 it = .init("/a");
2766 try std.testing.expectEqualStrings("/", it.root().?);2766 try std.testing.expectEqualStrings("/", it.root().?);
27672767
2768 // Root local device2768 // Root local device
2769 it = ComponentIterator(.windows, u8).init("\\\\.");2769 it = .init("\\\\.");
2770 try std.testing.expectEqualStrings("\\\\.", it.root().?);2770 try std.testing.expectEqualStrings("\\\\.", it.root().?);
2771 it = ComponentIterator(.windows, u8).init("//?");2771 it = .init("//?");
2772 try std.testing.expectEqualStrings("//?", it.root().?);2772 try std.testing.expectEqualStrings("//?", it.root().?);
27732773
2774 // UNC absolute2774 // UNC absolute
2775 it = ComponentIterator(.windows, u8).init("//");2775 it = .init("//");
2776 try std.testing.expectEqualStrings("//", it.root().?);2776 try std.testing.expectEqualStrings("//", it.root().?);
2777 it = ComponentIterator(.windows, u8).init("\\\\a");2777 it = .init("\\\\a");
2778 try std.testing.expectEqualStrings("\\\\a", it.root().?);2778 try std.testing.expectEqualStrings("\\\\a", it.root().?);
2779 it = ComponentIterator(.windows, u8).init("\\\\a\\b\\\\c");2779 it = .init("\\\\a\\b\\\\c");
2780 try std.testing.expectEqualStrings("\\\\a\\b\\", it.root().?);2780 try std.testing.expectEqualStrings("\\\\a\\b\\", it.root().?);
2781 it = ComponentIterator(.windows, u8).init("//a");2781 it = .init("//a");
2782 try std.testing.expectEqualStrings("//a", it.root().?);2782 try std.testing.expectEqualStrings("//a", it.root().?);
2783 it = ComponentIterator(.windows, u8).init("//a/b//c");2783 it = .init("//a/b//c");
2784 try std.testing.expectEqualStrings("//a/b/", it.root().?);2784 try std.testing.expectEqualStrings("//a/b/", it.root().?);
2785 // Malformed UNC path with empty server name2785 // Malformed UNC path with empty server name
2786 it = ComponentIterator(.windows, u8).init("\\\\\\a\\b\\c");2786 it = .init("\\\\\\a\\b\\c");
2787 try std.testing.expectEqualStrings("\\\\\\a\\", it.root().?);2787 try std.testing.expectEqualStrings("\\\\\\a\\", it.root().?);
2788 }2788 }
2789}2789}
lib/std/heap/debug_allocator.zig+4-4
...@@ -718,7 +718,7 @@ pub fn DebugAllocator(comptime config: Config) type {...@@ -718,7 +718,7 @@ pub fn DebugAllocator(comptime config: Config) type {
718718
719 fn alloc(context: *anyopaque, len: usize, alignment: mem.Alignment, ret_addr: usize) ?[*]u8 {719 fn alloc(context: *anyopaque, len: usize, alignment: mem.Alignment, ret_addr: usize) ?[*]u8 {
720 const self: *Self = @ptrCast(@alignCast(context));720 const self: *Self = @ptrCast(@alignCast(context));
721 if (have_mutex) std.Io.Threaded.mutexLock(&self.mutex);721 if (have_mutex) std.Io.Threaded.mutexLockUncancelable(&self.mutex);
722 defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex);722 defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex);
723723
724 if (config.enable_memory_limit) {724 if (config.enable_memory_limit) {
...@@ -831,7 +831,7 @@ pub fn DebugAllocator(comptime config: Config) type {...@@ -831,7 +831,7 @@ pub fn DebugAllocator(comptime config: Config) type {
831 return_address: usize,831 return_address: usize,
832 ) bool {832 ) bool {
833 const self: *Self = @ptrCast(@alignCast(context));833 const self: *Self = @ptrCast(@alignCast(context));
834 if (have_mutex) std.Io.Threaded.mutexLock(&self.mutex);834 if (have_mutex) std.Io.Threaded.mutexLockUncancelable(&self.mutex);
835 defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex);835 defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex);
836836
837 const size_class_index: usize = @max(@bitSizeOf(usize) - @clz(memory.len - 1), @backingInt(alignment));837 const size_class_index: usize = @max(@bitSizeOf(usize) - @clz(memory.len - 1), @backingInt(alignment));
...@@ -850,7 +850,7 @@ pub fn DebugAllocator(comptime config: Config) type {...@@ -850,7 +850,7 @@ pub fn DebugAllocator(comptime config: Config) type {
850 return_address: usize,850 return_address: usize,
851 ) ?[*]u8 {851 ) ?[*]u8 {
852 const self: *Self = @ptrCast(@alignCast(context));852 const self: *Self = @ptrCast(@alignCast(context));
853 if (have_mutex) std.Io.Threaded.mutexLock(&self.mutex);853 if (have_mutex) std.Io.Threaded.mutexLockUncancelable(&self.mutex);
854 defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex);854 defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex);
855855
856 const size_class_index: usize = @max(@bitSizeOf(usize) - @clz(memory.len - 1), @backingInt(alignment));856 const size_class_index: usize = @max(@bitSizeOf(usize) - @clz(memory.len - 1), @backingInt(alignment));
...@@ -868,7 +868,7 @@ pub fn DebugAllocator(comptime config: Config) type {...@@ -868,7 +868,7 @@ pub fn DebugAllocator(comptime config: Config) type {
868 return_address: usize,868 return_address: usize,
869 ) void {869 ) void {
870 const self: *Self = @ptrCast(@alignCast(context));870 const self: *Self = @ptrCast(@alignCast(context));
871 if (have_mutex) std.Io.Threaded.mutexLock(&self.mutex);871 if (have_mutex) std.Io.Threaded.mutexLockUncancelable(&self.mutex);
872 defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex);872 defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex);
873873
874 const size_class_index: usize = @max(@bitSizeOf(usize) - @clz(old_memory.len - 1), @backingInt(alignment));874 const size_class_index: usize = @max(@bitSizeOf(usize) - @clz(old_memory.len - 1), @backingInt(alignment));
lib/std/os/windows.zig+14
...@@ -147,9 +147,23 @@ pub const OBJECT = struct {...@@ -147,9 +147,23 @@ pub const OBJECT = struct {
147 pub const Max: @typeInfo(@This()).@"enum".tag_type = @typeInfo(@This()).@"enum".field_names.len;147 pub const Max: @typeInfo(@This()).@"enum".tag_type = @typeInfo(@This()).@"enum".field_names.len;
148 };148 };
149149
150 pub const BASIC_INFORMATION = extern struct {
151 Attributes: ATTRIBUTES.Flags,
152 GrantedAccess: ACCESS_MASK,
153 HandleCount: ULONG,
154 PointerCount: ULONG,
155 Reserved: [10]ULONG,
156 };
157
150 pub const NAME_INFORMATION = extern struct {158 pub const NAME_INFORMATION = extern struct {
151 Name: UNICODE_STRING,159 Name: UNICODE_STRING,
152 };160 };
161
162 pub const HANDLE_FLAG = packed struct(USHORT) {
163 INHERIT: bool = false,
164 PROTECT_FROM_CLOSE: bool = false,
165 Reserved1: u14 = 0,
166 };
153};167};
154168
155pub const FILE = struct {169pub const FILE = struct {
lib/std/os/windows/ntdll.zig+7
...@@ -461,6 +461,13 @@ pub extern "ntdll" fn LdrUnregisterDllNotification(...@@ -461,6 +461,13 @@ pub extern "ntdll" fn LdrUnregisterDllNotification(
461 Cookie: LDR.DLL_NOTIFICATION.COOKIE,461 Cookie: LDR.DLL_NOTIFICATION.COOKIE,
462) callconv(.winapi) NTSTATUS;462) callconv(.winapi) NTSTATUS;
463463
464pub extern "ntdll" fn NtSetInformationObject(
465 Handle: HANDLE,
466 ObjectInformationClass: OBJECT.INFORMATION_CLASS,
467 ObjectInformation: *const anyopaque,
468 ObjectInformationLength: ULONG,
469) callconv(.winapi) NTSTATUS;
470
464pub extern "ntdll" fn NtQueryAttributesFile(471pub extern "ntdll" fn NtQueryAttributesFile(
465 ObjectAttributes: *const OBJECT.ATTRIBUTES,472 ObjectAttributes: *const OBJECT.ATTRIBUTES,
466 FileAttributes: *FILE.BASIC_INFORMATION,473 FileAttributes: *FILE.BASIC_INFORMATION,