diff options
| author | LeRoyce Pearson <leroycepearson@geemili.xyz> | 2020-04-07 18:00:12 -0600 |
|---|---|---|
| committer | LeRoyce Pearson <leroycepearson@geemili.xyz> | 2020-04-07 18:00:12 -0600 |
| commit | 317f06dc7741dcb5579381fc4ad26dd23346eb67 (patch) | |
| tree | 02a713e7a809c81d01b39032ab35703d75ad6705 /lib/std | |
| parent | 117d15ed7a230d065cf88faa8156c08f7160e08b (diff) | |
| download | zig-317f06dc7741dcb5579381fc4ad26dd23346eb67.tar.gz zig-317f06dc7741dcb5579381fc4ad26dd23346eb67.zip | |
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.
Diffstat (limited to 'lib/std')
| -rw-r--r-- | lib/std/fs.zig | 43 | ||||
| -rw-r--r-- | lib/std/fs/file.zig | 10 | ||||
| -rw-r--r-- | lib/std/os/windows.zig | 5 |
3 files changed, 46 insertions, 12 deletions
diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 203dda1c40..f965ac2453 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -597,10 +597,11 @@ pub const Dir = struct { // Use the O_ locking flags if the os supports them const has_flock_open_flags = @hasDecl(os, "O_EXLOCK"); + const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0); const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) { .None => @as(u32, 0), - .Shared => os.O_SHLOCK, - .Exclusive => os.O_EXLOCK, + .Shared => os.O_SHLOCK | nonblocking_lock_flag, + .Exclusive => os.O_EXLOCK | nonblocking_lock_flag, } else 0; const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; @@ -617,10 +618,11 @@ pub const Dir = struct { if (!has_flock_open_flags and flags.lock != .None) { // TODO: integrate async I/O + const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0); try os.flock(fd, switch (flags.lock) { .None => unreachable, - .Shared => os.LOCK_SH, - .Exclusive => os.LOCK_EX, + .Shared => os.LOCK_SH | lock_nonblocking, + .Exclusive => os.LOCK_EX | lock_nonblocking, }); } @@ -644,12 +646,12 @@ pub const Dir = struct { const share_access = switch (flags.lock) { .None => @as(?w.ULONG, null), - .Shared => w.FILE_SHARE_READ, - .Exclusive => @as(?w.ULONG, 0), + .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, + .Exclusive => w.FILE_SHARE_DELETE, }; return @as(File, .{ - .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, w.FILE_OPEN), + .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, flags.lock_nonblocking, w.FILE_OPEN), .io_mode = .blocking, }); } @@ -677,6 +679,7 @@ pub const Dir = struct { // Use the O_ locking flags if the os supports them const has_flock_open_flags = @hasDecl(os, "O_EXLOCK"); + const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0); const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) { .None => @as(u32, 0), .Shared => os.O_SHLOCK, @@ -695,10 +698,11 @@ pub const Dir = struct { if (!has_flock_open_flags and flags.lock != .None) { // TODO: integrate async I/O + const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0); try os.flock(fd, switch (flags.lock) { .None => unreachable, - .Shared => os.LOCK_SH, - .Exclusive => os.LOCK_EX, + .Shared => os.LOCK_SH | lock_nonblocking, + .Exclusive => os.LOCK_EX | lock_nonblocking, }); } @@ -720,12 +724,12 @@ pub const Dir = struct { const share_access = switch (flags.lock) { .None => @as(?w.ULONG, null), - .Shared => w.FILE_SHARE_READ, - .Exclusive => @as(?w.ULONG, 0), + .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, + .Exclusive => w.FILE_SHARE_DELETE, }; return @as(File, .{ - .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, creation), + .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, flags.lock_nonblocking, creation), .io_mode = .blocking, }); } @@ -1680,6 +1684,21 @@ test "" { const FILE_LOCK_TEST_SLEEP_TIME = 1 * std.time.ns_per_s; +test "open file with exclusive nonblocking lock twice" { + const dir = cwd(); + const filename = "file_nonblocking_lock_test.txt"; + + const file1 = try dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); + + const file2 = dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); + std.debug.assert(std.meta.eql(file2, error.WouldBlock)); + + dir.deleteFile(filename) catch |err| switch (err) { + error.FileNotFound => {}, + else => return err, + }; +} + test "open file with lock twice, make sure it wasn't open at the same time" { if (builtin.single_threaded) return; diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 32eb1460c4..2a6ad875c5 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -60,6 +60,11 @@ pub const File = struct { /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt lock: Lock = .None, + /// Sets whether or not to wait until the file is locked to return. If set to true, + /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file + /// is available to proceed. + lock_nonblocking: bool = false, + /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`. /// It allows the use of `noasync` when calling functions related to opening /// the file, reading, and writing. @@ -94,6 +99,11 @@ pub const File = struct { /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt lock: Lock = .None, + /// Sets whether or not to wait until the file is locked to return. If set to true, + /// `error.WouldBlock` will be returned. Otherwise, the file will wait until the file + /// is available to proceed. + lock_nonblocking: bool = false, + /// For POSIX systems this is the file system mode the file will /// be created with. mode: Mode = default_mode, diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 817244e089..72593b92c7 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -98,6 +98,7 @@ pub const OpenError = error{ PathAlreadyExists, Unexpected, NameTooLong, + WouldBlock, }; /// TODO rename to CreateFileW @@ -108,6 +109,7 @@ pub fn OpenFileW( sa: ?*SECURITY_ATTRIBUTES, access_mask: ACCESS_MASK, share_access_opt: ?ULONG, + share_access_nonblocking: bool, creation: ULONG, ) OpenError!HANDLE { if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { @@ -161,6 +163,9 @@ pub fn OpenFileW( .NO_MEDIA_IN_DEVICE => return error.NoDevice, .INVALID_PARAMETER => unreachable, .SHARING_VIOLATION => { + if (share_access_nonblocking) { + return error.WouldBlock; + } std.time.sleep(delay); if (delay < 1 * std.time.ns_per_s) { delay *= 2; |
