| author | |
| committer | |
| log | 06129d7e3d5a8d9449edc98510a6d4f7a171b27f |
| tree | 9aa7bddedea56cb33ace3e7fed78d7e92b06472a |
| parent | 488f68069ba0a81f46a5afe721b908cfd3c61f76 |
This modifies the lock semantics from using AccessMode to using
NtLockFile/NtUnlockFile.
This is a breaking change.6 files changed, 239 insertions(+), 100 deletions(-)
doc/langref.html.in+8| ... | @@ -10713,6 +10713,14 @@ fn readU32Be() u32 {} | ... | @@ -10713,6 +10713,14 @@ fn readU32Be() u32 {} |
| 10713 | See the Zig Standard Library for more examples. | 10713 | See the Zig Standard Library for more examples. |
| 10714 | </p> | 10714 | </p> |
| 10715 | {#header_close#} | 10715 | {#header_close#} |
| 10716 | {#header_open|Doc Comment Guidance#} | ||
| 10717 | <ul> | ||
| 10718 | <li>Omit any information that is redundant based on the name of the thing being documented.</li> | ||
| 10719 | <li>Duplicating information onto multiple similar functions is encouraged because it helps IDEs and other tools provide better help text.</li> | ||
| 10720 | <li>Use the word <strong>assume</strong> to indicate invariants that cause {#link|Undefined Behavior#} when violated.</li> | ||
| 10721 | <li>Use the word <strong>assert</strong> to indicate invariants that cause <em>safety-checked</em> {#link|Undefined Behavior#} when violated.</li> | ||
| 10722 | </ul> | ||
| 10723 | {#header_close#} | ||
| 10716 | {#header_close#} | 10724 | {#header_close#} |
| 10717 | {#header_open|Source Encoding#} | 10725 | {#header_open|Source Encoding#} |
| 10718 | <p>Zig source code is encoded in UTF-8. An invalid UTF-8 byte sequence results in a compile error.</p> | 10726 | <p>Zig source code is encoded in UTF-8. An invalid UTF-8 byte sequence results in a compile error.</p> |
lib/std/fs.zig+47-17| ... | @@ -883,24 +883,39 @@ pub const Dir = struct { | ... | @@ -883,24 +883,39 @@ pub const Dir = struct { |
| 883 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. | 883 | /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. |
| 884 | pub fn openFileW(self: Dir, sub_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File { | 884 | pub fn openFileW(self: Dir, sub_path_w: []const u16, flags: File.OpenFlags) File.OpenError!File { |
| 885 | const w = os.windows; | 885 | const w = os.windows; |
| 886 | return @as(File, .{ | 886 | const file: File = .{ |
| 887 | .handle = try os.windows.OpenFile(sub_path_w, .{ | 887 | .handle = try w.OpenFile(sub_path_w, .{ |
| 888 | .dir = self.fd, | 888 | .dir = self.fd, |
| 889 | .access_mask = w.SYNCHRONIZE | | 889 | .access_mask = w.SYNCHRONIZE | |
| 890 | (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | | 890 | (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | |
| 891 | (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0), | 891 | (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0), |
| 892 | .share_access = switch (flags.lock) { | ||
| 893 | .None => w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | ||
| 894 | .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | ||
| 895 | .Exclusive => w.FILE_SHARE_DELETE, | ||
| 896 | }, | ||
| 897 | .share_access_nonblocking = flags.lock_nonblocking, | ||
| 898 | .creation = w.FILE_OPEN, | 892 | .creation = w.FILE_OPEN, |
| 899 | .io_mode = flags.intended_io_mode, | 893 | .io_mode = flags.intended_io_mode, |
| 900 | }), | 894 | }), |
| 901 | .capable_io_mode = std.io.default_mode, | 895 | .capable_io_mode = std.io.default_mode, |
| 902 | .intended_io_mode = flags.intended_io_mode, | 896 | .intended_io_mode = flags.intended_io_mode, |
| 903 | }); | 897 | }; |
| 898 | var io: w.IO_STATUS_BLOCK = undefined; | ||
| 899 | const range_off: w.LARGE_INTEGER = 0; | ||
| 900 | const range_len: w.LARGE_INTEGER = 1; | ||
| 901 | const exclusive = switch (flags.lock) { | ||
| 902 | .None => return file, | ||
| 903 | .Shared => false, | ||
| 904 | .Exclusive => true, | ||
| 905 | }; | ||
| 906 | try w.LockFile( | ||
| 907 | file.handle, | ||
| 908 | null, | ||
| 909 | null, | ||
| 910 | null, | ||
| 911 | &io, | ||
| 912 | &range_off, | ||
| 913 | &range_len, | ||
| 914 | null, | ||
| 915 | @boolToInt(flags.lock_nonblocking), | ||
| 916 | @boolToInt(exclusive), | ||
| 917 | ); | ||
| 918 | return file; | ||
| 904 | } | 919 | } |
| 905 | 920 | ||
| 906 | /// Creates, opens, or overwrites a file with write access. | 921 | /// Creates, opens, or overwrites a file with write access. |
| ... | @@ -1019,16 +1034,10 @@ pub const Dir = struct { | ... | @@ -1019,16 +1034,10 @@ pub const Dir = struct { |
| 1019 | pub fn createFileW(self: Dir, sub_path_w: []const u16, flags: File.CreateFlags) File.OpenError!File { | 1034 | pub fn createFileW(self: Dir, sub_path_w: []const u16, flags: File.CreateFlags) File.OpenError!File { |
| 1020 | const w = os.windows; | 1035 | const w = os.windows; |
| 1021 | const read_flag = if (flags.read) @as(u32, w.GENERIC_READ) else 0; | 1036 | const read_flag = if (flags.read) @as(u32, w.GENERIC_READ) else 0; |
| 1022 | return @as(File, .{ | 1037 | const file: File = .{ |
| 1023 | .handle = try os.windows.OpenFile(sub_path_w, .{ | 1038 | .handle = try os.windows.OpenFile(sub_path_w, .{ |
| 1024 | .dir = self.fd, | 1039 | .dir = self.fd, |
| 1025 | .access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | read_flag, | 1040 | .access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | read_flag, |
| 1026 | .share_access = switch (flags.lock) { | ||
| 1027 | .None => w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | ||
| 1028 | .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, | ||
| 1029 | .Exclusive => w.FILE_SHARE_DELETE, | ||
| 1030 | }, | ||
| 1031 | .share_access_nonblocking = flags.lock_nonblocking, | ||
| 1032 | .creation = if (flags.exclusive) | 1041 | .creation = if (flags.exclusive) |
| 1033 | @as(u32, w.FILE_CREATE) | 1042 | @as(u32, w.FILE_CREATE) |
| 1034 | else if (flags.truncate) | 1043 | else if (flags.truncate) |
| ... | @@ -1039,7 +1048,28 @@ pub const Dir = struct { | ... | @@ -1039,7 +1048,28 @@ pub const Dir = struct { |
| 1039 | }), | 1048 | }), |
| 1040 | .capable_io_mode = std.io.default_mode, | 1049 | .capable_io_mode = std.io.default_mode, |
| 1041 | .intended_io_mode = flags.intended_io_mode, | 1050 | .intended_io_mode = flags.intended_io_mode, |
| 1042 | }); | 1051 | }; |
| 1052 | var io: w.IO_STATUS_BLOCK = undefined; | ||
| 1053 | const range_off: w.LARGE_INTEGER = 0; | ||
| 1054 | const range_len: w.LARGE_INTEGER = 1; | ||
| 1055 | const exclusive = switch (flags.lock) { | ||
| 1056 | .None => return file, | ||
| 1057 | .Shared => false, | ||
| 1058 | .Exclusive => true, | ||
| 1059 | }; | ||
| 1060 | try w.LockFile( | ||
| 1061 | file.handle, | ||
| 1062 | null, | ||
| 1063 | null, | ||
| 1064 | null, | ||
| 1065 | &io, | ||
| 1066 | &range_off, | ||
| 1067 | &range_len, | ||
| 1068 | null, | ||
| 1069 | @boolToInt(flags.lock_nonblocking), | ||
| 1070 | @boolToInt(exclusive), | ||
| 1071 | ); | ||
| 1072 | return file; | ||
| 1043 | } | 1073 | } |
| 1044 | 1074 | ||
| 1045 | pub const openRead = @compileError("deprecated in favor of openFile"); | 1075 | pub const openRead = @compileError("deprecated in favor of openFile"); |
lib/std/fs/file.zig+140-26| ... | @@ -830,29 +830,25 @@ pub const File = struct { | ... | @@ -830,29 +830,25 @@ pub const File = struct { |
| 830 | return .{ .context = file }; | 830 | return .{ .context = file }; |
| 831 | } | 831 | } |
| 832 | 832 | ||
| 833 | pub const SetLockError = os.FlockError; | 833 | const range_off: windows.LARGE_INTEGER = 0; |
| 834 | const range_len: windows.LARGE_INTEGER = 1; | ||
| 835 | |||
| 836 | pub const LockError = error{ | ||
| 837 | SystemResources, | ||
| 838 | } || os.UnexpectedError; | ||
| 834 | 839 | ||
| 835 | /// Blocks when an incompatible lock is held by another process. | 840 | /// Blocks when an incompatible lock is held by another process. |
| 836 | /// `non_blocking` may be used to make a non-blocking request, | ||
| 837 | /// causing this function to possibly return `error.WouldBlock`. | ||
| 838 | /// A process may hold only one type of lock (shared or exclusive) on | 841 | /// A process may hold only one type of lock (shared or exclusive) on |
| 839 | /// a file. When a process terminates in any way, the lock is released. | 842 | /// a file. When a process terminates in any way, the lock is released. |
| 843 | /// | ||
| 844 | /// Assumes the file is unlocked. | ||
| 845 | /// | ||
| 840 | /// TODO: integrate with async I/O | 846 | /// TODO: integrate with async I/O |
| 841 | pub fn setLock(file: File, lock: Lock, non_blocking: bool) SetLockError!void { | 847 | pub fn lock(file: File, l: Lock) LockError!void { |
| 842 | if (is_windows) { | 848 | if (is_windows) { |
| 843 | const range_off: windows.LARGE_INTEGER = 0; | 849 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| 844 | const range_len: windows.LARGE_INTEGER = 1; | 850 | const exclusive = switch (l) { |
| 845 | const exclusive = switch (lock) { | 851 | .None => return, |
| 846 | .None => return windows.UnlockFile( | ||
| 847 | file.handle, | ||
| 848 | null, | ||
| 849 | &range_off, | ||
| 850 | &range_len, | ||
| 851 | null, | ||
| 852 | ) catch |err| switch (err) { | ||
| 853 | error.RangeNotLocked => return, | ||
| 854 | else => |e| return e, | ||
| 855 | }, | ||
| 856 | .Shared => false, | 852 | .Shared => false, |
| 857 | .Exclusive => true, | 853 | .Exclusive => true, |
| 858 | }; | 854 | }; |
| ... | @@ -861,19 +857,137 @@ pub const File = struct { | ... | @@ -861,19 +857,137 @@ pub const File = struct { |
| 861 | null, | 857 | null, |
| 862 | null, | 858 | null, |
| 863 | null, | 859 | null, |
| 860 | &io_status_block, | ||
| 861 | &range_off, | ||
| 862 | &range_len, | ||
| 864 | null, | 863 | null, |
| 864 | windows.FALSE, // non-blocking=false | ||
| 865 | @boolToInt(exclusive), | ||
| 866 | ) catch |err| switch (err) { | ||
| 867 | error.WouldBlock => unreachable, // non-blocking=false | ||
| 868 | else => |e| return e, | ||
| 869 | }; | ||
| 870 | } else { | ||
| 871 | return os.flock(file.handle, switch (l) { | ||
| 872 | .None => os.LOCK_UN, | ||
| 873 | .Shared => os.LOCK_SH, | ||
| 874 | .Exclusive => os.LOCK_EX, | ||
| 875 | }) catch |err| switch (err) { | ||
| 876 | error.WouldBlock => unreachable, // non-blocking=false | ||
| 877 | else => |e| return e, | ||
| 878 | }; | ||
| 879 | } | ||
| 880 | } | ||
| 881 | |||
| 882 | /// Assumes the file is locked. | ||
| 883 | pub fn unlock(file: File) void { | ||
| 884 | if (is_windows) { | ||
| 885 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | ||
| 886 | return windows.UnlockFile( | ||
| 887 | file.handle, | ||
| 888 | &io_status_block, | ||
| 865 | &range_off, | 889 | &range_off, |
| 866 | &range_len, | 890 | &range_len, |
| 867 | null, | 891 | null, |
| 868 | @boolToInt(non_blocking), | 892 | ) catch |err| switch (err) { |
| 893 | error.RangeNotLocked => unreachable, // Function assumes unlocked. | ||
| 894 | error.Unexpected => unreachable, // Resource deallocation must succeed. | ||
| 895 | }; | ||
| 896 | } else { | ||
| 897 | return os.flock(file.handle, os.LOCK_UN) catch |err| switch (err) { | ||
| 898 | error.WouldBlock => unreachable, // unlocking can't block | ||
| 899 | error.SystemResources => unreachable, // We are deallocating resources. | ||
| 900 | error.Unexpected => unreachable, // Resource deallocation must succeed. | ||
| 901 | }; | ||
| 902 | } | ||
| 903 | } | ||
| 904 | |||
| 905 | /// Attempts to obtain a lock, returning `true` if the lock is | ||
| 906 | /// obtained, and `false` if there was an existing incompatible lock held. | ||
| 907 | /// A process may hold only one type of lock (shared or exclusive) on | ||
| 908 | /// a file. When a process terminates in any way, the lock is released. | ||
| 909 | /// | ||
| 910 | /// Assumes the file is unlocked. | ||
| 911 | /// | ||
| 912 | /// TODO: integrate with async I/O | ||
| 913 | pub fn tryLock(file: File, l: Lock) LockError!bool { | ||
| 914 | if (is_windows) { | ||
| 915 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | ||
| 916 | const exclusive = switch (l) { | ||
| 917 | .None => return, | ||
| 918 | .Shared => false, | ||
| 919 | .Exclusive => true, | ||
| 920 | }; | ||
| 921 | windows.LockFile( | ||
| 922 | file.handle, | ||
| 923 | null, | ||
| 924 | null, | ||
| 925 | null, | ||
| 926 | &io_status_block, | ||
| 927 | &range_off, | ||
| 928 | &range_len, | ||
| 929 | null, | ||
| 930 | windows.TRUE, // non-blocking=true | ||
| 869 | @boolToInt(exclusive), | 931 | @boolToInt(exclusive), |
| 870 | ); | 932 | ) catch |err| switch (err) { |
| 871 | } | 933 | error.WouldBlock => return false, |
| 872 | const non_blocking_flag = if (non_blocking) os.LOCK_NB else @as(i32, 0); | 934 | else => |e| return e, |
| 873 | return os.flock(file.handle, switch (lock) { | 935 | }; |
| 874 | .None => os.LOCK_UN, | 936 | } else { |
| 875 | .Shared => os.LOCK_SH | non_blocking_flag, | 937 | os.flock(file.handle, switch (l) { |
| 876 | .Exclusive => os.LOCK_EX | non_blocking_flag, | 938 | .None => os.LOCK_UN, |
| 877 | }); | 939 | .Shared => os.LOCK_SH | os.LOCK_NB, |
| 940 | .Exclusive => os.LOCK_EX | os.LOCK_NB, | ||
| 941 | }) catch |err| switch (err) { | ||
| 942 | error.WouldBlock => return false, | ||
| 943 | else => |e| return e, | ||
| 944 | }; | ||
| 945 | } | ||
| 946 | return true; | ||
| 947 | } | ||
| 948 | |||
| 949 | /// Assumes the file is already locked in exclusive mode. | ||
| 950 | /// Atomically modifies the lock to be in shared mode, without releasing it. | ||
| 951 | /// | ||
| 952 | /// TODO: integrate with async I/O | ||
| 953 | pub fn downgradeLock(file: File) LockError!void { | ||
| 954 | if (is_windows) { | ||
| 955 | // On Windows it works like a semaphore + exclusivity flag. To implement this | ||
| 956 | // function, we first obtain another lock in shared mode. This changes the | ||
| 957 | // exclusivity flag, but increments the semaphore to 2. So we follow up with | ||
| 958 | // an NtUnlockFile which decrements the semaphore but does not modify the | ||
| 959 | // exclusivity flag. | ||
| 960 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; | ||
| 961 | windows.LockFile( | ||
| 962 | file.handle, | ||
| 963 | null, | ||
| 964 | null, | ||
| 965 | null, | ||
| 966 | &io_status_block, | ||
| 967 | &range_off, | ||
| 968 | &range_len, | ||
| 969 | null, | ||
| 970 | windows.TRUE, // non-blocking=true | ||
| 971 | windows.FALSE, // exclusive=false | ||
| 972 | ) catch |err| switch (err) { | ||
| 973 | error.WouldBlock => unreachable, // File was not locked in exclusive mode. | ||
| 974 | else => |e| return e, | ||
| 975 | }; | ||
| 976 | return windows.UnlockFile( | ||
| 977 | file.handle, | ||
| 978 | &io_status_block, | ||
| 979 | &range_off, | ||
| 980 | &range_len, | ||
| 981 | null, | ||
| 982 | ) catch |err| switch (err) { | ||
| 983 | error.RangeNotLocked => unreachable, // File was not locked. | ||
| 984 | error.Unexpected => unreachable, // Resource deallocation must succeed. | ||
| 985 | }; | ||
| 986 | } else { | ||
| 987 | return os.flock(file.handle, os.LOCK_SH | os.LOCK_NB) catch |err| switch (err) { | ||
| 988 | error.WouldBlock => unreachable, // File was not locked in exclusive mode. | ||
| 989 | else => |e| return e, | ||
| 990 | }; | ||
| 991 | } | ||
| 878 | } | 992 | } |
| 879 | }; | 993 | }; |
lib/std/os/windows.zig+37-52| ... | @@ -49,7 +49,6 @@ pub const OpenFileOptions = struct { | ... | @@ -49,7 +49,6 @@ pub const OpenFileOptions = struct { |
| 49 | dir: ?HANDLE = null, | 49 | dir: ?HANDLE = null, |
| 50 | sa: ?*SECURITY_ATTRIBUTES = null, | 50 | sa: ?*SECURITY_ATTRIBUTES = null, |
| 51 | share_access: ULONG = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, | 51 | share_access: ULONG = FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, |
| 52 | share_access_nonblocking: bool = false, | ||
| 53 | creation: ULONG, | 52 | creation: ULONG, |
| 54 | io_mode: std.io.ModeOverride, | 53 | io_mode: std.io.ModeOverride, |
| 55 | /// If true, tries to open path as a directory. | 54 | /// If true, tries to open path as a directory. |
| ... | @@ -60,8 +59,6 @@ pub const OpenFileOptions = struct { | ... | @@ -60,8 +59,6 @@ pub const OpenFileOptions = struct { |
| 60 | follow_symlinks: bool = true, | 59 | follow_symlinks: bool = true, |
| 61 | }; | 60 | }; |
| 62 | 61 | ||
| 63 | /// TODO when share_access_nonblocking is false, this implementation uses | ||
| 64 | /// untinterruptible sleep() to block. This is not the final iteration of the API. | ||
| 65 | pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE { | 62 | pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE { |
| 66 | if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.open_dir) { | 63 | if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.open_dir) { |
| 67 | return error.IsDir; | 64 | return error.IsDir; |
| ... | @@ -94,53 +91,39 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN | ... | @@ -94,53 +91,39 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 94 | // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT. | 91 | // If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT. |
| 95 | const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT; | 92 | const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT; |
| 96 | 93 | ||
| 97 | var delay: usize = 1; | 94 | const rc = ntdll.NtCreateFile( |
| 98 | while (true) { | 95 | &result, |
| 99 | const rc = ntdll.NtCreateFile( | 96 | options.access_mask, |
| 100 | &result, | 97 | &attr, |
| 101 | options.access_mask, | 98 | &io, |
| 102 | &attr, | 99 | null, |
| 103 | &io, | 100 | FILE_ATTRIBUTE_NORMAL, |
| 104 | null, | 101 | options.share_access, |
| 105 | FILE_ATTRIBUTE_NORMAL, | 102 | options.creation, |
| 106 | options.share_access, | 103 | flags, |
| 107 | options.creation, | 104 | null, |
| 108 | flags, | 105 | 0, |
| 109 | null, | 106 | ); |
| 110 | 0, | 107 | switch (rc) { |
| 111 | ); | 108 | .SUCCESS => { |
| 112 | switch (rc) { | 109 | if (std.io.is_async and options.io_mode == .evented) { |
| 113 | .SUCCESS => { | 110 | _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined; |
| 114 | if (std.io.is_async and options.io_mode == .evented) { | 111 | } |
| 115 | _ = CreateIoCompletionPort(result, std.event.Loop.instance.?.os_data.io_port, undefined, undefined) catch undefined; | 112 | return result; |
| 116 | } | 113 | }, |
| 117 | return result; | 114 | .OBJECT_NAME_INVALID => unreachable, |
| 118 | }, | 115 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, |
| 119 | .OBJECT_NAME_INVALID => unreachable, | 116 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, |
| 120 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, | 117 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, |
| 121 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, | 118 | .INVALID_PARAMETER => unreachable, |
| 122 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, | 119 | .SHARING_VIOLATION => return error.AccessDenied, |
| 123 | .INVALID_PARAMETER => unreachable, | 120 | .ACCESS_DENIED => return error.AccessDenied, |
| 124 | .SHARING_VIOLATION => { | 121 | .PIPE_BUSY => return error.PipeBusy, |
| 125 | if (options.share_access_nonblocking) { | 122 | .OBJECT_PATH_SYNTAX_BAD => unreachable, |
| 126 | return error.WouldBlock; | 123 | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, |
| 127 | } | 124 | .FILE_IS_A_DIRECTORY => return error.IsDir, |
| 128 | // TODO sleep in a way that is interruptable | 125 | .NOT_A_DIRECTORY => return error.NotDir, |
| 129 | // TODO integrate with async I/O | 126 | else => return unexpectedStatus(rc), |
| 130 | std.time.sleep(delay); | ||
| 131 | if (delay < 1 * std.time.ns_per_s) { | ||
| 132 | delay *= 2; | ||
| 133 | } | ||
| 134 | continue; | ||
| 135 | }, | ||
| 136 | .ACCESS_DENIED => return error.AccessDenied, | ||
| 137 | .PIPE_BUSY => return error.PipeBusy, | ||
| 138 | .OBJECT_PATH_SYNTAX_BAD => unreachable, | ||
| 139 | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, | ||
| 140 | .FILE_IS_A_DIRECTORY => return error.IsDir, | ||
| 141 | .NOT_A_DIRECTORY => return error.NotDir, | ||
| 142 | else => return unexpectedStatus(rc), | ||
| 143 | } | ||
| 144 | } | 127 | } |
| 145 | } | 128 | } |
| 146 | 129 | ||
| ... | @@ -1689,7 +1672,7 @@ pub fn LockFile( | ... | @@ -1689,7 +1672,7 @@ pub fn LockFile( |
| 1689 | Event: ?HANDLE, | 1672 | Event: ?HANDLE, |
| 1690 | ApcRoutine: ?*IO_APC_ROUTINE, | 1673 | ApcRoutine: ?*IO_APC_ROUTINE, |
| 1691 | ApcContext: ?*c_void, | 1674 | ApcContext: ?*c_void, |
| 1692 | IoStatusBlock: ?*IO_STATUS_BLOCK, | 1675 | IoStatusBlock: *IO_STATUS_BLOCK, |
| 1693 | ByteOffset: *const LARGE_INTEGER, | 1676 | ByteOffset: *const LARGE_INTEGER, |
| 1694 | Length: *const LARGE_INTEGER, | 1677 | Length: *const LARGE_INTEGER, |
| 1695 | Key: ?*ULONG, | 1678 | Key: ?*ULONG, |
| ... | @@ -1712,6 +1695,7 @@ pub fn LockFile( | ... | @@ -1712,6 +1695,7 @@ pub fn LockFile( |
| 1712 | .SUCCESS => return, | 1695 | .SUCCESS => return, |
| 1713 | .INSUFFICIENT_RESOURCES => return error.SystemResources, | 1696 | .INSUFFICIENT_RESOURCES => return error.SystemResources, |
| 1714 | .LOCK_NOT_GRANTED => return error.WouldBlock, | 1697 | .LOCK_NOT_GRANTED => return error.WouldBlock, |
| 1698 | .ACCESS_VIOLATION => unreachable, // bad io_status_block pointer | ||
| 1715 | else => return unexpectedStatus(rc), | 1699 | else => return unexpectedStatus(rc), |
| 1716 | } | 1700 | } |
| 1717 | } | 1701 | } |
| ... | @@ -1722,7 +1706,7 @@ pub const UnlockFileError = error{ | ... | @@ -1722,7 +1706,7 @@ pub const UnlockFileError = error{ |
| 1722 | 1706 | ||
| 1723 | pub fn UnlockFile( | 1707 | pub fn UnlockFile( |
| 1724 | FileHandle: HANDLE, | 1708 | FileHandle: HANDLE, |
| 1725 | IoStatusBlock: ?*IO_STATUS_BLOCK, | 1709 | IoStatusBlock: *IO_STATUS_BLOCK, |
| 1726 | ByteOffset: *const LARGE_INTEGER, | 1710 | ByteOffset: *const LARGE_INTEGER, |
| 1727 | Length: *const LARGE_INTEGER, | 1711 | Length: *const LARGE_INTEGER, |
| 1728 | Key: ?*ULONG, | 1712 | Key: ?*ULONG, |
| ... | @@ -1731,6 +1715,7 @@ pub fn UnlockFile( | ... | @@ -1731,6 +1715,7 @@ pub fn UnlockFile( |
| 1731 | switch (rc) { | 1715 | switch (rc) { |
| 1732 | .SUCCESS => return, | 1716 | .SUCCESS => return, |
| 1733 | .RANGE_NOT_LOCKED => return error.RangeNotLocked, | 1717 | .RANGE_NOT_LOCKED => return error.RangeNotLocked, |
| 1718 | .ACCESS_VIOLATION => unreachable, // bad io_status_block pointer | ||
| 1734 | else => return unexpectedStatus(rc), | 1719 | else => return unexpectedStatus(rc), |
| 1735 | } | 1720 | } |
| 1736 | } | 1721 | } |
lib/std/os/windows/ntdll.zig+2-2| ... | @@ -145,7 +145,7 @@ pub extern "NtDll" fn NtLockFile( | ... | @@ -145,7 +145,7 @@ pub extern "NtDll" fn NtLockFile( |
| 145 | Event: ?HANDLE, | 145 | Event: ?HANDLE, |
| 146 | ApcRoutine: ?*IO_APC_ROUTINE, | 146 | ApcRoutine: ?*IO_APC_ROUTINE, |
| 147 | ApcContext: ?*c_void, | 147 | ApcContext: ?*c_void, |
| 148 | IoStatusBlock: ?*IO_STATUS_BLOCK, | 148 | IoStatusBlock: *IO_STATUS_BLOCK, |
| 149 | ByteOffset: *const LARGE_INTEGER, | 149 | ByteOffset: *const LARGE_INTEGER, |
| 150 | Length: *const LARGE_INTEGER, | 150 | Length: *const LARGE_INTEGER, |
| 151 | Key: ?*ULONG, | 151 | Key: ?*ULONG, |
| ... | @@ -155,7 +155,7 @@ pub extern "NtDll" fn NtLockFile( | ... | @@ -155,7 +155,7 @@ pub extern "NtDll" fn NtLockFile( |
| 155 | 155 | ||
| 156 | pub extern "NtDll" fn NtUnlockFile( | 156 | pub extern "NtDll" fn NtUnlockFile( |
| 157 | FileHandle: HANDLE, | 157 | FileHandle: HANDLE, |
| 158 | IoStatusBlock: ?*IO_STATUS_BLOCK, | 158 | IoStatusBlock: *IO_STATUS_BLOCK, |
| 159 | ByteOffset: *const LARGE_INTEGER, | 159 | ByteOffset: *const LARGE_INTEGER, |
| 160 | Length: *const LARGE_INTEGER, | 160 | Length: *const LARGE_INTEGER, |
| 161 | Key: ?*ULONG, | 161 | Key: ?*ULONG, |
src/Cache.zig+5-3| ... | @@ -670,15 +670,17 @@ pub const Manifest = struct { | ... | @@ -670,15 +670,17 @@ pub const Manifest = struct { |
| 670 | fn downgradeToSharedLock(self: *Manifest) !void { | 670 | fn downgradeToSharedLock(self: *Manifest) !void { |
| 671 | if (!self.have_exclusive_lock) return; | 671 | if (!self.have_exclusive_lock) return; |
| 672 | const manifest_file = self.manifest_file.?; | 672 | const manifest_file = self.manifest_file.?; |
| 673 | try manifest_file.setLock(.Shared, false); | 673 | try manifest_file.downgradeLock(); |
| 674 | self.have_exclusive_lock = false; | 674 | self.have_exclusive_lock = false; |
| 675 | } | 675 | } |
| 676 | 676 | ||
| 677 | fn upgradeToExclusiveLock(self: *Manifest) !void { | 677 | fn upgradeToExclusiveLock(self: *Manifest) !void { |
| 678 | if (self.have_exclusive_lock) return; | 678 | if (self.have_exclusive_lock) return; |
| 679 | const manifest_file = self.manifest_file.?; | 679 | const manifest_file = self.manifest_file.?; |
| 680 | try manifest_file.setLock(.None, false); | 680 | // Here we intentionally have a period where the lock is released, in case there are |
| 681 | try manifest_file.setLock(.Exclusive, false); | 681 | // other processes holding a shared lock. |
| 682 | manifest_file.unlock(); | ||
| 683 | try manifest_file.lock(.Exclusive); | ||
| 682 | self.have_exclusive_lock = true; | 684 | self.have_exclusive_lock = true; |
| 683 | } | 685 | } |
| 684 | 686 |