authorgravatar for leroycepearson@geemili.xyzLeRoyce Pearson <leroycepearson@geemili.xyz> 2020-04-07 18:00:12-06:00
committergravatar for leroycepearson@geemili.xyzLeRoyce Pearson <leroycepearson@geemili.xyz> 2020-04-07 18:00:12-06:00
log317f06dc7741dcb5579381fc4ad26dd23346eb67
tree02a713e7a809c81d01b39032ab35703d75ad6705
parent117d15ed7a230d065cf88faa8156c08f7160e08b

Add lock_nonblocking flag for creating or opening files

Also, make windows share delete access. Rationale: this is how it works on Unix systems, mostly because locks are (usually) advisory on Unix.

3 files changed, 46 insertions(+), 12 deletions(-)

lib/std/fs.zig+31-12
...@@ -597,10 +597,11 @@ pub const Dir = struct {...@@ -597,10 +597,11 @@ pub const Dir = struct {
597597
598 // Use the O_ locking flags if the os supports them598 // Use the O_ locking flags if the os supports them
599 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK");599 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK");
600 const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0);
600 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {601 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
601 .None => @as(u32, 0),602 .None => @as(u32, 0),
602 .Shared => os.O_SHLOCK,603 .Shared => os.O_SHLOCK | nonblocking_lock_flag,
603 .Exclusive => os.O_EXLOCK,604 .Exclusive => os.O_EXLOCK | nonblocking_lock_flag,
604 } else 0;605 } else 0;
605606
606 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;607 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
...@@ -617,10 +618,11 @@ pub const Dir = struct {...@@ -617,10 +618,11 @@ pub const Dir = struct {
617618
618 if (!has_flock_open_flags and flags.lock != .None) {619 if (!has_flock_open_flags and flags.lock != .None) {
619 // TODO: integrate async I/O620 // TODO: integrate async I/O
621 const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0);
620 try os.flock(fd, switch (flags.lock) {622 try os.flock(fd, switch (flags.lock) {
621 .None => unreachable,623 .None => unreachable,
622 .Shared => os.LOCK_SH,624 .Shared => os.LOCK_SH | lock_nonblocking,
623 .Exclusive => os.LOCK_EX,625 .Exclusive => os.LOCK_EX | lock_nonblocking,
624 });626 });
625 }627 }
626628
...@@ -644,12 +646,12 @@ pub const Dir = struct {...@@ -644,12 +646,12 @@ pub const Dir = struct {
644646
645 const share_access = switch (flags.lock) {647 const share_access = switch (flags.lock) {
646 .None => @as(?w.ULONG, null),648 .None => @as(?w.ULONG, null),
647 .Shared => w.FILE_SHARE_READ,649 .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
648 .Exclusive => @as(?w.ULONG, 0),650 .Exclusive => w.FILE_SHARE_DELETE,
649 };651 };
650652
651 return @as(File, .{653 return @as(File, .{
652 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, w.FILE_OPEN),654 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, flags.lock_nonblocking, w.FILE_OPEN),
653 .io_mode = .blocking,655 .io_mode = .blocking,
654 });656 });
655 }657 }
...@@ -677,6 +679,7 @@ pub const Dir = struct {...@@ -677,6 +679,7 @@ pub const Dir = struct {
677679
678 // Use the O_ locking flags if the os supports them680 // Use the O_ locking flags if the os supports them
679 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK");681 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK");
682 const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0);
680 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {683 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
681 .None => @as(u32, 0),684 .None => @as(u32, 0),
682 .Shared => os.O_SHLOCK,685 .Shared => os.O_SHLOCK,
...@@ -695,10 +698,11 @@ pub const Dir = struct {...@@ -695,10 +698,11 @@ pub const Dir = struct {
695698
696 if (!has_flock_open_flags and flags.lock != .None) {699 if (!has_flock_open_flags and flags.lock != .None) {
697 // TODO: integrate async I/O700 // TODO: integrate async I/O
701 const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0);
698 try os.flock(fd, switch (flags.lock) {702 try os.flock(fd, switch (flags.lock) {
699 .None => unreachable,703 .None => unreachable,
700 .Shared => os.LOCK_SH,704 .Shared => os.LOCK_SH | lock_nonblocking,
701 .Exclusive => os.LOCK_EX,705 .Exclusive => os.LOCK_EX | lock_nonblocking,
702 });706 });
703 }707 }
704708
...@@ -720,12 +724,12 @@ pub const Dir = struct {...@@ -720,12 +724,12 @@ pub const Dir = struct {
720724
721 const share_access = switch (flags.lock) {725 const share_access = switch (flags.lock) {
722 .None => @as(?w.ULONG, null),726 .None => @as(?w.ULONG, null),
723 .Shared => w.FILE_SHARE_READ,727 .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
724 .Exclusive => @as(?w.ULONG, 0),728 .Exclusive => w.FILE_SHARE_DELETE,
725 };729 };
726730
727 return @as(File, .{731 return @as(File, .{
728 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, creation),732 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, flags.lock_nonblocking, creation),
729 .io_mode = .blocking,733 .io_mode = .blocking,
730 });734 });
731 }735 }
...@@ -1680,6 +1684,21 @@ test "" {...@@ -1680,6 +1684,21 @@ test "" {
16801684
1681const FILE_LOCK_TEST_SLEEP_TIME = 1 * std.time.ns_per_s;1685const FILE_LOCK_TEST_SLEEP_TIME = 1 * std.time.ns_per_s;
16821686
1687test "open file with exclusive nonblocking lock twice" {
1688 const dir = cwd();
1689 const filename = "file_nonblocking_lock_test.txt";
1690
1691 const file1 = try dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
1692
1693 const file2 = dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
1694 std.debug.assert(std.meta.eql(file2, error.WouldBlock));
1695
1696 dir.deleteFile(filename) catch |err| switch (err) {
1697 error.FileNotFound => {},
1698 else => return err,
1699 };
1700}
1701
1683test "open file with lock twice, make sure it wasn't open at the same time" {1702test "open file with lock twice, make sure it wasn't open at the same time" {
1684 if (builtin.single_threaded) return;1703 if (builtin.single_threaded) return;
16851704
lib/std/fs/file.zig+10
...@@ -60,6 +60,11 @@ pub const File = struct {...@@ -60,6 +60,11 @@ pub const File = struct {
60 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt60 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
61 lock: Lock = .None,61 lock: Lock = .None,
6262
63 /// Sets whether or not to wait until the file is locked to return. If set to true,
64 /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
65 /// is available to proceed.
66 lock_nonblocking: bool = false,
67
63 /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`.68 /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`.
64 /// It allows the use of `noasync` when calling functions related to opening69 /// It allows the use of `noasync` when calling functions related to opening
65 /// the file, reading, and writing.70 /// the file, reading, and writing.
...@@ -94,6 +99,11 @@ pub const File = struct {...@@ -94,6 +99,11 @@ pub const File = struct {
94 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt99 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
95 lock: Lock = .None,100 lock: Lock = .None,
96101
102 /// Sets whether or not to wait until the file is locked to return. If set to true,
103 /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file
104 /// is available to proceed.
105 lock_nonblocking: bool = false,
106
97 /// For POSIX systems this is the file system mode the file will107 /// For POSIX systems this is the file system mode the file will
98 /// be created with.108 /// be created with.
99 mode: Mode = default_mode,109 mode: Mode = default_mode,
lib/std/os/windows.zig+5
...@@ -98,6 +98,7 @@ pub const OpenError = error{...@@ -98,6 +98,7 @@ pub const OpenError = error{
98 PathAlreadyExists,98 PathAlreadyExists,
99 Unexpected,99 Unexpected,
100 NameTooLong,100 NameTooLong,
101 WouldBlock,
101};102};
102103
103/// TODO rename to CreateFileW104/// TODO rename to CreateFileW
...@@ -108,6 +109,7 @@ pub fn OpenFileW(...@@ -108,6 +109,7 @@ pub fn OpenFileW(
108 sa: ?*SECURITY_ATTRIBUTES,109 sa: ?*SECURITY_ATTRIBUTES,
109 access_mask: ACCESS_MASK,110 access_mask: ACCESS_MASK,
110 share_access_opt: ?ULONG,111 share_access_opt: ?ULONG,
112 share_access_nonblocking: bool,
111 creation: ULONG,113 creation: ULONG,
112) OpenError!HANDLE {114) OpenError!HANDLE {
113 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {115 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
...@@ -161,6 +163,9 @@ pub fn OpenFileW(...@@ -161,6 +163,9 @@ pub fn OpenFileW(
161 .NO_MEDIA_IN_DEVICE => return error.NoDevice,163 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
162 .INVALID_PARAMETER => unreachable,164 .INVALID_PARAMETER => unreachable,
163 .SHARING_VIOLATION => {165 .SHARING_VIOLATION => {
166 if (share_access_nonblocking) {
167 return error.WouldBlock;
168 }
164 std.time.sleep(delay);169 std.time.sleep(delay);
165 if (delay < 1 * std.time.ns_per_s) {170 if (delay < 1 * std.time.ns_per_s) {
166 delay *= 2;171 delay *= 2;