| ... | @@ -64,6 +64,8 @@ stderr_writer_initialized: bool = false, | ... | @@ -64,6 +64,8 @@ stderr_writer_initialized: bool = false, |
| 64 | argv0: Argv0, | 64 | argv0: Argv0, |
| 65 | environ: Environ, | 65 | environ: Environ, |
| 66 | | 66 | |
| | 67 | nul_handle: if (is_windows) ?windows.HANDLE else void = if (is_windows) null else {}, |
| | 68 | |
| 67 | pub const Argv0 = switch (native_os) { | 69 | pub const Argv0 = switch (native_os) { |
| 68 | .openbsd, .haiku => struct { | 70 | .openbsd, .haiku => struct { |
| 69 | value: ?[*:0]const u8, | 71 | value: ?[*:0]const u8, |
| ... | @@ -1247,8 +1249,13 @@ pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void { | ... | @@ -1247,8 +1249,13 @@ pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void { |
| 1247 | | 1249 | |
| 1248 | pub fn deinit(t: *Threaded) void { | 1250 | pub fn deinit(t: *Threaded) void { |
| 1249 | t.join(); | 1251 | t.join(); |
| 1250 | if (is_windows and t.wsa.status == .initialized) { | 1252 | if (is_windows) { |
| 1251 | if (ws2_32.WSACleanup() != 0) recoverableOsBugDetected(); | 1253 | if (t.wsa.status == .initialized) { |
| | 1254 | if (ws2_32.WSACleanup() != 0) recoverableOsBugDetected(); |
| | 1255 | } |
| | 1256 | if (t.nul_handle) |handle| { |
| | 1257 | windows.CloseHandle(handle); |
| | 1258 | } |
| 1252 | } | 1259 | } |
| 1253 | if (posix.Sigaction != void and t.have_signal_handler) { | 1260 | if (posix.Sigaction != void and t.have_signal_handler) { |
| 1254 | if (have_sig_io) posix.sigaction(.IO, &t.old_sig_io, null); | 1261 | if (have_sig_io) posix.sigaction(.IO, &t.old_sig_io, null); |
| ... | @@ -3666,7 +3673,10 @@ pub fn dirOpenFileWtf16( | ... | @@ -3666,7 +3673,10 @@ pub fn dirOpenFileWtf16( |
| 3666 | // kernel bug with retry attempts. | 3673 | // kernel bug with retry attempts. |
| 3667 | syscall.finish(); | 3674 | syscall.finish(); |
| 3668 | if (max_attempts - attempt == 0) return error.SharingViolation; | 3675 | if (max_attempts - attempt == 0) return error.SharingViolation; |
| 3669 | _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE); | 3676 | try parking_sleep.sleep(.{ .duration = .{ |
| | 3677 | .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1), |
| | 3678 | .clock = .awake, |
| | 3679 | } }); |
| 3670 | attempt += 1; | 3680 | attempt += 1; |
| 3671 | syscall = try .start(); | 3681 | syscall = try .start(); |
| 3672 | continue; | 3682 | continue; |
| ... | @@ -3688,7 +3698,10 @@ pub fn dirOpenFileWtf16( | ... | @@ -3688,7 +3698,10 @@ pub fn dirOpenFileWtf16( |
| 3688 | // fixed by sleeping and retrying until the error goes away. | 3698 | // fixed by sleeping and retrying until the error goes away. |
| 3689 | syscall.finish(); | 3699 | syscall.finish(); |
| 3690 | if (max_attempts - attempt == 0) return error.SharingViolation; | 3700 | if (max_attempts - attempt == 0) return error.SharingViolation; |
| 3691 | _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE); | 3701 | try parking_sleep.sleep(.{ .duration = .{ |
| | 3702 | .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1), |
| | 3703 | .clock = .awake, |
| | 3704 | } }); |
| 3692 | attempt += 1; | 3705 | attempt += 1; |
| 3693 | syscall = try .start(); | 3706 | syscall = try .start(); |
| 3694 | continue; | 3707 | continue; |
| ... | @@ -13377,34 +13390,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -13377,34 +13390,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 13377 | options.stdout == .ignore or | 13390 | options.stdout == .ignore or |
| 13378 | options.stderr == .ignore; | 13391 | options.stderr == .ignore; |
| 13379 | | 13392 | |
| 13380 | // TODO: cache the handle to null file! | 13393 | const nul_handle = if (any_ignore) try getNulHandle(t) else undefined; |
| 13381 | const nul_handle = if (any_ignore) | | |
| 13382 | // "\Device\Null" or "\??\NUL" | | |
| 13383 | windows.OpenFile(&[_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }, .{ | | |
| 13384 | .access_mask = .{ | | |
| 13385 | .STANDARD = .{ .SYNCHRONIZE = true }, | | |
| 13386 | .GENERIC = .{ .WRITE = true, .READ = true }, | | |
| 13387 | }, | | |
| 13388 | .sa = &saAttr, | | |
| 13389 | .creation = .OPEN, | | |
| 13390 | }) catch |err| switch (err) { | | |
| 13391 | error.PathAlreadyExists => return error.Unexpected, // not possible for "NUL" | | |
| 13392 | error.PipeBusy => return error.Unexpected, // not possible for "NUL" | | |
| 13393 | error.NoDevice => return error.Unexpected, // not possible for "NUL" | | |
| 13394 | error.FileNotFound => return error.Unexpected, // not possible for "NUL" | | |
| 13395 | error.AccessDenied => return error.Unexpected, // not possible for "NUL" | | |
| 13396 | error.NameTooLong => return error.Unexpected, // not possible for "NUL" | | |
| 13397 | error.WouldBlock => return error.Unexpected, // not possible for "NUL" | | |
| 13398 | error.NetworkNotFound => return error.Unexpected, // not possible for "NUL" | | |
| 13399 | error.AntivirusInterference => return error.Unexpected, // not possible for "NUL" | | |
| 13400 | error.OperationCanceled => return error.Unexpected, // we're not canceling the operation | | |
| 13401 | else => |e| return e, | | |
| 13402 | } | | |
| 13403 | else | | |
| 13404 | undefined; | | |
| 13405 | defer { | | |
| 13406 | if (any_ignore) posix.close(nul_handle); | | |
| 13407 | } | | |
| 13408 | | 13394 | |
| 13409 | var g_hChildStd_IN_Rd: ?windows.HANDLE = null; | 13395 | var g_hChildStd_IN_Rd: ?windows.HANDLE = null; |
| 13410 | var g_hChildStd_IN_Wr: ?windows.HANDLE = null; | 13396 | var g_hChildStd_IN_Wr: ?windows.HANDLE = null; |
| ... | @@ -13647,6 +13633,101 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro | ... | @@ -13647,6 +13633,101 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 13647 | }; | 13633 | }; |
| 13648 | } | 13634 | } |
| 13649 | | 13635 | |
| | 13636 | fn getNulHandle(t: *Threaded) !windows.HANDLE { |
| | 13637 | { |
| | 13638 | t.mutex.lock(); |
| | 13639 | defer t.mutex.unlock(); |
| | 13640 | if (t.nul_handle) |handle| return handle; |
| | 13641 | } |
| | 13642 | |
| | 13643 | const device_path = [_]u16{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' }; |
| | 13644 | var nt_name: windows.UNICODE_STRING = .{ |
| | 13645 | .Length = device_path.len * 2, |
| | 13646 | .MaximumLength = device_path.len * 2, |
| | 13647 | .Buffer = @constCast(&device_path), |
| | 13648 | }; |
| | 13649 | const attr: windows.OBJECT_ATTRIBUTES = .{ |
| | 13650 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), |
| | 13651 | .RootDirectory = null, |
| | 13652 | .Attributes = .{ |
| | 13653 | .INHERIT = true, |
| | 13654 | }, |
| | 13655 | .ObjectName = &nt_name, |
| | 13656 | .SecurityDescriptor = null, |
| | 13657 | .SecurityQualityOfService = null, |
| | 13658 | }; |
| | 13659 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| | 13660 | var fresh_handle: windows.HANDLE = undefined; |
| | 13661 | var syscall: Syscall = try .start(); |
| | 13662 | while (true) switch (windows.ntdll.NtCreateFile( |
| | 13663 | &fresh_handle, |
| | 13664 | .{ |
| | 13665 | .STANDARD = .{ .SYNCHRONIZE = true }, |
| | 13666 | .GENERIC = .{ .WRITE = true, .READ = true }, |
| | 13667 | }, |
| | 13668 | &attr, |
| | 13669 | &io_status_block, |
| | 13670 | null, |
| | 13671 | .{ .NORMAL = true }, |
| | 13672 | .VALID_FLAGS, |
| | 13673 | .OPEN, |
| | 13674 | .{ |
| | 13675 | .DIRECTORY_FILE = false, |
| | 13676 | .NON_DIRECTORY_FILE = true, |
| | 13677 | .IO = .SYNCHRONOUS_NONALERT, |
| | 13678 | .OPEN_REPARSE_POINT = false, |
| | 13679 | }, |
| | 13680 | null, |
| | 13681 | 0, |
| | 13682 | )) { |
| | 13683 | .SUCCESS => { |
| | 13684 | syscall.finish(); |
| | 13685 | t.mutex.lock(); // Another thread might have won the race. |
| | 13686 | defer t.mutex.unlock(); |
| | 13687 | if (t.nul_handle) |prev_handle| { |
| | 13688 | windows.CloseHandle(fresh_handle); |
| | 13689 | return prev_handle; |
| | 13690 | } else { |
| | 13691 | t.nul_handle = fresh_handle; |
| | 13692 | return fresh_handle; |
| | 13693 | } |
| | 13694 | }, |
| | 13695 | .DELETE_PENDING => { |
| | 13696 | // This error means that there *was* a file in this location on |
| | 13697 | // the file system, but it was deleted. However, the OS is not |
| | 13698 | // finished with the deletion operation, and so this CreateFile |
| | 13699 | // call has failed. There is not really a sane way to handle |
| | 13700 | // this other than retrying the creation after the OS finishes |
| | 13701 | // the deletion. |
| | 13702 | syscall.finish(); |
| | 13703 | try parking_sleep.sleep(.{ .duration = .{ |
| | 13704 | .raw = .fromMilliseconds(1), |
| | 13705 | .clock = .awake, |
| | 13706 | } }); |
| | 13707 | syscall = try .start(); |
| | 13708 | continue; |
| | 13709 | }, |
| | 13710 | .CANCELLED => { |
| | 13711 | try syscall.checkCancel(); |
| | 13712 | continue; |
| | 13713 | }, |
| | 13714 | .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status), |
| | 13715 | .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status), |
| | 13716 | .INVALID_HANDLE => |status| return syscall.ntstatusBug(status), |
| | 13717 | .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName), |
| | 13718 | .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound), |
| | 13719 | .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound), |
| | 13720 | .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice), |
| | 13721 | .SHARING_VIOLATION => return syscall.fail(error.AccessDenied), |
| | 13722 | .ACCESS_DENIED => return syscall.fail(error.AccessDenied), |
| | 13723 | .PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice), |
| | 13724 | .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir), |
| | 13725 | .NOT_A_DIRECTORY => return syscall.fail(error.NotDir), |
| | 13726 | .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied), |
| | 13727 | else => |status| return syscall.unexpectedNtstatus(status), |
| | 13728 | }; |
| | 13729 | } |
| | 13730 | |
| 13650 | /// Expects `app_buf` to contain exactly the app name, and `dir_buf` to contain exactly the dir path. | 13731 | /// Expects `app_buf` to contain exactly the app name, and `dir_buf` to contain exactly the dir path. |
| 13651 | /// After return, `app_buf` will always contain exactly the app name and `dir_buf` will always contain exactly the dir path. | 13732 | /// After return, `app_buf` will always contain exactly the app name and `dir_buf` will always contain exactly the dir path. |
| 13652 | /// Note: `app_buf` should not contain any leading path separators. | 13733 | /// Note: `app_buf` should not contain any leading path separators. |