authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-28 16:46:37-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-06-29 14:25:04-07:00
log06129d7e3d5a8d9449edc98510a6d4f7a171b27f
tree9aa7bddedea56cb33ace3e7fed78d7e92b06472a
parent488f68069ba0a81f46a5afe721b908cfd3c61f76

std: implement a cross platform file locking abstraction

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 }
905920
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 }
10441074
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 }
832832
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;
834839
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) on841 /// 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/O846 /// 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};
6261
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.
65pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {62pub 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;
9693
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 interruptable125 .NOT_A_DIRECTORY => return error.NotDir,
129 // TODO integrate with async I/O126 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}
146129
...@@ -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{
17221706
1723pub fn UnlockFile(1707pub 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(
155155
156pub extern "NtDll" fn NtUnlockFile(156pub 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 }
676676
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 }
684686