authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-04-10 15:00:45-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-04-10 15:00:45-04:00
loga6e288d5fe51d5373fa995b5eec2dd2325c1ea9f
tree583c2fbf0a708c222e3cc7a493f93b0a2e9da16b
parent121307679bd1ffeaa8a290a826396662f26a4ac1
parent5951211d3fc348fc37a86abc9906acf4ee796883
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #4711 from leroycep/feature-file-locks

Add lock option to File.OpenFlags and File.CreateFlags

21 files changed, 526 insertions(+), 34 deletions(-)

lib/std/c.zig+1
......@@ -122,6 +122,7 @@ pub extern "c" fn sysctlnametomib(name: [*:0]const u8, mibp: ?*c_int, sizep: ?*u
122122pub extern "c" fn tcgetattr(fd: fd_t, termios_p: *termios) c_int;
123123pub extern "c" fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) c_int;
124124pub extern "c" fn fcntl(fd: fd_t, cmd: c_int, ...) c_int;
125pub extern "c" fn flock(fd: fd_t, operation: c_int) c_int;
125126pub extern "c" fn uname(buf: *utsname) c_int;
126127
127128pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int;
lib/std/child_process.zig+1
......@@ -357,6 +357,7 @@ pub const ChildProcess = struct {
357357 error.NoSpaceLeft => unreachable,
358358 error.FileTooBig => unreachable,
359359 error.DeviceBusy => unreachable,
360 error.FileLocksNotSupported => unreachable,
360361 else => |e| return e,
361362 }
362363 else
lib/std/fs.zig+219-5
......@@ -594,8 +594,19 @@ pub const Dir = struct {
594594 const path_w = try os.windows.cStrToPrefixedFileW(sub_path);
595595 return self.openFileW(&path_w, flags);
596596 }
597
598 // Use the O_ locking flags if the os supports them
599 // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag)
600 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !builtin.os.tag.isDarwin();
601 const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0);
602 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
603 .None => @as(u32, 0),
604 .Shared => os.O_SHLOCK | nonblocking_lock_flag,
605 .Exclusive => os.O_EXLOCK | nonblocking_lock_flag,
606 } else 0;
607
597608 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
598 const os_flags = O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read)
609 const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read)
599610 @as(u32, os.O_RDWR)
600611 else if (flags.write)
601612 @as(u32, os.O_WRONLY)
......@@ -605,6 +616,17 @@ pub const Dir = struct {
605616 try std.event.Loop.instance.?.openatZ(self.fd, sub_path, os_flags, 0)
606617 else
607618 try os.openatZ(self.fd, sub_path, os_flags, 0);
619
620 if (!has_flock_open_flags and flags.lock != .None) {
621 // TODO: integrate async I/O
622 const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0);
623 try os.flock(fd, switch (flags.lock) {
624 .None => unreachable,
625 .Shared => os.LOCK_SH | lock_nonblocking,
626 .Exclusive => os.LOCK_EX | lock_nonblocking,
627 });
628 }
629
608630 return File{
609631 .handle = fd,
610632 .io_mode = .blocking,
......@@ -622,8 +644,15 @@ pub const Dir = struct {
622644 const access_mask = w.SYNCHRONIZE |
623645 (if (flags.read) @as(u32, w.GENERIC_READ) else 0) |
624646 (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0);
647
648 const share_access = switch (flags.lock) {
649 .None => @as(?w.ULONG, null),
650 .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
651 .Exclusive => w.FILE_SHARE_DELETE,
652 };
653
625654 return @as(File, .{
626 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, w.FILE_OPEN),
655 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, flags.lock_nonblocking, w.FILE_OPEN),
627656 .io_mode = .blocking,
628657 });
629658 }
......@@ -648,8 +677,19 @@ pub const Dir = struct {
648677 const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
649678 return self.createFileW(&path_w, flags);
650679 }
680
681 // Use the O_ locking flags if the os supports them
682 // (Or if it's darwin, as darwin's `open` doesn't support the O_SYNC flag)
683 const has_flock_open_flags = @hasDecl(os, "O_EXLOCK") and !builtin.os.tag.isDarwin();
684 const nonblocking_lock_flag = if (has_flock_open_flags and flags.lock_nonblocking) (os.O_NONBLOCK | os.O_SYNC) else @as(u32, 0);
685 const lock_flag: u32 = if (has_flock_open_flags) switch (flags.lock) {
686 .None => @as(u32, 0),
687 .Shared => os.O_SHLOCK,
688 .Exclusive => os.O_EXLOCK,
689 } else 0;
690
651691 const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
652 const os_flags = O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC |
692 const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC |
653693 (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |
654694 (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |
655695 (if (flags.exclusive) @as(u32, os.O_EXCL) else 0);
......@@ -657,6 +697,17 @@ pub const Dir = struct {
657697 try std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, flags.mode)
658698 else
659699 try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode);
700
701 if (!has_flock_open_flags and flags.lock != .None) {
702 // TODO: integrate async I/O
703 const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK_NB else @as(i32, 0);
704 try os.flock(fd, switch (flags.lock) {
705 .None => unreachable,
706 .Shared => os.LOCK_SH | lock_nonblocking,
707 .Exclusive => os.LOCK_EX | lock_nonblocking,
708 });
709 }
710
660711 return File{ .handle = fd, .io_mode = .blocking };
661712 }
662713
......@@ -672,8 +723,15 @@ pub const Dir = struct {
672723 @as(u32, w.FILE_OVERWRITE_IF)
673724 else
674725 @as(u32, w.FILE_OPEN_IF);
726
727 const share_access = switch (flags.lock) {
728 .None => @as(?w.ULONG, null),
729 .Shared => w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
730 .Exclusive => w.FILE_SHARE_DELETE,
731 };
732
675733 return @as(File, .{
676 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, creation),
734 .handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, flags.lock_nonblocking, creation),
677735 .io_mode = .blocking,
678736 });
679737 }
......@@ -802,6 +860,7 @@ pub const Dir = struct {
802860 error.IsDir => unreachable, // we're providing O_DIRECTORY
803861 error.NoSpaceLeft => unreachable, // not providing O_CREAT
804862 error.PathAlreadyExists => unreachable, // not providing O_CREAT
863 error.FileLocksNotSupported => unreachable, // locking folders is not supported
805864 else => |e| return e,
806865 };
807866 return Dir{ .fd = fd };
......@@ -1508,7 +1567,7 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
15081567 return walker;
15091568}
15101569
1511pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError;
1570pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError || os.FlockError;
15121571
15131572pub fn openSelfExe() OpenSelfExeError!File {
15141573 if (builtin.os.tag == .linux) {
......@@ -1624,3 +1683,158 @@ test "" {
16241683 _ = @import("fs/get_app_data_dir.zig");
16251684 _ = @import("fs/watch.zig");
16261685}
1686
1687const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.millisecond;
1688
1689test "open file with exclusive nonblocking lock twice" {
1690 const dir = cwd();
1691 const filename = "file_nonblocking_lock_test.txt";
1692
1693 const file1 = try dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
1694 defer file1.close();
1695
1696 const file2 = dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
1697 std.debug.assert(std.meta.eql(file2, error.WouldBlock));
1698
1699 dir.deleteFile(filename) catch |err| switch (err) {
1700 error.FileNotFound => {},
1701 else => return err,
1702 };
1703}
1704
1705test "open file with lock twice, make sure it wasn't open at the same time" {
1706 if (builtin.single_threaded) return;
1707
1708 const filename = "file_lock_test.txt";
1709
1710 var contexts = [_]FileLockTestContext{
1711 .{ .filename = filename, .create = true, .lock = .Exclusive },
1712 .{ .filename = filename, .create = true, .lock = .Exclusive },
1713 };
1714 try run_lock_file_test(&contexts);
1715
1716 // Check for an error
1717 var was_error = false;
1718 for (contexts) |context, idx| {
1719 if (context.err) |err| {
1720 was_error = true;
1721 std.debug.warn("\nError in context {}: {}\n", .{ idx, err });
1722 }
1723 }
1724 if (was_error) builtin.panic("There was an error in contexts", null);
1725
1726 std.debug.assert(!contexts[0].overlaps(&contexts[1]));
1727
1728 cwd().deleteFile(filename) catch |err| switch (err) {
1729 error.FileNotFound => {},
1730 else => return err,
1731 };
1732}
1733
1734test "create file, lock and read from multiple process at once" {
1735 if (builtin.single_threaded) return;
1736
1737 const filename = "file_read_lock_test.txt";
1738 const filedata = "Hello, world!\n";
1739
1740 try std.fs.cwd().writeFile(filename, filedata);
1741
1742 var contexts = [_]FileLockTestContext{
1743 .{ .filename = filename, .create = false, .lock = .Shared },
1744 .{ .filename = filename, .create = false, .lock = .Shared },
1745 .{ .filename = filename, .create = false, .lock = .Exclusive },
1746 };
1747
1748 try run_lock_file_test(&contexts);
1749
1750 var was_error = false;
1751 for (contexts) |context, idx| {
1752 if (context.err) |err| {
1753 was_error = true;
1754 std.debug.warn("\nError in context {}: {}\n", .{ idx, err });
1755 }
1756 }
1757 if (was_error) builtin.panic("There was an error in contexts", null);
1758
1759 std.debug.assert(contexts[0].overlaps(&contexts[1]));
1760 std.debug.assert(!contexts[2].overlaps(&contexts[0]));
1761 std.debug.assert(!contexts[2].overlaps(&contexts[1]));
1762 if (contexts[0].bytes_read.? != filedata.len) {
1763 std.debug.warn("\n bytes_read: {}, expected: {} \n", .{ contexts[0].bytes_read, filedata.len });
1764 }
1765 std.debug.assert(contexts[0].bytes_read.? == filedata.len);
1766 std.debug.assert(contexts[1].bytes_read.? == filedata.len);
1767
1768 cwd().deleteFile(filename) catch |err| switch (err) {
1769 error.FileNotFound => {},
1770 else => return err,
1771 };
1772}
1773
1774const FileLockTestContext = struct {
1775 filename: []const u8,
1776 pid: if (builtin.os.tag == .windows) ?void else ?std.os.pid_t = null,
1777
1778 // use file.createFile
1779 create: bool,
1780 // the type of lock to use
1781 lock: File.Lock,
1782
1783 // Output variables
1784 err: ?(File.OpenError || std.os.ReadError) = null,
1785 start_time: u64 = 0,
1786 end_time: u64 = 0,
1787 bytes_read: ?usize = null,
1788
1789 fn overlaps(self: *const @This(), other: *const @This()) bool {
1790 return (self.start_time < other.end_time) and (self.end_time > other.start_time);
1791 }
1792
1793 fn run(ctx: *@This()) void {
1794 var file: File = undefined;
1795 if (ctx.create) {
1796 file = cwd().createFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
1797 ctx.err = err;
1798 return;
1799 };
1800 } else {
1801 file = cwd().openFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| {
1802 ctx.err = err;
1803 return;
1804 };
1805 }
1806 defer file.close();
1807
1808 ctx.start_time = std.time.milliTimestamp();
1809
1810 if (!ctx.create) {
1811 var buffer: [100]u8 = undefined;
1812 ctx.bytes_read = 0;
1813 while (true) {
1814 const amt = file.read(buffer[0..]) catch |err| {
1815 ctx.err = err;
1816 return;
1817 };
1818 if (amt == 0) break;
1819 ctx.bytes_read.? += amt;
1820 }
1821 }
1822
1823 std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME);
1824
1825 ctx.end_time = std.time.milliTimestamp();
1826 }
1827};
1828
1829fn run_lock_file_test(contexts: []FileLockTestContext) !void {
1830 var threads = std.ArrayList(*std.Thread).init(std.testing.allocator);
1831 defer {
1832 for (threads.toSlice()) |thread| {
1833 thread.wait();
1834 }
1835 threads.deinit();
1836 }
1837 for (contexts) |*ctx, idx| {
1838 try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run));
1839 }
1840}
lib/std/fs/file.zig+45-1
......@@ -34,13 +34,37 @@ pub const File = struct {
3434 else => 0o666,
3535 };
3636
37 pub const OpenError = windows.CreateFileError || os.OpenError;
37 pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError;
38
39 pub const Lock = enum {
40 None, Shared, Exclusive
41 };
3842
3943 /// TODO https://github.com/ziglang/zig/issues/3802
4044 pub const OpenFlags = struct {
4145 read: bool = true,
4246 write: bool = false,
4347
48 /// Open the file with a lock to prevent other processes from accessing it at the
49 /// same time. An exclusive lock will prevent other processes from acquiring a lock.
50 /// A shared lock will prevent other processes from acquiring a exclusive lock, but
51 /// doesn't prevent other process from getting their own shared locks.
52 ///
53 /// Note that the lock is only advisory on Linux, except in very specific cirsumstances[1].
54 /// This means that a process that does not respect the locking API can still get access
55 /// to the file, despite the lock.
56 ///
57 /// Windows' file locks are mandatory, and any process attempting to access the file will
58 /// receive an error.
59 ///
60 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
61 lock: Lock = .None,
62
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
4468 /// This prevents `O_NONBLOCK` from being passed even if `std.io.is_async`.
4569 /// It allows the use of `noasync` when calling functions related to opening
4670 /// the file, reading, and writing.
......@@ -60,6 +84,26 @@ pub const File = struct {
6084 /// `error.FileAlreadyExists` to be returned.
6185 exclusive: bool = false,
6286
87 /// Open the file with a lock to prevent other processes from accessing it at the
88 /// same time. An exclusive lock will prevent other processes from acquiring a lock.
89 /// A shared lock will prevent other processes from acquiring a exclusive lock, but
90 /// doesn't prevent other process from getting their own shared locks.
91 ///
92 /// Note that the lock is only advisory on Linux, except in very specific cirsumstances[1].
93 /// This means that a process that does not respect the locking API can still get access
94 /// to the file, despite the lock.
95 ///
96 /// Windows' file locks are mandatory, and any process attempting to access the file will
97 /// receive an error.
98 ///
99 /// [1]: https://www.kernel.org/doc/Documentation/filesystems/mandatory-locking.txt
100 lock: Lock = .None,
101
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
63107 /// For POSIX systems this is the file system mode the file will
64108 /// be created with.
65109 mode: Mode = default_mode,
lib/std/os.zig+34-2
......@@ -846,6 +846,9 @@ pub const OpenError = error{
846846 /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided.
847847 PathAlreadyExists,
848848 DeviceBusy,
849
850 /// The underlying filesystem does not support file locks
851 FileLocksNotSupported,
849852} || UnexpectedError;
850853
851854/// Open and possibly create a file. Keeps trying if it gets interrupted.
......@@ -931,6 +934,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t)
931934 EPERM => return error.AccessDenied,
932935 EEXIST => return error.PathAlreadyExists,
933936 EBUSY => return error.DeviceBusy,
937 EOPNOTSUPP => return error.FileLocksNotSupported,
934938 else => |err| return unexpectedErrno(err),
935939 }
936940 }
......@@ -1676,7 +1680,10 @@ pub fn renameatW(
16761680 ReplaceIfExists: windows.BOOLEAN,
16771681) RenameError!void {
16781682 const access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE;
1679 const src_fd = try windows.OpenFileW(old_dir_fd, old_path, null, access_mask, windows.FILE_OPEN);
1683 const src_fd = windows.OpenFileW(old_dir_fd, old_path, null, access_mask, null, false, windows.FILE_OPEN) catch |err| switch (err) {
1684 error.WouldBlock => unreachable,
1685 else => |e| return e,
1686 };
16801687 defer windows.CloseHandle(src_fd);
16811688
16821689 const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1);
......@@ -3218,6 +3225,28 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
32183225 }
32193226}
32203227
3228pub const FlockError = error{
3229 WouldBlock,
3230
3231 /// The kernel ran out of memory for allocating file locks
3232 SystemResources,
3233} || UnexpectedError;
3234
3235pub fn flock(fd: fd_t, operation: i32) FlockError!void {
3236 while (true) {
3237 const rc = system.flock(fd, operation);
3238 switch (errno(rc)) {
3239 0 => return,
3240 EBADF => unreachable,
3241 EINTR => continue,
3242 EINVAL => unreachable, // invalid parameters
3243 ENOLCK => return error.SystemResources,
3244 EWOULDBLOCK => return error.WouldBlock, // TODO: integrate with async instead of just returning an error
3245 else => |err| return unexpectedErrno(err),
3246 }
3247 }
3248}
3249
32213250pub const RealPathError = error{
32223251 FileNotFound,
32233252 AccessDenied,
......@@ -3269,7 +3298,10 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
32693298 return realpathW(&pathname_w, out_buffer);
32703299 }
32713300 if (builtin.os.tag == .linux and !builtin.link_libc) {
3272 const fd = try openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0);
3301 const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) {
3302 error.FileLocksNotSupported => unreachable,
3303 else => |e| return e,
3304 };
32733305 defer close(fd);
32743306
32753307 var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined;
lib/std/os/bits/darwin.zig+13
......@@ -55,6 +55,14 @@ pub const mach_timebase_info_data = extern struct {
5555pub const off_t = i64;
5656pub const ino_t = u64;
5757
58pub const Flock = extern struct {
59 l_start: off_t,
60 l_len: off_t,
61 l_pid: pid_t,
62 l_type: i16,
63 l_whence: i16,
64};
65
5866/// Renamed to Stat to not conflict with the stat function.
5967/// atime, mtime, and ctime have functions to return `timespec`,
6068/// because although this is a POSIX API, the layout and names of
......@@ -1386,3 +1394,8 @@ pub const F_UNLCK = 2;
13861394
13871395/// exclusive or write lock
13881396pub const F_WRLCK = 3;
1397
1398pub const LOCK_SH = 1;
1399pub const LOCK_EX = 2;
1400pub const LOCK_UN = 8;
1401pub const LOCK_NB = 4;
lib/std/os/bits/dragonfly.zig+5
......@@ -697,6 +697,11 @@ pub const F_DUP2FD = 10;
697697pub const F_DUPFD_CLOEXEC = 17;
698698pub const F_DUP2FD_CLOEXEC = 18;
699699
700pub const LOCK_SH = 1;
701pub const LOCK_EX = 2;
702pub const LOCK_UN = 8;
703pub const LOCK_NB = 4;
704
700705pub const Flock = extern struct {
701706 l_start: off_t,
702707 l_len: off_t,
lib/std/os/bits/freebsd.zig+22
......@@ -51,6 +51,16 @@ pub const dl_phdr_info = extern struct {
5151 dlpi_phnum: u16,
5252};
5353
54pub const Flock = extern struct {
55 l_start: off_t,
56 l_len: off_t,
57 l_pid: pid_t,
58 l_type: i16,
59 l_whence: i16,
60 l_sysid: i32,
61 __unused: [4]u8,
62};
63
5464pub const msghdr = extern struct {
5565 /// optional address
5666 msg_name: ?*sockaddr,
......@@ -315,6 +325,9 @@ pub const O_WRONLY = 0x0001;
315325pub const O_RDWR = 0x0002;
316326pub const O_ACCMODE = 0x0003;
317327
328pub const O_SHLOCK = 0x0010;
329pub const O_EXLOCK = 0x0020;
330
318331pub const O_CREAT = 0x0200;
319332pub const O_EXCL = 0x0800;
320333pub const O_NOCTTY = 0x8000;
......@@ -350,6 +363,15 @@ pub const F_GETLK = 5;
350363pub const F_SETLK = 6;
351364pub const F_SETLKW = 7;
352365
366pub const F_RDLCK = 1;
367pub const F_WRLCK = 3;
368pub const F_UNLCK = 2;
369
370pub const LOCK_SH = 1;
371pub const LOCK_EX = 2;
372pub const LOCK_UN = 8;
373pub const LOCK_NB = 4;
374
353375pub const F_SETOWN_EX = 15;
354376pub const F_GETOWN_EX = 16;
355377
lib/std/os/bits/linux/arm-eabi.zig+20
......@@ -8,6 +8,7 @@ const stack_t = linux.stack_t;
88const sigset_t = linux.sigset_t;
99const uid_t = linux.uid_t;
1010const gid_t = linux.gid_t;
11const pid_t = linux.pid_t;
1112
1213pub const SYS = extern enum(usize) {
1314 restart_syscall = 0,
......@@ -452,11 +453,20 @@ pub const F_GETLK = 12;
452453pub const F_SETLK = 13;
453454pub const F_SETLKW = 14;
454455
456pub const F_RDLCK = 0;
457pub const F_WRLCK = 1;
458pub const F_UNLCK = 2;
459
455460pub const F_SETOWN_EX = 15;
456461pub const F_GETOWN_EX = 16;
457462
458463pub const F_GETOWNER_UIDS = 17;
459464
465pub const LOCK_SH = 1;
466pub const LOCK_EX = 2;
467pub const LOCK_UN = 8;
468pub const LOCK_NB = 4;
469
460470/// stack-like segment
461471pub const MAP_GROWSDOWN = 0x0100;
462472
......@@ -499,6 +509,16 @@ pub const HWCAP_IDIV = HWCAP_IDIVA | HWCAP_IDIVT;
499509pub const HWCAP_LPAE = 1 << 20;
500510pub const HWCAP_EVTSTRM = 1 << 21;
501511
512pub const Flock = extern struct {
513 l_type: i16,
514 l_whence: i16,
515 __pad0: [4]u8,
516 l_start: off_t,
517 l_len: off_t,
518 l_pid: pid_t,
519 __unused: [4]u8,
520};
521
502522pub const msghdr = extern struct {
503523 msg_name: ?*sockaddr,
504524 msg_namelen: socklen_t,
lib/std/os/bits/linux/arm64.zig+19
......@@ -8,6 +8,7 @@ const iovec = linux.iovec;
88const iovec_const = linux.iovec_const;
99const uid_t = linux.uid_t;
1010const gid_t = linux.gid_t;
11const pid_t = linux.pid_t;
1112const stack_t = linux.stack_t;
1213const sigset_t = linux.sigset_t;
1314pub const SYS = extern enum(usize) {
......@@ -344,6 +345,15 @@ pub const F_GETLK = 5;
344345pub const F_SETLK = 6;
345346pub const F_SETLKW = 7;
346347
348pub const F_RDLCK = 0;
349pub const F_WRLCK = 1;
350pub const F_UNLCK = 2;
351
352pub const LOCK_SH = 1;
353pub const LOCK_EX = 2;
354pub const LOCK_UN = 8;
355pub const LOCK_NB = 4;
356
347357pub const F_SETOWN_EX = 15;
348358pub const F_GETOWN_EX = 16;
349359
......@@ -367,6 +377,15 @@ pub const MAP_NORESERVE = 0x4000;
367377pub const VDSO_CGT_SYM = "__kernel_clock_gettime";
368378pub const VDSO_CGT_VER = "LINUX_2.6.39";
369379
380pub const Flock = extern struct {
381 l_type: i16,
382 l_whence: i16,
383 l_start: off_t,
384 l_len: off_t,
385 l_pid: pid_t,
386 __unused: [4]u8,
387};
388
370389pub const msghdr = extern struct {
371390 msg_name: ?*sockaddr,
372391 msg_namelen: socklen_t,
lib/std/os/bits/linux/i386.zig+18
......@@ -8,6 +8,7 @@ const iovec = linux.iovec;
88const iovec_const = linux.iovec_const;
99const uid_t = linux.uid_t;
1010const gid_t = linux.gid_t;
11const pid_t = linux.pid_t;
1112const stack_t = linux.stack_t;
1213const sigset_t = linux.sigset_t;
1314
......@@ -477,6 +478,15 @@ pub const F_GETLK = 12;
477478pub const F_SETLK = 13;
478479pub const F_SETLKW = 14;
479480
481pub const F_RDLCK = 0;
482pub const F_WRLCK = 1;
483pub const F_UNLCK = 2;
484
485pub const LOCK_SH = 1;
486pub const LOCK_EX = 2;
487pub const LOCK_UN = 8;
488pub const LOCK_NB = 4;
489
480490pub const F_SETOWN_EX = 15;
481491pub const F_GETOWN_EX = 16;
482492
......@@ -494,6 +504,14 @@ pub const MMAP2_UNIT = 4096;
494504pub const VDSO_CGT_SYM = "__vdso_clock_gettime";
495505pub const VDSO_CGT_VER = "LINUX_2.6";
496506
507pub const Flock = extern struct {
508 l_type: i16,
509 l_whence: i16,
510 l_start: off_t,
511 l_len: off_t,
512 l_pid: pid_t,
513};
514
497515pub const msghdr = extern struct {
498516 msg_name: ?*sockaddr,
499517 msg_namelen: socklen_t,
lib/std/os/bits/linux/mipsel.zig+20
......@@ -5,6 +5,7 @@ const iovec = linux.iovec;
55const iovec_const = linux.iovec_const;
66const uid_t = linux.uid_t;
77const gid_t = linux.gid_t;
8const pid_t = linux.pid_t;
89
910pub const SYS = extern enum(usize) {
1011 pub const Linux = 4000;
......@@ -419,6 +420,15 @@ pub const F_GETLK = 33;
419420pub const F_SETLK = 34;
420421pub const F_SETLKW = 35;
421422
423pub const F_RDLCK = 0;
424pub const F_WRLCK = 1;
425pub const F_UNLCK = 2;
426
427pub const LOCK_SH = 1;
428pub const LOCK_EX = 2;
429pub const LOCK_UN = 8;
430pub const LOCK_NB = 4;
431
422432pub const F_SETOWN_EX = 15;
423433pub const F_GETOWN_EX = 16;
424434
......@@ -464,6 +474,16 @@ pub const SO_RCVBUFFORCE = 33;
464474pub const VDSO_CGT_SYM = "__kernel_clock_gettime";
465475pub const VDSO_CGT_VER = "LINUX_2.6.39";
466476
477pub const Flock = extern struct {
478 l_type: i16,
479 l_whence: i16,
480 __pad0: [4]u8,
481 l_start: off_t,
482 l_len: off_t,
483 l_pid: pid_t,
484 __unused: [4]u8,
485};
486
467487pub const blksize_t = i32;
468488pub const nlink_t = u32;
469489pub const time_t = isize;
lib/std/os/bits/linux/riscv64.zig+19
......@@ -2,6 +2,7 @@
22const std = @import("../../../std.zig");
33const uid_t = std.os.linux.uid_t;
44const gid_t = std.os.linux.gid_t;
5const pid_t = std.os.linux.pid_t;
56
67pub const SYS = extern enum(usize) {
78 io_setup = 0,
......@@ -338,6 +339,15 @@ pub const F_GETOWN = 9;
338339pub const F_SETSIG = 10;
339340pub const F_GETSIG = 11;
340341
342pub const F_RDLCK = 0;
343pub const F_WRLCK = 1;
344pub const F_UNLCK = 2;
345
346pub const LOCK_SH = 1;
347pub const LOCK_EX = 2;
348pub const LOCK_UN = 8;
349pub const LOCK_NB = 4;
350
341351pub const F_SETOWN_EX = 15;
342352pub const F_GETOWN_EX = 16;
343353
......@@ -356,6 +366,15 @@ pub const timespec = extern struct {
356366 tv_nsec: isize,
357367};
358368
369pub const Flock = extern struct {
370 l_type: i16,
371 l_whence: i16,
372 l_start: off_t,
373 l_len: off_t,
374 l_pid: pid_t,
375 __unused: [4]u8,
376};
377
359378/// Renamed to Stat to not conflict with the stat function.
360379/// atime, mtime, and ctime have functions to return `timespec`,
361380/// because although this is a POSIX API, the layout and names of
lib/std/os/bits/linux/x86_64.zig+17
......@@ -462,6 +462,23 @@ pub const REG_TRAPNO = 20;
462462pub const REG_OLDMASK = 21;
463463pub const REG_CR2 = 22;
464464
465pub const LOCK_SH = 1;
466pub const LOCK_EX = 2;
467pub const LOCK_UN = 8;
468pub const LOCK_NB = 4;
469
470pub const F_RDLCK = 0;
471pub const F_WRLCK = 1;
472pub const F_UNLCK = 2;
473
474pub const Flock = extern struct {
475 l_type: i16,
476 l_whence: i16,
477 l_start: off_t,
478 l_len: off_t,
479 l_pid: pid_t,
480};
481
465482pub const msghdr = extern struct {
466483 msg_name: ?*sockaddr,
467484 msg_namelen: socklen_t,
lib/std/os/bits/netbsd.zig+17
......@@ -35,6 +35,14 @@ pub const dl_phdr_info = extern struct {
3535 dlpi_phnum: u16,
3636};
3737
38pub const Flock = extern struct {
39 l_start: off_t,
40 l_len: off_t,
41 l_pid: pid_t,
42 l_type: i16,
43 l_whence: i16,
44};
45
3846pub const addrinfo = extern struct {
3947 flags: i32,
4048 family: i32,
......@@ -435,6 +443,15 @@ pub const F_GETLK = 7;
435443pub const F_SETLK = 8;
436444pub const F_SETLKW = 9;
437445
446pub const F_RDLCK = 1;
447pub const F_WRLCK = 3;
448pub const F_UNLCK = 2;
449
450pub const LOCK_SH = 1;
451pub const LOCK_EX = 2;
452pub const LOCK_UN = 8;
453pub const LOCK_NB = 4;
454
438455pub const FD_CLOEXEC = 1;
439456
440457pub const SEEK_SET = 0;
lib/std/os/linux.zig+4
......@@ -592,6 +592,10 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) usize {
592592 return syscall3(.fcntl, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, cmd)), arg);
593593}
594594
595pub fn flock(fd: fd_t, operation: i32) usize {
596 return syscall2(.flock, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, operation)));
597}
598
595599var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime);
596600
597601// We must follow the C calling convention when we call into the VDSO
lib/std/os/windows.zig+43-26
......@@ -98,6 +98,7 @@ pub const OpenError = error{
9898 PathAlreadyExists,
9999 Unexpected,
100100 NameTooLong,
101 WouldBlock,
101102};
102103
103104/// TODO rename to CreateFileW
......@@ -107,6 +108,8 @@ pub fn OpenFileW(
107108 sub_path_w: [*:0]const u16,
108109 sa: ?*SECURITY_ATTRIBUTES,
109110 access_mask: ACCESS_MASK,
111 share_access_opt: ?ULONG,
112 share_access_nonblocking: bool,
110113 creation: ULONG,
111114) OpenError!HANDLE {
112115 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
......@@ -135,32 +138,46 @@ pub fn OpenFileW(
135138 .SecurityQualityOfService = null,
136139 };
137140 var io: IO_STATUS_BLOCK = undefined;
138 const rc = ntdll.NtCreateFile(
139 &result,
140 access_mask,
141 &attr,
142 &io,
143 null,
144 FILE_ATTRIBUTE_NORMAL,
145 FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
146 creation,
147 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
148 null,
149 0,
150 );
151 switch (rc) {
152 .SUCCESS => return result,
153 .OBJECT_NAME_INVALID => unreachable,
154 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
155 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
156 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
157 .INVALID_PARAMETER => unreachable,
158 .SHARING_VIOLATION => return error.SharingViolation,
159 .ACCESS_DENIED => return error.AccessDenied,
160 .PIPE_BUSY => return error.PipeBusy,
161 .OBJECT_PATH_SYNTAX_BAD => unreachable,
162 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
163 else => return unexpectedStatus(rc),
141 const share_access = share_access_opt orelse (FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE);
142
143 var delay: usize = 1;
144 while (true) {
145 const rc = ntdll.NtCreateFile(
146 &result,
147 access_mask,
148 &attr,
149 &io,
150 null,
151 FILE_ATTRIBUTE_NORMAL,
152 share_access,
153 creation,
154 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
155 null,
156 0,
157 );
158 switch (rc) {
159 .SUCCESS => return result,
160 .OBJECT_NAME_INVALID => unreachable,
161 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
162 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
163 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
164 .INVALID_PARAMETER => unreachable,
165 .SHARING_VIOLATION => {
166 if (share_access_nonblocking) {
167 return error.WouldBlock;
168 }
169 std.time.sleep(delay);
170 if (delay < 1 * std.time.ns_per_s) {
171 delay *= 2;
172 }
173 continue; // TODO: don't loop for async
174 },
175 .ACCESS_DENIED => return error.AccessDenied,
176 .PIPE_BUSY => return error.PipeBusy,
177 .OBJECT_PATH_SYNTAX_BAD => unreachable,
178 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
179 else => return unexpectedStatus(rc),
180 }
164181 }
165182}
166183
lib/std/zig/system.zig+2
......@@ -468,6 +468,8 @@ pub const NativeTargetInfo = struct {
468468 error.InvalidUtf8 => unreachable,
469469 error.BadPathName => unreachable,
470470 error.PipeBusy => unreachable,
471 error.FileLocksNotSupported => unreachable,
472 error.WouldBlock => unreachable,
471473
472474 error.IsDir,
473475 error.NotDir,
src-self-hosted/stage2.zig+3
......@@ -116,6 +116,8 @@ const Error = extern enum {
116116 UnknownClangOption,
117117 NestedResponseFile,
118118 ZigIsTheCCompiler,
119 FileBusy,
120 Locked,
119121};
120122
121123const FILE = std.c.FILE;
......@@ -847,6 +849,7 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [
847849 error.NoDevice => return .NoDevice,
848850 error.NotDir => return .NotDir,
849851 error.DeviceBusy => return .DeviceBusy,
852 error.FileLocksNotSupported => unreachable,
850853 };
851854 stage1_libc.initFromStage2(libc);
852855 return .None;
src/error.cpp+2
......@@ -86,6 +86,8 @@ const char *err_str(Error err) {
8686 case ErrorUnknownClangOption: return "unknown Clang option";
8787 case ErrorNestedResponseFile: return "nested response file";
8888 case ErrorZigIsTheCCompiler: return "Zig was not provided with libc installation information, and so it does not know where the libc paths are on the system. Zig attempted to use the system C compiler to find out where the libc paths are, but discovered that Zig is being used as the system C compiler.";
89 case ErrorFileBusy: return "file is busy";
90 case ErrorLocked: return "file is locked by another process";
8991 }
9092 return "(invalid error)";
9193}
src/stage2.h+2
......@@ -108,6 +108,8 @@ enum Error {
108108 ErrorUnknownClangOption,
109109 ErrorNestedResponseFile,
110110 ErrorZigIsTheCCompiler,
111 ErrorFileBusy,
112 ErrorLocked,
111113};
112114
113115// ABI warning