| ... | ... | @@ -64,6 +64,8 @@ stderr_writer_initialized: bool = false, |
| 64 | 64 | argv0: Argv0, |
| 65 | 65 | environ: Environ, |
| 66 | 66 | |
| 67 | nul_handle: if (is_windows) ?windows.HANDLE else void = if (is_windows) null else {}, |
| 68 | |
| 67 | 69 | pub const Argv0 = switch (native_os) { |
| 68 | 70 | .openbsd, .haiku => struct { |
| 69 | 71 | value: ?[*:0]const u8, |
| ... | ... | @@ -1247,8 +1249,13 @@ pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void { |
| 1247 | 1249 | |
| 1248 | 1250 | pub fn deinit(t: *Threaded) void { |
| 1249 | 1251 | t.join(); |
| 1250 | | if (is_windows and t.wsa.status == .initialized) { |
| 1251 | | if (ws2_32.WSACleanup() != 0) recoverableOsBugDetected(); |
| 1252 | if (is_windows) { |
| 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 | 1260 | if (posix.Sigaction != void and t.have_signal_handler) { |
| 1254 | 1261 | if (have_sig_io) posix.sigaction(.IO, &t.old_sig_io, null); |
| ... | ... | @@ -3666,7 +3673,10 @@ pub fn dirOpenFileWtf16( |
| 3666 | 3673 | // kernel bug with retry attempts. |
| 3667 | 3674 | syscall.finish(); |
| 3668 | 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 | 3680 | attempt += 1; |
| 3671 | 3681 | syscall = try .start(); |
| 3672 | 3682 | continue; |
| ... | ... | @@ -3688,7 +3698,10 @@ pub fn dirOpenFileWtf16( |
| 3688 | 3698 | // fixed by sleeping and retrying until the error goes away. |
| 3689 | 3699 | syscall.finish(); |
| 3690 | 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 | 3705 | attempt += 1; |
| 3693 | 3706 | syscall = try .start(); |
| 3694 | 3707 | continue; |
| ... | ... | @@ -13377,34 +13390,7 @@ fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) pro |
| 13377 | 13390 | options.stdout == .ignore or |
| 13378 | 13391 | options.stderr == .ignore; |
| 13379 | 13392 | |
| 13380 | | // TODO: cache the handle to null file! |
| 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 | | } |
| 13393 | const nul_handle = if (any_ignore) try getNulHandle(t) else undefined; |
| 13408 | 13394 | |
| 13409 | 13395 | var g_hChildStd_IN_Rd: ?windows.HANDLE = null; |
| 13410 | 13396 | var g_hChildStd_IN_Wr: ?windows.HANDLE = null; |
| ... | ... | @@ -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 | 13731 | /// Expects `app_buf` to contain exactly the app name, and `dir_buf` to contain exactly the dir path. |
| 13651 | 13732 | /// After return, `app_buf` will always contain exactly the app name and `dir_buf` will always contain exactly the dir path. |
| 13652 | 13733 | /// Note: `app_buf` should not contain any leading path separators. |