authorgravatar for leroycepearson@geemili.xyzLeRoyce Pearson <leroycepearson@geemili.xyz> 2020-04-07 16:23:12-06:00
committergravatar for leroycepearson@geemili.xyzLeRoyce Pearson <leroycepearson@geemili.xyz> 2020-04-07 16:49:30-06:00
log71c5aab3e740c131e63766be57de19b3dd451972
tree56761bbf7ed18991e831e70aed7cc6c924d05d6d
parent28d71c97d1874abdeb782a82f655d250a77a2f69

Make lock option an enum

For some reason, this breaks file locking on windows. Not sure if this is a problem with wine.

2 files changed, 67 insertions(+), 39 deletions(-)

lib/std/fs.zig+45-30
...@@ -596,13 +596,12 @@ pub const Dir = struct {...@@ -596,13 +596,12 @@ pub const Dir = struct {
596 }596 }
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") and @hasDecl(os, "O_SHLOCK");599 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK");
600 const lock_flag: u32 = lock_flag: {600 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
601 if (has_flock_open_flags and flags.lock) {601 .None => @as(u32, 0),
602 break :lock_flag if (flags.write) @as(u32, os.O_EXLOCK) else @as(u32, os.O_SHLOCK);602 .Shared => os.O_SHLOCK,
603 }603 .Exclusive => os.O_EXLOCK,
604 break :lock_flag @as(u32, 0);604 } else 0;
605 };
606605
607 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;606 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
608 const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read)607 const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read)
...@@ -616,9 +615,13 @@ pub const Dir = struct {...@@ -616,9 +615,13 @@ pub const Dir = struct {
616 else615 else
617 try os.openatZ(self.fd, sub_path, os_flags, 0);616 try os.openatZ(self.fd, sub_path, os_flags, 0);
618617
619 if (!has_flock_open_flags and flags.lock) {618 if (!has_flock_open_flags and flags.lock != .None) {
620 // TODO: integrate async I/O619 // TODO: integrate async I/O
621 try os.flock(fd, if (flags.write) os.LOCK_EX else os.LOCK_SH);620 try os.flock(fd, switch (flags.lock) {
621 .None => unreachable,
622 .Shared => os.LOCK_SH,
623 .Exclusive => os.LOCK_EX,
624 });
622 }625 }
623626
624 return File{627 return File{
...@@ -638,11 +641,13 @@ pub const Dir = struct {...@@ -638,11 +641,13 @@ pub const Dir = struct {
638 const access_mask = w.SYNCHRONIZE |641 const access_mask = w.SYNCHRONIZE |
639 (if (flags.read) @as(u32, w.GENERIC_READ) else 0) |642 (if (flags.read) @as(u32, w.GENERIC_READ) else 0) |
640 (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0);643 (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0);
641 const share_access = if (flags.lock)644
642 w.FILE_SHARE_DELETE |645 const share_access = switch (flags.lock) {
643 (if (flags.write) @as(os.windows.ULONG, 0) else w.FILE_SHARE_READ)646 .None => @as(?w.ULONG, null),
644 else647 .Shared => w.FILE_SHARE_READ,
645 null;648 .Exclusive => @as(?w.ULONG, 0),
649 };
650
646 return @as(File, .{651 return @as(File, .{
647 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, w.FILE_OPEN),652 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, w.FILE_OPEN),
648 .io_mode = .blocking,653 .io_mode = .blocking,
...@@ -672,7 +677,11 @@ pub const Dir = struct {...@@ -672,7 +677,11 @@ pub const Dir = struct {
672677
673 // Use the O_ locking flags if the os supports them678 // Use the O_ locking flags if the os supports them
674 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK");679 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK");
675 const lock_flag: u32 = if (has_flock_open_flags and flags.lock) os.O_EXLOCK else 0;680 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
681 .None => @as(u32, 0),
682 .Shared => os.O_SHLOCK,
683 .Exclusive => os.O_EXLOCK,
684 } else 0;
676685
677 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;686 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
678 const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC |687 const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC |
...@@ -684,9 +693,13 @@ pub const Dir = struct {...@@ -684,9 +693,13 @@ pub const Dir = struct {
684 else693 else
685 try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode);694 try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode);
686695
687 if (!has_flock_open_flags and flags.lock) {696 if (!has_flock_open_flags and flags.lock != .None) {
688 // TODO: integrate async I/O697 // TODO: integrate async I/O
689 try os.flock(fd, os.LOCK_EX);698 try os.flock(fd, switch (flags.lock) {
699 .None => unreachable,
700 .Shared => os.LOCK_SH,
701 .Exclusive => os.LOCK_EX,
702 });
690 }703 }
691704
692 return File{ .handle = fd, .io_mode = .blocking };705 return File{ .handle = fd, .io_mode = .blocking };
...@@ -705,10 +718,12 @@ pub const Dir = struct {...@@ -705,10 +718,12 @@ pub const Dir = struct {
705 else718 else
706 @as(u32, w.FILE_OPEN_IF);719 @as(u32, w.FILE_OPEN_IF);
707720
708 const share_access = if (flags.lock)721 const share_access = switch (flags.lock) {
709 @as(os.windows.ULONG, w.FILE_SHARE_DELETE)722 .None => @as(?w.ULONG, null),
710 else723 .Shared => w.FILE_SHARE_READ,
711 null;724 .Exclusive => @as(?w.ULONG, 0),
725 };
726
712 return @as(File, .{727 return @as(File, .{
713 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, creation),728 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, creation),
714 .io_mode = .blocking,729 .io_mode = .blocking,
...@@ -1671,8 +1686,8 @@ test "open file with lock twice, make sure it wasn't open at the same time" {...@@ -1671,8 +1686,8 @@ test "open file with lock twice, make sure it wasn't open at the same time" {
1671 const filename = "file_lock_test.txt";1686 const filename = "file_lock_test.txt";
16721687
1673 var contexts = [_]FileLockTestContext{1688 var contexts = [_]FileLockTestContext{
1674 .{ .filename = filename, .create = true, .exclusive = true },1689 .{ .filename = filename, .create = true, .lock = .Exclusive },
1675 .{ .filename = filename, .create = true, .exclusive = true },1690 .{ .filename = filename, .create = true, .lock = .Exclusive },
1676 };1691 };
1677 try run_lock_file_test(&contexts);1692 try run_lock_file_test(&contexts);
16781693
...@@ -1703,9 +1718,9 @@ test "create file, lock and read from multiple process at once" {...@@ -1703,9 +1718,9 @@ test "create file, lock and read from multiple process at once" {
1703 try std.fs.cwd().writeFile(filename, filedata);1718 try std.fs.cwd().writeFile(filename, filedata);
17041719
1705 var contexts = [_]FileLockTestContext{1720 var contexts = [_]FileLockTestContext{
1706 .{ .filename = filename, .create = false, .exclusive = false },1721 .{ .filename = filename, .create = false, .lock = .Shared },
1707 .{ .filename = filename, .create = false, .exclusive = false },1722 .{ .filename = filename, .create = false, .lock = .Shared },
1708 .{ .filename = filename, .create = false, .exclusive = true },1723 .{ .filename = filename, .create = false, .lock = .Exclusive },
1709 };1724 };
17101725
1711 try run_lock_file_test(&contexts);1726 try run_lock_file_test(&contexts);
...@@ -1740,8 +1755,8 @@ const FileLockTestContext = struct {...@@ -1740,8 +1755,8 @@ const FileLockTestContext = struct {
17401755
1741 // use file.createFile1756 // use file.createFile
1742 create: bool,1757 create: bool,
1743 // get a read/write lock, instead of just a read lock1758 // the type of lock to use
1744 exclusive: bool,1759 lock: File.Lock,
17451760
1746 // Output variables1761 // Output variables
1747 err: ?(File.OpenError || std.os.ReadError) = null,1762 err: ?(File.OpenError || std.os.ReadError) = null,
...@@ -1756,12 +1771,12 @@ const FileLockTestContext = struct {...@@ -1756,12 +1771,12 @@ const FileLockTestContext = struct {
1756 fn run(ctx: *@This()) void {1771 fn run(ctx: *@This()) void {
1757 var file: File = undefined;1772 var file: File = undefined;
1758 if (ctx.create) {1773 if (ctx.create) {
1759 file = cwd().createFile(ctx.filename, .{ .lock = true }) catch |err| {1774 file = cwd().createFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
1760 ctx.err = err;1775 ctx.err = err;
1761 return;1776 return;
1762 };1777 };
1763 } else {1778 } else {
1764 file = cwd().openFile(ctx.filename, .{ .lock = true, .write = ctx.exclusive }) catch |err| {1779 file = cwd().openFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
1765 ctx.err = err;1780 ctx.err = err;
1766 return;1781 return;
1767 };1782 };
lib/std/fs/file.zig+22-9
...@@ -36,22 +36,29 @@ pub const File = struct {...@@ -36,22 +36,29 @@ pub const File = struct {
3636
37 pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError;37 pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError;
3838
39 pub const Lock = enum {
40 None, Shared, Exclusive
41 };
42
39 /// TODO https://github.com/ziglang/zig/issues/380243 /// TODO https://github.com/ziglang/zig/issues/3802
40 pub const OpenFlags = struct {44 pub const OpenFlags = struct {
41 read: bool = true,45 read: bool = true,
42 write: bool = false,46 write: bool = false,
4347
44 /// Open the file with exclusive access. If `write` is true, then the file is opened with an48 /// Open the file with a lock to prevent other processes from accessing it at the
45 /// exclusive lock, meaning that no other processes can read or write to the file. Otherwise49 /// same time. An exclusive lock will prevent other processes from acquiring a lock.
46 /// the file is opened with a shared lock, allowing the other processes to read from the50 /// A shared lock will prevent other processes from acquiring a exclusive lock, but
47 /// file, but not to write to the file.51 /// doesn't prevent other process from getting their own shared locks.
48 ///52 ///
49 /// Note that the lock is only advisory on Linux, except in very specific cirsumstances[1].53 /// Note that the lock is only advisory on Linux, except in very specific cirsumstances[1].
50 /// This means that a process that does not respect the locking API can still read and write54 /// This means that a process that does not respect the locking API can still get access
51 /// to the file, despite the lock.55 /// to the file, despite the lock.
52 ///56 ///
57 /// Windows' file locks are mandatory, and any process attempting to access the file will
58 /// receive an error.
59 ///
53 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt60 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
54 lock: bool = false,61 lock: Lock = .None,
5562
56 /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`.63 /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`.
57 /// It allows the use of `noasync` when calling functions related to opening64 /// It allows the use of `noasync` when calling functions related to opening
...@@ -72,14 +79,20 @@ pub const File = struct {...@@ -72,14 +79,20 @@ pub const File = struct {
72 /// `error.FileAlreadyExists` to be returned.79 /// `error.FileAlreadyExists` to be returned.
73 exclusive: bool = false,80 exclusive: bool = false,
7481
75 /// Prevent other files from accessing this file while this process has it is open.82 /// Open the file with a lock to prevent other processes from accessing it at the
83 /// same time. An exclusive lock will prevent other processes from acquiring a lock.
84 /// A shared lock will prevent other processes from acquiring a exclusive lock, but
85 /// doesn't prevent other process from getting their own shared locks.
76 ///86 ///
77 /// Note that the lock is only advisory on Linux, except in very specific cirsumstances[1].87 /// Note that the lock is only advisory on Linux, except in very specific cirsumstances[1].
78 /// This means that a process that does not respect the locking API can still read and write88 /// This means that a process that does not respect the locking API can still get access
79 /// to the file, despite the lock.89 /// to the file, despite the lock.
80 ///90 ///
91 /// Windows' file locks are mandatory, and any process attempting to access the file will
92 /// receive an error.
93 ///
81 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt94 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
82 lock: bool = false,95 lock: Lock = .None,
8396
84 /// For POSIX systems this is the file system mode the file will97 /// For POSIX systems this is the file system mode the file will
85 /// be created with.98 /// be created with.