| ... | @@ -85,6 +85,8 @@ csprng: Csprng = .uninitialized, | ... | @@ -85,6 +85,8 @@ csprng: Csprng = .uninitialized, |
| 85 | | 85 | |
| 86 | system_basic_information: SystemBasicInformation = .{}, | 86 | system_basic_information: SystemBasicInformation = .{}, |
| 87 | | 87 | |
| | 88 | process_spawn_mutex: if (is_windows) Io.Mutex else void = if (is_windows) .init else {}, |
| | 89 | |
| 88 | const SystemBasicInformation = if (!is_windows) struct {} else struct { | 90 | const 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; |
| 1706 | pub const global_single_threaded: *Threaded = &global_single_threaded_instance; | 1708 | pub const global_single_threaded: *Threaded = &global_single_threaded_instance; |
| 1707 | | 1709 | |
| 1708 | pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void { | 1710 | pub 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 { |
| 1727 | fn join(t: *Threaded) void { | 1729 | fn 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 { |
| 1789 | | 1791 | |
| 1790 | defer t.wait_group.finish(); | 1792 | defer t.wait_group.finish(); |
| 1791 | | 1793 | |
| 1792 | mutexLock(&t.mutex); | 1794 | mutexLockUncancelable(&t.mutex); |
| 1793 | defer mutexUnlock(&t.mutex); | 1795 | defer mutexUnlock(&t.mutex); |
| 1794 | | 1796 | |
| 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 | }; |
| 2090 | | 2092 | |
| 2091 | mutexLock(&t.mutex); | 2093 | mutexLockUncancelable(&t.mutex); |
| 2092 | | 2094 | |
| 2093 | const busy_count = t.busy_count; | 2095 | const busy_count = t.busy_count; |
| 2094 | | 2096 | |
| ... | @@ -2140,7 +2142,7 @@ fn concurrent( | ... | @@ -2140,7 +2142,7 @@ fn concurrent( |
| 2140 | }; | 2142 | }; |
| 2141 | errdefer future.destroy(gpa); | 2143 | errdefer future.destroy(gpa); |
| 2142 | | 2144 | |
| 2143 | mutexLock(&t.mutex); | 2145 | mutexLockUncancelable(&t.mutex); |
| 2144 | defer mutexUnlock(&t.mutex); | 2146 | defer mutexUnlock(&t.mutex); |
| 2145 | | 2147 | |
| 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 | }; |
| 2187 | | 2189 | |
| 2188 | mutexLock(&t.mutex); | 2190 | mutexLockUncancelable(&t.mutex); |
| 2189 | | 2191 | |
| 2190 | const busy_count = t.busy_count; | 2192 | const busy_count = t.busy_count; |
| 2191 | | 2193 | |
| ... | @@ -2248,7 +2250,7 @@ fn groupConcurrent( | ... | @@ -2248,7 +2250,7 @@ fn groupConcurrent( |
| 2248 | }; | 2250 | }; |
| 2249 | errdefer task.destroy(gpa); | 2251 | errdefer task.destroy(gpa); |
| 2250 | | 2252 | |
| 2251 | mutexLock(&t.mutex); | 2253 | mutexLockUncancelable(&t.mutex); |
| 2252 | defer mutexUnlock(&t.mutex); | 2254 | defer mutexUnlock(&t.mutex); |
| 2253 | | 2255 | |
| 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 { |
| 4118 | | 4120 | |
| 4119 | fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM.BASIC_INFORMATION { | 4121 | fn 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); |
| 4123 | | 4125 | |
| 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(); |
| 14092 | | 14094 | |
| 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 | }; |
| 15118 | | 15120 | |
| 15119 | fn scanEnviron(t: *Threaded) void { | 15121 | fn 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 |
| 15416 | | 15418 | |
| 15417 | fn getDevNullFd(t: *Threaded) !posix.fd_t { | 15419 | fn 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 |
| 15856 | fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { | 15858 | fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { |
| 15857 | const t: *Threaded = @ptrCast(@alignCast(userdata)); | 15859 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 15858 | | 15860 | |
| | 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 or | 15940 | options.stdin == .ignore or |
| 15861 | options.stdout == .ignore or | 15941 | 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); |
| 15899 | | 15979 | |
| | 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; |
| 15976 | | 16093 | |
| 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 | }; |
| 16015 | | 16106 | |
| 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 |
| 16072 | | 16124 | |
| 16073 | var dir_buf: std.ArrayList(u16) = .empty; | 16125 | var dir_buf: std.ArrayList(u16) = .empty; |
| 16074 | | 16126 | |
| 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 | }; |
| 16102 | | 16152 | |
| 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 the | 16153 | // 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 path | 16154 | // "cwd provided by options is in effect when choosing the executable path |
| 16107 | // to match posix semantics" behavior--we don't want to skip searching | 16155 | // 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; |
| 16112 | | 16165 | |
| 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 | } |
| 16188 | | 16241 | |
| 16189 | fn inheritFile() windows.HANDLE {} | | |
| 16190 | | | |
| 16191 | fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { | 16242 | fn 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 { |
| 16234 | | 16285 | |
| 16235 | fn getNulDevice(t: *Threaded) !windows.HANDLE { | 16286 | fn 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 { |
| 16293 | | 16344 | |
| 16294 | fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE { | 16345 | fn 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 | else | 16651 | else |
| 16601 | full_app_name; | 16652 | full_app_name; |
| 16602 | | 16653 | |
| 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 | } |
| 17757 | | 17825 | |
| 17758 | fn randomMainThread(t: *Threaded, buffer: []u8) void { | 17826 | fn randomMainThread(t: *Threaded, buffer: []u8) void { |
| 17759 | mutexLock(&t.mutex); | 17827 | mutexLockUncancelable(&t.mutex); |
| 17760 | defer mutexUnlock(&t.mutex); | 17828 | defer mutexUnlock(&t.mutex); |
| 17761 | | 17829 | |
| 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); |
| 17768 | | 17836 | |
| 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 { |
| 17951 | | 18019 | |
| 17952 | fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { | 18020 | fn 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); |
| 17956 | | 18024 | |
| 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 | } |
| 19577 | | 19645 | |
| 19578 | mutexUnlock(mutex); | 19646 | mutexUnlock(mutex); |
| 19579 | defer mutexLock(mutex); | 19647 | defer mutexLockUncancelable(mutex); |
| 19580 | | 19648 | |
| 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 | } |
| 19598 | | 19666 | |
| | 19667 | /// Same as `Io.Mutex.lock` but avoids the VTable. |
| | 19668 | pub 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. |
| 19600 | pub fn mutexLock(m: *Io.Mutex) void { | 19687 | pub 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, |