| ... | ... | @@ -85,6 +85,8 @@ csprng: Csprng = .uninitialized, |
| 85 | 85 | |
| 86 | 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 | 90 | const SystemBasicInformation = if (!is_windows) struct {} else struct { |
| 89 | 91 | buffer: windows.SYSTEM.BASIC_INFORMATION = undefined, |
| 90 | 92 | initialized: std.atomic.Value(bool) = .{ .raw = false }, |
| ... | ... | @@ -1706,7 +1708,7 @@ var global_single_threaded_instance: Threaded = .init_single_threaded; |
| 1706 | 1708 | pub const global_single_threaded: *Threaded = &global_single_threaded_instance; |
| 1707 | 1709 | |
| 1708 | 1710 | pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void { |
| 1709 | | mutexLock(&t.mutex); |
| 1711 | mutexLockUncancelable(&t.mutex); |
| 1710 | 1712 | defer mutexUnlock(&t.mutex); |
| 1711 | 1713 | t.async_limit = new_limit; |
| 1712 | 1714 | } |
| ... | ... | @@ -1727,7 +1729,7 @@ pub fn deinit(t: *Threaded) void { |
| 1727 | 1729 | fn join(t: *Threaded) void { |
| 1728 | 1730 | if (builtin.single_threaded) return; |
| 1729 | 1731 | { |
| 1730 | | mutexLock(&t.mutex); |
| 1732 | mutexLockUncancelable(&t.mutex); |
| 1731 | 1733 | defer mutexUnlock(&t.mutex); |
| 1732 | 1734 | t.join_requested = true; |
| 1733 | 1735 | } |
| ... | ... | @@ -1789,7 +1791,7 @@ fn worker(t: *Threaded) void { |
| 1789 | 1791 | |
| 1790 | 1792 | defer t.wait_group.finish(); |
| 1791 | 1793 | |
| 1792 | | mutexLock(&t.mutex); |
| 1794 | mutexLockUncancelable(&t.mutex); |
| 1793 | 1795 | defer mutexUnlock(&t.mutex); |
| 1794 | 1796 | |
| 1795 | 1797 | while (true) { |
| ... | ... | @@ -1798,7 +1800,7 @@ fn worker(t: *Threaded) void { |
| 1798 | 1800 | thread.cancel_protection = .unblocked; |
| 1799 | 1801 | const runnable: *Runnable = @fieldParentPtr("node", runnable_node); |
| 1800 | 1802 | runnable.startFn(runnable, &thread, t); |
| 1801 | | mutexLock(&t.mutex); |
| 1803 | mutexLockUncancelable(&t.mutex); |
| 1802 | 1804 | t.busy_count -= 1; |
| 1803 | 1805 | } |
| 1804 | 1806 | if (t.join_requested) break; |
| ... | ... | @@ -2088,7 +2090,7 @@ fn async( |
| 2088 | 2090 | }, |
| 2089 | 2091 | }; |
| 2090 | 2092 | |
| 2091 | | mutexLock(&t.mutex); |
| 2093 | mutexLockUncancelable(&t.mutex); |
| 2092 | 2094 | |
| 2093 | 2095 | const busy_count = t.busy_count; |
| 2094 | 2096 | |
| ... | ... | @@ -2140,7 +2142,7 @@ fn concurrent( |
| 2140 | 2142 | }; |
| 2141 | 2143 | errdefer future.destroy(gpa); |
| 2142 | 2144 | |
| 2143 | | mutexLock(&t.mutex); |
| 2145 | mutexLockUncancelable(&t.mutex); |
| 2144 | 2146 | defer mutexUnlock(&t.mutex); |
| 2145 | 2147 | |
| 2146 | 2148 | const busy_count = t.busy_count; |
| ... | ... | @@ -2185,7 +2187,7 @@ fn groupAsync( |
| 2185 | 2187 | error.OutOfMemory => return groupAsyncEager(start, context.ptr), |
| 2186 | 2188 | }; |
| 2187 | 2189 | |
| 2188 | | mutexLock(&t.mutex); |
| 2190 | mutexLockUncancelable(&t.mutex); |
| 2189 | 2191 | |
| 2190 | 2192 | const busy_count = t.busy_count; |
| 2191 | 2193 | |
| ... | ... | @@ -2248,7 +2250,7 @@ fn groupConcurrent( |
| 2248 | 2250 | }; |
| 2249 | 2251 | errdefer task.destroy(gpa); |
| 2250 | 2252 | |
| 2251 | | mutexLock(&t.mutex); |
| 2253 | mutexLockUncancelable(&t.mutex); |
| 2252 | 2254 | defer mutexUnlock(&t.mutex); |
| 2253 | 2255 | |
| 2254 | 2256 | const busy_count = t.busy_count; |
| ... | ... | @@ -4118,7 +4120,7 @@ fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat { |
| 4118 | 4120 | |
| 4119 | 4121 | fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM.BASIC_INFORMATION { |
| 4120 | 4122 | if (!t.system_basic_information.initialized.load(.acquire)) { |
| 4121 | | mutexLock(&t.mutex); |
| 4123 | mutexLockUncancelable(&t.mutex); |
| 4122 | 4124 | defer mutexUnlock(&t.mutex); |
| 4123 | 4125 | |
| 4124 | 4126 | switch (windows.ntdll.NtQuerySystemInformation( |
| ... | ... | @@ -10011,7 +10013,7 @@ fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize { |
| 10011 | 10013 | .PENDING => unreachable, |
| 10012 | 10014 | .CANCELLED => unreachable, |
| 10013 | 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 | 10017 | .INVALID_HANDLE => return error.NotOpenForReading, |
| 10016 | 10018 | .INVALID_DEVICE_REQUEST => return error.IsDir, |
| 10017 | 10019 | .FILE_LOCK_CONFLICT => return error.LockViolation, |
| ... | ... | @@ -10028,7 +10030,7 @@ fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize { |
| 10028 | 10030 | .INVALID_USER_BUFFER => return error.SystemResources, |
| 10029 | 10031 | .NO_MEMORY => return error.SystemResources, |
| 10030 | 10032 | .QUOTA_EXCEEDED => return error.SystemResources, |
| 10031 | | .PIPE_BROKEN => return error.BrokenPipe, |
| 10033 | .PIPE_BROKEN, .PIPE_CLOSING => return error.BrokenPipe, |
| 10032 | 10034 | .INVALID_HANDLE => return error.NotOpenForWriting, |
| 10033 | 10035 | .FILE_LOCK_CONFLICT => return error.LockViolation, |
| 10034 | 10036 | .ACCESS_DENIED => return error.AccessDenied, |
| ... | ... | @@ -14091,7 +14093,7 @@ fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancel |
| 14091 | 14093 | const current_thread_id = Thread.currentId(); |
| 14092 | 14094 | |
| 14093 | 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 | 14097 | assert(t.stderr_mutex_lock_count == 0); |
| 14096 | 14098 | @atomicStore(std.Thread.Id, &t.stderr_mutex_locker, current_thread_id, .unordered); |
| 14097 | 14099 | } |
| ... | ... | @@ -15117,7 +15119,7 @@ const WindowsEnvironStrings = struct { |
| 15117 | 15119 | }; |
| 15118 | 15120 | |
| 15119 | 15121 | fn scanEnviron(t: *Threaded) void { |
| 15120 | | mutexLock(&t.mutex); |
| 15122 | mutexLockUncancelable(&t.mutex); |
| 15121 | 15123 | defer mutexUnlock(&t.mutex); |
| 15122 | 15124 | if (t.environ_initialized) return; |
| 15123 | 15125 | t.environ.scan(t.allocator); |
| ... | ... | @@ -15416,7 +15418,7 @@ fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Sp |
| 15416 | 15418 | |
| 15417 | 15419 | fn getDevNullFd(t: *Threaded) !posix.fd_t { |
| 15418 | 15420 | { |
| 15419 | | mutexLock(&t.mutex); |
| 15421 | try mutexLock(&t.mutex); |
| 15420 | 15422 | defer mutexUnlock(&t.mutex); |
| 15421 | 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 | 15430 | .SUCCESS => { |
| 15429 | 15431 | syscall.finish(); |
| 15430 | 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 | 15434 | defer mutexUnlock(&t.mutex); |
| 15433 | 15435 | if (t.null_file.fd != -1) { |
| 15434 | 15436 | closeFd(fresh_fd); |
| ... | ... | @@ -15856,6 +15858,84 @@ fn processSpawnDarwin(userdata: ?*anyopaque, options: process.SpawnOptions) proc |
| 15856 | 15858 | fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child { |
| 15857 | 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 | 15939 | const any_ignore = |
| 15860 | 15940 | options.stdin == .ignore or |
| 15861 | 15941 | options.stdout == .ignore or |
| ... | ... | @@ -15897,6 +15977,43 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15897 | 15977 | }) else undefined; |
| 15898 | 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 | 16017 | var siStartInfo: windows.STARTUPINFOW = .{ |
| 15901 | 16018 | .cb = @sizeOf(windows.STARTUPINFOW), |
| 15902 | 16019 | .dwFlags = windows.STARTF_USESTDHANDLES, |
| ... | ... | @@ -15974,32 +16091,6 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 15974 | 16091 | }; |
| 15975 | 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 | 16094 | const env_block = env_block: { |
| 16004 | 16095 | const prog_handle = if (options.progress_node.index != .none) |
| 16005 | 16096 | prog_pipe[1] |
| ... | ... | @@ -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 | 16107 | run: { |
| 16056 | 16108 | // We have to scan each time because the PEB environment pointer is not stable. |
| 16057 | 16109 | const env_strings: WindowsEnvironStrings = .scan(); |
| ... | ... | @@ -16072,10 +16124,8 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 16072 | 16124 | |
| 16073 | 16125 | var dir_buf: std.ArrayList(u16) = .empty; |
| 16074 | 16126 | |
| 16075 | | if (cwd_path_w.len > 0) { |
| 16076 | | try dir_buf.appendSlice(arena, cwd_path_w); |
| 16077 | | } |
| 16078 | | if (app_dirname_w) |app_dir| { |
| 16127 | if (app_path_w) |app_path| try dir_buf.appendSlice(arena, app_path); |
| 16128 | if (app_dir_w) |app_dir| { |
| 16079 | 16129 | if (dir_buf.items.len > 0) try dir_buf.append(arena, Dir.path.sep); |
| 16080 | 16130 | try dir_buf.appendSlice(arena, app_dir); |
| 16081 | 16131 | } |
| ... | ... | @@ -16100,15 +16150,18 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 16100 | 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 | 16153 | // We still search the path if the cwd is absolute because of the |
| 16106 | 16154 | // "cwd provided by options is in effect when choosing the executable path |
| 16107 | 16155 | // to match posix semantics" behavior--we don't want to skip searching |
| 16108 | 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) { |
| 16110 | | return original_err; |
| 16111 | | } |
| 16157 | if (switch (options.exe) { |
| 16158 | // If the app name had path separators, that disallows PATH searching. |
| 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 | 16166 | var it = std.mem.tokenizeScalar(u16, PATH, ';'); |
| 16114 | 16167 | while (it.next()) |search_path| { |
| ... | ... | @@ -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 | 16242 | fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 16192 | 16243 | { |
| 16193 | | mutexLock(&t.mutex); |
| 16244 | try mutexLock(&t.mutex); |
| 16194 | 16245 | defer mutexUnlock(&t.mutex); |
| 16195 | 16246 | if (t.random_file.handle) |handle| return handle; |
| 16196 | 16247 | } |
| ... | ... | @@ -16213,7 +16264,7 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 16213 | 16264 | )) { |
| 16214 | 16265 | .SUCCESS => { |
| 16215 | 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 | 16268 | defer mutexUnlock(&t.mutex); |
| 16218 | 16269 | if (t.random_file.handle) |prev_handle| { |
| 16219 | 16270 | windows.CloseHandle(fresh_handle); |
| ... | ... | @@ -16234,7 +16285,7 @@ fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE { |
| 16234 | 16285 | |
| 16235 | 16286 | fn getNulDevice(t: *Threaded) !windows.HANDLE { |
| 16236 | 16287 | { |
| 16237 | | mutexLock(&t.mutex); |
| 16288 | try mutexLock(&t.mutex); |
| 16238 | 16289 | defer mutexUnlock(&t.mutex); |
| 16239 | 16290 | if (t.null_file.handle) |handle| return handle; |
| 16240 | 16291 | } |
| ... | ... | @@ -16260,7 +16311,7 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE { |
| 16260 | 16311 | )) { |
| 16261 | 16312 | .SUCCESS => { |
| 16262 | 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 | 16315 | defer mutexUnlock(&t.mutex); |
| 16265 | 16316 | if (t.null_file.handle) |prev_handle| { |
| 16266 | 16317 | windows.CloseHandle(fresh_handle); |
| ... | ... | @@ -16293,7 +16344,7 @@ fn getNulDevice(t: *Threaded) !windows.HANDLE { |
| 16293 | 16344 | |
| 16294 | 16345 | fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE { |
| 16295 | 16346 | { |
| 16296 | | mutexLock(&t.mutex); |
| 16347 | try mutexLock(&t.mutex); |
| 16297 | 16348 | defer mutexUnlock(&t.mutex); |
| 16298 | 16349 | if (t.pipe_file.handle) |handle| return handle; |
| 16299 | 16350 | } |
| ... | ... | @@ -16315,7 +16366,7 @@ fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE { |
| 16315 | 16366 | )) { |
| 16316 | 16367 | .SUCCESS => { |
| 16317 | 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 | 16370 | defer mutexUnlock(&t.mutex); |
| 16320 | 16371 | if (t.pipe_file.handle) |prev_handle| { |
| 16321 | 16372 | windows.CloseHandle(fresh_handle); |
| ... | ... | @@ -16600,7 +16651,15 @@ fn windowsCreateProcessPathExt( |
| 16600 | 16651 | else |
| 16601 | 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 | 16663 | return; |
| 16605 | 16664 | } else |err| switch (err) { |
| 16606 | 16665 | error.FileNotFound => continue, |
| ... | ... | @@ -17716,6 +17775,15 @@ fn inheritParentHandle(handle: posix.fd_t) Io.InheritParentHandleError!void { |
| 17716 | 17775 | .visionos, |
| 17717 | 17776 | .watchos, |
| 17718 | 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 | 17787 | else => return error.UnsupportedOperation, |
| 17720 | 17788 | } |
| 17721 | 17789 | } |
| ... | ... | @@ -17756,7 +17824,7 @@ fn random(userdata: ?*anyopaque, buffer: []u8) void { |
| 17756 | 17824 | } |
| 17757 | 17825 | |
| 17758 | 17826 | fn randomMainThread(t: *Threaded, buffer: []u8) void { |
| 17759 | | mutexLock(&t.mutex); |
| 17827 | mutexLockUncancelable(&t.mutex); |
| 17760 | 17828 | defer mutexUnlock(&t.mutex); |
| 17761 | 17829 | |
| 17762 | 17830 | if (!t.csprng.isInitialized()) { |
| ... | ... | @@ -17764,7 +17832,7 @@ fn randomMainThread(t: *Threaded, buffer: []u8) void { |
| 17764 | 17832 | var seed: [Csprng.seed_len]u8 = undefined; |
| 17765 | 17833 | { |
| 17766 | 17834 | mutexUnlock(&t.mutex); |
| 17767 | | defer mutexLock(&t.mutex); |
| 17835 | defer mutexLockUncancelable(&t.mutex); |
| 17768 | 17836 | |
| 17769 | 17837 | const prev = swapCancelProtection(t, .blocked); |
| 17770 | 17838 | defer _ = swapCancelProtection(t, prev); |
| ... | ... | @@ -17951,7 +18019,7 @@ fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void { |
| 17951 | 18019 | |
| 17952 | 18020 | fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { |
| 17953 | 18021 | { |
| 17954 | | mutexLock(&t.mutex); |
| 18022 | try mutexLock(&t.mutex); |
| 17955 | 18023 | defer mutexUnlock(&t.mutex); |
| 17956 | 18024 | |
| 17957 | 18025 | if (t.random_file.fd == -2) return error.EntropyUnavailable; |
| ... | ... | @@ -17992,7 +18060,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { |
| 17992 | 18060 | .SUCCESS => { |
| 17993 | 18061 | syscall.finish(); |
| 17994 | 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 | 18064 | defer mutexUnlock(&t.mutex); |
| 17997 | 18065 | if (t.random_file.fd >= 0) { |
| 17998 | 18066 | closeFd(fd); |
| ... | ... | @@ -18020,7 +18088,7 @@ fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t { |
| 18020 | 18088 | switch (posix.errno(fstat_sym(fd, &stat))) { |
| 18021 | 18089 | .SUCCESS => { |
| 18022 | 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 | 18092 | defer mutexUnlock(&t.mutex); |
| 18025 | 18093 | if (t.random_file.fd >= 0) { |
| 18026 | 18094 | closeFd(fd); |
| ... | ... | @@ -19576,7 +19644,7 @@ fn condWait(cond: *Io.Condition, mutex: *Io.Mutex) void { |
| 19576 | 19644 | } |
| 19577 | 19645 | |
| 19578 | 19646 | mutexUnlock(mutex); |
| 19579 | | defer mutexLock(mutex); |
| 19647 | defer mutexLockUncancelable(mutex); |
| 19580 | 19648 | |
| 19581 | 19649 | while (true) { |
| 19582 | 19650 | Thread.futexWaitUncancelable(&cond.epoch.raw, epoch, null); |
| ... | ... | @@ -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 | 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 | 19688 | const initial_state = m.state.cmpxchgStrong( |
| 19602 | 19689 | .unlocked, |
| 19603 | 19690 | .locked_once, |