| author | |
| committer | |
| log | a6e288d5fe51d5373fa995b5eec2dd2325c1ea9f |
| tree | 583c2fbf0a708c222e3cc7a493f93b0a2e9da16b |
| parent | 121307679bd1ffeaa8a290a826396662f26a4ac1 |
| parent | 5951211d3fc348fc37a86abc9906acf4ee796883 |
| signature |
Add lock option to File.OpenFlags and File.CreateFlags21 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 | ... | @@ -122,6 +122,7 @@ pub extern "c" fn sysctlnametomib(name: [*:0]const u8, mibp: ?*c_int, sizep: ?*u |
| 122 | pub extern "c" fn tcgetattr(fd: fd_t, termios_p: *termios) c_int; | 122 | pub extern "c" fn tcgetattr(fd: fd_t, termios_p: *termios) c_int; |
| 123 | pub extern "c" fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) c_int; | 123 | pub extern "c" fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) c_int; |
| 124 | pub extern "c" fn fcntl(fd: fd_t, cmd: c_int, ...) c_int; | 124 | pub extern "c" fn fcntl(fd: fd_t, cmd: c_int, ...) c_int; |
| 125 | pub extern "c" fn flock(fd: fd_t, operation: c_int) c_int; | ||
| 125 | pub extern "c" fn uname(buf: *utsname) c_int; | 126 | pub extern "c" fn uname(buf: *utsname) c_int; |
| 126 | 127 | ||
| 127 | pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int; | 128 | pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int; |
lib/std/child_process.zig+1| ... | @@ -357,6 +357,7 @@ pub const ChildProcess = struct { | ... | @@ -357,6 +357,7 @@ pub const ChildProcess = struct { |
| 357 | error.NoSpaceLeft => unreachable, | 357 | error.NoSpaceLeft => unreachable, |
| 358 | error.FileTooBig => unreachable, | 358 | error.FileTooBig => unreachable, |
| 359 | error.DeviceBusy => unreachable, | 359 | error.DeviceBusy => unreachable, |
| 360 | error.FileLocksNotSupported => unreachable, | ||
| 360 | else => |e| return e, | 361 | else => |e| return e, |
| 361 | } | 362 | } |
| 362 | else | 363 | else |
lib/std/fs.zig+219-5| ... | @@ -594,8 +594,19 @@ pub const Dir = struct { | ... | @@ -594,8 +594,19 @@ pub const Dir = struct { |
| 594 | const path_w = try os.windows.cStrToPrefixedFileW(sub_path); | 594 | const path_w = try os.windows.cStrToPrefixedFileW(sub_path); |
| 595 | return self.openFileW(&path_w, flags); | 595 | return self.openFileW(&path_w, flags); |
| 596 | } | 596 | } |
| 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 | |||
| 597 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; | 608 | 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) |
| 599 | @as(u32, os.O_RDWR) | 610 | @as(u32, os.O_RDWR) |
| 600 | else if (flags.write) | 611 | else if (flags.write) |
| 601 | @as(u32, os.O_WRONLY) | 612 | @as(u32, os.O_WRONLY) |
| ... | @@ -605,6 +616,17 @@ pub const Dir = struct { | ... | @@ -605,6 +616,17 @@ pub const Dir = struct { |
| 605 | try std.event.Loop.instance.?.openatZ(self.fd, sub_path, os_flags, 0) | 616 | try std.event.Loop.instance.?.openatZ(self.fd, sub_path, os_flags, 0) |
| 606 | else | 617 | else |
| 607 | try os.openatZ(self.fd, sub_path, os_flags, 0); | 618 | 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 | |||
| 608 | return File{ | 630 | return File{ |
| 609 | .handle = fd, | 631 | .handle = fd, |
| 610 | .io_mode = .blocking, | 632 | .io_mode = .blocking, |
| ... | @@ -622,8 +644,15 @@ pub const Dir = struct { | ... | @@ -622,8 +644,15 @@ pub const Dir = struct { |
| 622 | const access_mask = w.SYNCHRONIZE | | 644 | const access_mask = w.SYNCHRONIZE | |
| 623 | (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | | 645 | (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | |
| 624 | (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0); | 646 | (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 | |||
| 625 | return @as(File, .{ | 654 | 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), |
| 627 | .io_mode = .blocking, | 656 | .io_mode = .blocking, |
| 628 | }); | 657 | }); |
| 629 | } | 658 | } |
| ... | @@ -648,8 +677,19 @@ pub const Dir = struct { | ... | @@ -648,8 +677,19 @@ pub const Dir = struct { |
| 648 | const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); | 677 | const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); |
| 649 | return self.createFileW(&path_w, flags); | 678 | return self.createFileW(&path_w, flags); |
| 650 | } | 679 | } |
| 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 | |||
| 651 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; | 691 | 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 | |
| 653 | (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) | | 693 | (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) | |
| 654 | (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) | | 694 | (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) | |
| 655 | (if (flags.exclusive) @as(u32, os.O_EXCL) else 0); | 695 | (if (flags.exclusive) @as(u32, os.O_EXCL) else 0); |
| ... | @@ -657,6 +697,17 @@ pub const Dir = struct { | ... | @@ -657,6 +697,17 @@ pub const Dir = struct { |
| 657 | try std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, flags.mode) | 697 | try std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, flags.mode) |
| 658 | else | 698 | else |
| 659 | try os.openatZ(self.fd, sub_path_c, os_flags, flags.mode); | 699 | 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 | |||
| 660 | return File{ .handle = fd, .io_mode = .blocking }; | 711 | return File{ .handle = fd, .io_mode = .blocking }; |
| 661 | } | 712 | } |
| 662 | 713 | ||
| ... | @@ -672,8 +723,15 @@ pub const Dir = struct { | ... | @@ -672,8 +723,15 @@ pub const Dir = struct { |
| 672 | @as(u32, w.FILE_OVERWRITE_IF) | 723 | @as(u32, w.FILE_OVERWRITE_IF) |
| 673 | else | 724 | else |
| 674 | @as(u32, w.FILE_OPEN_IF); | 725 | @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 | |||
| 675 | return @as(File, .{ | 733 | 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), |
| 677 | .io_mode = .blocking, | 735 | .io_mode = .blocking, |
| 678 | }); | 736 | }); |
| 679 | } | 737 | } |
| ... | @@ -802,6 +860,7 @@ pub const Dir = struct { | ... | @@ -802,6 +860,7 @@ pub const Dir = struct { |
| 802 | error.IsDir => unreachable, // we're providing O_DIRECTORY | 860 | error.IsDir => unreachable, // we're providing O_DIRECTORY |
| 803 | error.NoSpaceLeft => unreachable, // not providing O_CREAT | 861 | error.NoSpaceLeft => unreachable, // not providing O_CREAT |
| 804 | error.PathAlreadyExists => unreachable, // not providing O_CREAT | 862 | error.PathAlreadyExists => unreachable, // not providing O_CREAT |
| 863 | error.FileLocksNotSupported => unreachable, // locking folders is not supported | ||
| 805 | else => |e| return e, | 864 | else => |e| return e, |
| 806 | }; | 865 | }; |
| 807 | return Dir{ .fd = fd }; | 866 | return Dir{ .fd = fd }; |
| ... | @@ -1508,7 +1567,7 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { | ... | @@ -1508,7 +1567,7 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { |
| 1508 | return walker; | 1567 | return walker; |
| 1509 | } | 1568 | } |
| 1510 | 1569 | ||
| 1511 | pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError; | 1570 | pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError || os.FlockError; |
| 1512 | 1571 | ||
| 1513 | pub fn openSelfExe() OpenSelfExeError!File { | 1572 | pub fn openSelfExe() OpenSelfExeError!File { |
| 1514 | if (builtin.os.tag == .linux) { | 1573 | if (builtin.os.tag == .linux) { |
| ... | @@ -1624,3 +1683,158 @@ test "" { | ... | @@ -1624,3 +1683,158 @@ test "" { |
| 1624 | _ = @import("fs/get_app_data_dir.zig"); | 1683 | _ = @import("fs/get_app_data_dir.zig"); |
| 1625 | _ = @import("fs/watch.zig"); | 1684 | _ = @import("fs/watch.zig"); |
| 1626 | } | 1685 | } |
| 1686 | |||
| 1687 | const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.millisecond; | ||
| 1688 | |||
| 1689 | test "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 | |||
| 1705 | test "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 | |||
| 1734 | test "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 | |||
| 1774 | const 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 | |||
| 1829 | fn 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 { | ... | @@ -34,13 +34,37 @@ pub const File = struct { |
| 34 | else => 0o666, | 34 | else => 0o666, |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 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 | }; | ||
| 38 | 42 | ||
| 39 | /// TODO https://github.com/ziglang/zig/issues/3802 | 43 | /// 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, |
| 43 | 47 | ||
| 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 | |||
| 44 | /// 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`. |
| 45 | /// It allows the use of `noasync` when calling functions related to opening | 69 | /// It allows the use of `noasync` when calling functions related to opening |
| 46 | /// the file, reading, and writing. | 70 | /// the file, reading, and writing. |
| ... | @@ -60,6 +84,26 @@ pub const File = struct { | ... | @@ -60,6 +84,26 @@ pub const File = struct { |
| 60 | /// `error.FileAlreadyExists` to be returned. | 84 | /// `error.FileAlreadyExists` to be returned. |
| 61 | exclusive: bool = false, | 85 | exclusive: bool = false, |
| 62 | 86 | ||
| 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 | |||
| 63 | /// For POSIX systems this is the file system mode the file will | 107 | /// For POSIX systems this is the file system mode the file will |
| 64 | /// be created with. | 108 | /// be created with. |
| 65 | mode: Mode = default_mode, | 109 | mode: Mode = default_mode, |
lib/std/os.zig+34-2| ... | @@ -846,6 +846,9 @@ pub const OpenError = error{ | ... | @@ -846,6 +846,9 @@ pub const OpenError = error{ |
| 846 | /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided. | 846 | /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided. |
| 847 | PathAlreadyExists, | 847 | PathAlreadyExists, |
| 848 | DeviceBusy, | 848 | DeviceBusy, |
| 849 | |||
| 850 | /// The underlying filesystem does not support file locks | ||
| 851 | FileLocksNotSupported, | ||
| 849 | } || UnexpectedError; | 852 | } || UnexpectedError; |
| 850 | 853 | ||
| 851 | /// Open and possibly create a file. Keeps trying if it gets interrupted. | 854 | /// 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) | ... | @@ -931,6 +934,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) |
| 931 | EPERM => return error.AccessDenied, | 934 | EPERM => return error.AccessDenied, |
| 932 | EEXIST => return error.PathAlreadyExists, | 935 | EEXIST => return error.PathAlreadyExists, |
| 933 | EBUSY => return error.DeviceBusy, | 936 | EBUSY => return error.DeviceBusy, |
| 937 | EOPNOTSUPP => return error.FileLocksNotSupported, | ||
| 934 | else => |err| return unexpectedErrno(err), | 938 | else => |err| return unexpectedErrno(err), |
| 935 | } | 939 | } |
| 936 | } | 940 | } |
| ... | @@ -1676,7 +1680,10 @@ pub fn renameatW( | ... | @@ -1676,7 +1680,10 @@ pub fn renameatW( |
| 1676 | ReplaceIfExists: windows.BOOLEAN, | 1680 | ReplaceIfExists: windows.BOOLEAN, |
| 1677 | ) RenameError!void { | 1681 | ) RenameError!void { |
| 1678 | const access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE; | 1682 | 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 | }; | ||
| 1680 | defer windows.CloseHandle(src_fd); | 1687 | defer windows.CloseHandle(src_fd); |
| 1681 | 1688 | ||
| 1682 | const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1); | 1689 | 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 { | ... | @@ -3218,6 +3225,28 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize { |
| 3218 | } | 3225 | } |
| 3219 | } | 3226 | } |
| 3220 | 3227 | ||
| 3228 | pub const FlockError = error{ | ||
| 3229 | WouldBlock, | ||
| 3230 | |||
| 3231 | /// The kernel ran out of memory for allocating file locks | ||
| 3232 | SystemResources, | ||
| 3233 | } || UnexpectedError; | ||
| 3234 | |||
| 3235 | pub 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 | |||
| 3221 | pub const RealPathError = error{ | 3250 | pub const RealPathError = error{ |
| 3222 | FileNotFound, | 3251 | FileNotFound, |
| 3223 | AccessDenied, | 3252 | AccessDenied, |
| ... | @@ -3269,7 +3298,10 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP | ... | @@ -3269,7 +3298,10 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP |
| 3269 | return realpathW(&pathname_w, out_buffer); | 3298 | return realpathW(&pathname_w, out_buffer); |
| 3270 | } | 3299 | } |
| 3271 | if (builtin.os.tag == .linux and !builtin.link_libc) { | 3300 | 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 | }; | ||
| 3273 | defer close(fd); | 3305 | defer close(fd); |
| 3274 | 3306 | ||
| 3275 | var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined; | 3307 | 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 { | ... | @@ -55,6 +55,14 @@ pub const mach_timebase_info_data = extern struct { |
| 55 | pub const off_t = i64; | 55 | pub const off_t = i64; |
| 56 | pub const ino_t = u64; | 56 | pub const ino_t = u64; |
| 57 | 57 | ||
| 58 | pub 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 | |||
| 58 | /// Renamed to Stat to not conflict with the stat function. | 66 | /// Renamed to Stat to not conflict with the stat function. |
| 59 | /// atime, mtime, and ctime have functions to return `timespec`, | 67 | /// atime, mtime, and ctime have functions to return `timespec`, |
| 60 | /// because although this is a POSIX API, the layout and names of | 68 | /// because although this is a POSIX API, the layout and names of |
| ... | @@ -1386,3 +1394,8 @@ pub const F_UNLCK = 2; | ... | @@ -1386,3 +1394,8 @@ pub const F_UNLCK = 2; |
| 1386 | 1394 | ||
| 1387 | /// exclusive or write lock | 1395 | /// exclusive or write lock |
| 1388 | pub const F_WRLCK = 3; | 1396 | pub const F_WRLCK = 3; |
| 1397 | |||
| 1398 | pub const LOCK_SH = 1; | ||
| 1399 | pub const LOCK_EX = 2; | ||
| 1400 | pub const LOCK_UN = 8; | ||
| 1401 | pub const LOCK_NB = 4; |
lib/std/os/bits/dragonfly.zig+5| ... | @@ -697,6 +697,11 @@ pub const F_DUP2FD = 10; | ... | @@ -697,6 +697,11 @@ pub const F_DUP2FD = 10; |
| 697 | pub const F_DUPFD_CLOEXEC = 17; | 697 | pub const F_DUPFD_CLOEXEC = 17; |
| 698 | pub const F_DUP2FD_CLOEXEC = 18; | 698 | pub const F_DUP2FD_CLOEXEC = 18; |
| 699 | 699 | ||
| 700 | pub const LOCK_SH = 1; | ||
| 701 | pub const LOCK_EX = 2; | ||
| 702 | pub const LOCK_UN = 8; | ||
| 703 | pub const LOCK_NB = 4; | ||
| 704 | |||
| 700 | pub const Flock = extern struct { | 705 | pub const Flock = extern struct { |
| 701 | l_start: off_t, | 706 | l_start: off_t, |
| 702 | l_len: off_t, | 707 | l_len: off_t, |
lib/std/os/bits/freebsd.zig+22| ... | @@ -51,6 +51,16 @@ pub const dl_phdr_info = extern struct { | ... | @@ -51,6 +51,16 @@ pub const dl_phdr_info = extern struct { |
| 51 | dlpi_phnum: u16, | 51 | dlpi_phnum: u16, |
| 52 | }; | 52 | }; |
| 53 | 53 | ||
| 54 | pub 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 | |||
| 54 | pub const msghdr = extern struct { | 64 | pub const msghdr = extern struct { |
| 55 | /// optional address | 65 | /// optional address |
| 56 | msg_name: ?*sockaddr, | 66 | msg_name: ?*sockaddr, |
| ... | @@ -315,6 +325,9 @@ pub const O_WRONLY = 0x0001; | ... | @@ -315,6 +325,9 @@ pub const O_WRONLY = 0x0001; |
| 315 | pub const O_RDWR = 0x0002; | 325 | pub const O_RDWR = 0x0002; |
| 316 | pub const O_ACCMODE = 0x0003; | 326 | pub const O_ACCMODE = 0x0003; |
| 317 | 327 | ||
| 328 | pub const O_SHLOCK = 0x0010; | ||
| 329 | pub const O_EXLOCK = 0x0020; | ||
| 330 | |||
| 318 | pub const O_CREAT = 0x0200; | 331 | pub const O_CREAT = 0x0200; |
| 319 | pub const O_EXCL = 0x0800; | 332 | pub const O_EXCL = 0x0800; |
| 320 | pub const O_NOCTTY = 0x8000; | 333 | pub const O_NOCTTY = 0x8000; |
| ... | @@ -350,6 +363,15 @@ pub const F_GETLK = 5; | ... | @@ -350,6 +363,15 @@ pub const F_GETLK = 5; |
| 350 | pub const F_SETLK = 6; | 363 | pub const F_SETLK = 6; |
| 351 | pub const F_SETLKW = 7; | 364 | pub const F_SETLKW = 7; |
| 352 | 365 | ||
| 366 | pub const F_RDLCK = 1; | ||
| 367 | pub const F_WRLCK = 3; | ||
| 368 | pub const F_UNLCK = 2; | ||
| 369 | |||
| 370 | pub const LOCK_SH = 1; | ||
| 371 | pub const LOCK_EX = 2; | ||
| 372 | pub const LOCK_UN = 8; | ||
| 373 | pub const LOCK_NB = 4; | ||
| 374 | |||
| 353 | pub const F_SETOWN_EX = 15; | 375 | pub const F_SETOWN_EX = 15; |
| 354 | pub const F_GETOWN_EX = 16; | 376 | pub const F_GETOWN_EX = 16; |
| 355 | 377 |
lib/std/os/bits/linux/arm-eabi.zig+20| ... | @@ -8,6 +8,7 @@ const stack_t = linux.stack_t; | ... | @@ -8,6 +8,7 @@ const stack_t = linux.stack_t; |
| 8 | const sigset_t = linux.sigset_t; | 8 | const sigset_t = linux.sigset_t; |
| 9 | const uid_t = linux.uid_t; | 9 | const uid_t = linux.uid_t; |
| 10 | const gid_t = linux.gid_t; | 10 | const gid_t = linux.gid_t; |
| 11 | const pid_t = linux.pid_t; | ||
| 11 | 12 | ||
| 12 | pub const SYS = extern enum(usize) { | 13 | pub const SYS = extern enum(usize) { |
| 13 | restart_syscall = 0, | 14 | restart_syscall = 0, |
| ... | @@ -452,11 +453,20 @@ pub const F_GETLK = 12; | ... | @@ -452,11 +453,20 @@ pub const F_GETLK = 12; |
| 452 | pub const F_SETLK = 13; | 453 | pub const F_SETLK = 13; |
| 453 | pub const F_SETLKW = 14; | 454 | pub const F_SETLKW = 14; |
| 454 | 455 | ||
| 456 | pub const F_RDLCK = 0; | ||
| 457 | pub const F_WRLCK = 1; | ||
| 458 | pub const F_UNLCK = 2; | ||
| 459 | |||
| 455 | pub const F_SETOWN_EX = 15; | 460 | pub const F_SETOWN_EX = 15; |
| 456 | pub const F_GETOWN_EX = 16; | 461 | pub const F_GETOWN_EX = 16; |
| 457 | 462 | ||
| 458 | pub const F_GETOWNER_UIDS = 17; | 463 | pub const F_GETOWNER_UIDS = 17; |
| 459 | 464 | ||
| 465 | pub const LOCK_SH = 1; | ||
| 466 | pub const LOCK_EX = 2; | ||
| 467 | pub const LOCK_UN = 8; | ||
| 468 | pub const LOCK_NB = 4; | ||
| 469 | |||
| 460 | /// stack-like segment | 470 | /// stack-like segment |
| 461 | pub const MAP_GROWSDOWN = 0x0100; | 471 | pub const MAP_GROWSDOWN = 0x0100; |
| 462 | 472 | ||
| ... | @@ -499,6 +509,16 @@ pub const HWCAP_IDIV = HWCAP_IDIVA | HWCAP_IDIVT; | ... | @@ -499,6 +509,16 @@ pub const HWCAP_IDIV = HWCAP_IDIVA | HWCAP_IDIVT; |
| 499 | pub const HWCAP_LPAE = 1 << 20; | 509 | pub const HWCAP_LPAE = 1 << 20; |
| 500 | pub const HWCAP_EVTSTRM = 1 << 21; | 510 | pub const HWCAP_EVTSTRM = 1 << 21; |
| 501 | 511 | ||
| 512 | pub 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 | |||
| 502 | pub const msghdr = extern struct { | 522 | pub const msghdr = extern struct { |
| 503 | msg_name: ?*sockaddr, | 523 | msg_name: ?*sockaddr, |
| 504 | msg_namelen: socklen_t, | 524 | msg_namelen: socklen_t, |
lib/std/os/bits/linux/arm64.zig+19| ... | @@ -8,6 +8,7 @@ const iovec = linux.iovec; | ... | @@ -8,6 +8,7 @@ const iovec = linux.iovec; |
| 8 | const iovec_const = linux.iovec_const; | 8 | const iovec_const = linux.iovec_const; |
| 9 | const uid_t = linux.uid_t; | 9 | const uid_t = linux.uid_t; |
| 10 | const gid_t = linux.gid_t; | 10 | const gid_t = linux.gid_t; |
| 11 | const pid_t = linux.pid_t; | ||
| 11 | const stack_t = linux.stack_t; | 12 | const stack_t = linux.stack_t; |
| 12 | const sigset_t = linux.sigset_t; | 13 | const sigset_t = linux.sigset_t; |
| 13 | pub const SYS = extern enum(usize) { | 14 | pub const SYS = extern enum(usize) { |
| ... | @@ -344,6 +345,15 @@ pub const F_GETLK = 5; | ... | @@ -344,6 +345,15 @@ pub const F_GETLK = 5; |
| 344 | pub const F_SETLK = 6; | 345 | pub const F_SETLK = 6; |
| 345 | pub const F_SETLKW = 7; | 346 | pub const F_SETLKW = 7; |
| 346 | 347 | ||
| 348 | pub const F_RDLCK = 0; | ||
| 349 | pub const F_WRLCK = 1; | ||
| 350 | pub const F_UNLCK = 2; | ||
| 351 | |||
| 352 | pub const LOCK_SH = 1; | ||
| 353 | pub const LOCK_EX = 2; | ||
| 354 | pub const LOCK_UN = 8; | ||
| 355 | pub const LOCK_NB = 4; | ||
| 356 | |||
| 347 | pub const F_SETOWN_EX = 15; | 357 | pub const F_SETOWN_EX = 15; |
| 348 | pub const F_GETOWN_EX = 16; | 358 | pub const F_GETOWN_EX = 16; |
| 349 | 359 | ||
| ... | @@ -367,6 +377,15 @@ pub const MAP_NORESERVE = 0x4000; | ... | @@ -367,6 +377,15 @@ pub const MAP_NORESERVE = 0x4000; |
| 367 | pub const VDSO_CGT_SYM = "__kernel_clock_gettime"; | 377 | pub const VDSO_CGT_SYM = "__kernel_clock_gettime"; |
| 368 | pub const VDSO_CGT_VER = "LINUX_2.6.39"; | 378 | pub const VDSO_CGT_VER = "LINUX_2.6.39"; |
| 369 | 379 | ||
| 380 | pub 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 | |||
| 370 | pub const msghdr = extern struct { | 389 | pub const msghdr = extern struct { |
| 371 | msg_name: ?*sockaddr, | 390 | msg_name: ?*sockaddr, |
| 372 | msg_namelen: socklen_t, | 391 | msg_namelen: socklen_t, |
lib/std/os/bits/linux/i386.zig+18| ... | @@ -8,6 +8,7 @@ const iovec = linux.iovec; | ... | @@ -8,6 +8,7 @@ const iovec = linux.iovec; |
| 8 | const iovec_const = linux.iovec_const; | 8 | const iovec_const = linux.iovec_const; |
| 9 | const uid_t = linux.uid_t; | 9 | const uid_t = linux.uid_t; |
| 10 | const gid_t = linux.gid_t; | 10 | const gid_t = linux.gid_t; |
| 11 | const pid_t = linux.pid_t; | ||
| 11 | const stack_t = linux.stack_t; | 12 | const stack_t = linux.stack_t; |
| 12 | const sigset_t = linux.sigset_t; | 13 | const sigset_t = linux.sigset_t; |
| 13 | 14 | ||
| ... | @@ -477,6 +478,15 @@ pub const F_GETLK = 12; | ... | @@ -477,6 +478,15 @@ pub const F_GETLK = 12; |
| 477 | pub const F_SETLK = 13; | 478 | pub const F_SETLK = 13; |
| 478 | pub const F_SETLKW = 14; | 479 | pub const F_SETLKW = 14; |
| 479 | 480 | ||
| 481 | pub const F_RDLCK = 0; | ||
| 482 | pub const F_WRLCK = 1; | ||
| 483 | pub const F_UNLCK = 2; | ||
| 484 | |||
| 485 | pub const LOCK_SH = 1; | ||
| 486 | pub const LOCK_EX = 2; | ||
| 487 | pub const LOCK_UN = 8; | ||
| 488 | pub const LOCK_NB = 4; | ||
| 489 | |||
| 480 | pub const F_SETOWN_EX = 15; | 490 | pub const F_SETOWN_EX = 15; |
| 481 | pub const F_GETOWN_EX = 16; | 491 | pub const F_GETOWN_EX = 16; |
| 482 | 492 | ||
| ... | @@ -494,6 +504,14 @@ pub const MMAP2_UNIT = 4096; | ... | @@ -494,6 +504,14 @@ pub const MMAP2_UNIT = 4096; |
| 494 | pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; | 504 | pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; |
| 495 | pub const VDSO_CGT_VER = "LINUX_2.6"; | 505 | pub const VDSO_CGT_VER = "LINUX_2.6"; |
| 496 | 506 | ||
| 507 | pub 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 | |||
| 497 | pub const msghdr = extern struct { | 515 | pub const msghdr = extern struct { |
| 498 | msg_name: ?*sockaddr, | 516 | msg_name: ?*sockaddr, |
| 499 | msg_namelen: socklen_t, | 517 | msg_namelen: socklen_t, |
lib/std/os/bits/linux/mipsel.zig+20| ... | @@ -5,6 +5,7 @@ const iovec = linux.iovec; | ... | @@ -5,6 +5,7 @@ const iovec = linux.iovec; |
| 5 | const iovec_const = linux.iovec_const; | 5 | const iovec_const = linux.iovec_const; |
| 6 | const uid_t = linux.uid_t; | 6 | const uid_t = linux.uid_t; |
| 7 | const gid_t = linux.gid_t; | 7 | const gid_t = linux.gid_t; |
| 8 | const pid_t = linux.pid_t; | ||
| 8 | 9 | ||
| 9 | pub const SYS = extern enum(usize) { | 10 | pub const SYS = extern enum(usize) { |
| 10 | pub const Linux = 4000; | 11 | pub const Linux = 4000; |
| ... | @@ -419,6 +420,15 @@ pub const F_GETLK = 33; | ... | @@ -419,6 +420,15 @@ pub const F_GETLK = 33; |
| 419 | pub const F_SETLK = 34; | 420 | pub const F_SETLK = 34; |
| 420 | pub const F_SETLKW = 35; | 421 | pub const F_SETLKW = 35; |
| 421 | 422 | ||
| 423 | pub const F_RDLCK = 0; | ||
| 424 | pub const F_WRLCK = 1; | ||
| 425 | pub const F_UNLCK = 2; | ||
| 426 | |||
| 427 | pub const LOCK_SH = 1; | ||
| 428 | pub const LOCK_EX = 2; | ||
| 429 | pub const LOCK_UN = 8; | ||
| 430 | pub const LOCK_NB = 4; | ||
| 431 | |||
| 422 | pub const F_SETOWN_EX = 15; | 432 | pub const F_SETOWN_EX = 15; |
| 423 | pub const F_GETOWN_EX = 16; | 433 | pub const F_GETOWN_EX = 16; |
| 424 | 434 | ||
| ... | @@ -464,6 +474,16 @@ pub const SO_RCVBUFFORCE = 33; | ... | @@ -464,6 +474,16 @@ pub const SO_RCVBUFFORCE = 33; |
| 464 | pub const VDSO_CGT_SYM = "__kernel_clock_gettime"; | 474 | pub const VDSO_CGT_SYM = "__kernel_clock_gettime"; |
| 465 | pub const VDSO_CGT_VER = "LINUX_2.6.39"; | 475 | pub const VDSO_CGT_VER = "LINUX_2.6.39"; |
| 466 | 476 | ||
| 477 | pub 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 | |||
| 467 | pub const blksize_t = i32; | 487 | pub const blksize_t = i32; |
| 468 | pub const nlink_t = u32; | 488 | pub const nlink_t = u32; |
| 469 | pub const time_t = isize; | 489 | pub const time_t = isize; |
lib/std/os/bits/linux/riscv64.zig+19| ... | @@ -2,6 +2,7 @@ | ... | @@ -2,6 +2,7 @@ |
| 2 | const std = @import("../../../std.zig"); | 2 | const std = @import("../../../std.zig"); |
| 3 | const uid_t = std.os.linux.uid_t; | 3 | const uid_t = std.os.linux.uid_t; |
| 4 | const gid_t = std.os.linux.gid_t; | 4 | const gid_t = std.os.linux.gid_t; |
| 5 | const pid_t = std.os.linux.pid_t; | ||
| 5 | 6 | ||
| 6 | pub const SYS = extern enum(usize) { | 7 | pub const SYS = extern enum(usize) { |
| 7 | io_setup = 0, | 8 | io_setup = 0, |
| ... | @@ -338,6 +339,15 @@ pub const F_GETOWN = 9; | ... | @@ -338,6 +339,15 @@ pub const F_GETOWN = 9; |
| 338 | pub const F_SETSIG = 10; | 339 | pub const F_SETSIG = 10; |
| 339 | pub const F_GETSIG = 11; | 340 | pub const F_GETSIG = 11; |
| 340 | 341 | ||
| 342 | pub const F_RDLCK = 0; | ||
| 343 | pub const F_WRLCK = 1; | ||
| 344 | pub const F_UNLCK = 2; | ||
| 345 | |||
| 346 | pub const LOCK_SH = 1; | ||
| 347 | pub const LOCK_EX = 2; | ||
| 348 | pub const LOCK_UN = 8; | ||
| 349 | pub const LOCK_NB = 4; | ||
| 350 | |||
| 341 | pub const F_SETOWN_EX = 15; | 351 | pub const F_SETOWN_EX = 15; |
| 342 | pub const F_GETOWN_EX = 16; | 352 | pub const F_GETOWN_EX = 16; |
| 343 | 353 | ||
| ... | @@ -356,6 +366,15 @@ pub const timespec = extern struct { | ... | @@ -356,6 +366,15 @@ pub const timespec = extern struct { |
| 356 | tv_nsec: isize, | 366 | tv_nsec: isize, |
| 357 | }; | 367 | }; |
| 358 | 368 | ||
| 369 | pub 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 | |||
| 359 | /// Renamed to Stat to not conflict with the stat function. | 378 | /// Renamed to Stat to not conflict with the stat function. |
| 360 | /// atime, mtime, and ctime have functions to return `timespec`, | 379 | /// atime, mtime, and ctime have functions to return `timespec`, |
| 361 | /// because although this is a POSIX API, the layout and names of | 380 | /// 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; | ... | @@ -462,6 +462,23 @@ pub const REG_TRAPNO = 20; |
| 462 | pub const REG_OLDMASK = 21; | 462 | pub const REG_OLDMASK = 21; |
| 463 | pub const REG_CR2 = 22; | 463 | pub const REG_CR2 = 22; |
| 464 | 464 | ||
| 465 | pub const LOCK_SH = 1; | ||
| 466 | pub const LOCK_EX = 2; | ||
| 467 | pub const LOCK_UN = 8; | ||
| 468 | pub const LOCK_NB = 4; | ||
| 469 | |||
| 470 | pub const F_RDLCK = 0; | ||
| 471 | pub const F_WRLCK = 1; | ||
| 472 | pub const F_UNLCK = 2; | ||
| 473 | |||
| 474 | pub 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 | |||
| 465 | pub const msghdr = extern struct { | 482 | pub const msghdr = extern struct { |
| 466 | msg_name: ?*sockaddr, | 483 | msg_name: ?*sockaddr, |
| 467 | msg_namelen: socklen_t, | 484 | msg_namelen: socklen_t, |
lib/std/os/bits/netbsd.zig+17| ... | @@ -35,6 +35,14 @@ pub const dl_phdr_info = extern struct { | ... | @@ -35,6 +35,14 @@ pub const dl_phdr_info = extern struct { |
| 35 | dlpi_phnum: u16, | 35 | dlpi_phnum: u16, |
| 36 | }; | 36 | }; |
| 37 | 37 | ||
| 38 | pub 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 | |||
| 38 | pub const addrinfo = extern struct { | 46 | pub const addrinfo = extern struct { |
| 39 | flags: i32, | 47 | flags: i32, |
| 40 | family: i32, | 48 | family: i32, |
| ... | @@ -435,6 +443,15 @@ pub const F_GETLK = 7; | ... | @@ -435,6 +443,15 @@ pub const F_GETLK = 7; |
| 435 | pub const F_SETLK = 8; | 443 | pub const F_SETLK = 8; |
| 436 | pub const F_SETLKW = 9; | 444 | pub const F_SETLKW = 9; |
| 437 | 445 | ||
| 446 | pub const F_RDLCK = 1; | ||
| 447 | pub const F_WRLCK = 3; | ||
| 448 | pub const F_UNLCK = 2; | ||
| 449 | |||
| 450 | pub const LOCK_SH = 1; | ||
| 451 | pub const LOCK_EX = 2; | ||
| 452 | pub const LOCK_UN = 8; | ||
| 453 | pub const LOCK_NB = 4; | ||
| 454 | |||
| 438 | pub const FD_CLOEXEC = 1; | 455 | pub const FD_CLOEXEC = 1; |
| 439 | 456 | ||
| 440 | pub const SEEK_SET = 0; | 457 | pub 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 { | ... | @@ -592,6 +592,10 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) usize { |
| 592 | return syscall3(.fcntl, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, cmd)), arg); | 592 | return syscall3(.fcntl, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, cmd)), arg); |
| 593 | } | 593 | } |
| 594 | 594 | ||
| 595 | pub fn flock(fd: fd_t, operation: i32) usize { | ||
| 596 | return syscall2(.flock, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, operation))); | ||
| 597 | } | ||
| 598 | |||
| 595 | var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime); | 599 | var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime); |
| 596 | 600 | ||
| 597 | // We must follow the C calling convention when we call into the VDSO | 601 | // 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{ | ... | @@ -98,6 +98,7 @@ pub const OpenError = error{ |
| 98 | PathAlreadyExists, | 98 | PathAlreadyExists, |
| 99 | Unexpected, | 99 | Unexpected, |
| 100 | NameTooLong, | 100 | NameTooLong, |
| 101 | WouldBlock, | ||
| 101 | }; | 102 | }; |
| 102 | 103 | ||
| 103 | /// TODO rename to CreateFileW | 104 | /// TODO rename to CreateFileW |
| ... | @@ -107,6 +108,8 @@ pub fn OpenFileW( | ... | @@ -107,6 +108,8 @@ pub fn OpenFileW( |
| 107 | sub_path_w: [*:0]const u16, | 108 | sub_path_w: [*:0]const u16, |
| 108 | sa: ?*SECURITY_ATTRIBUTES, | 109 | sa: ?*SECURITY_ATTRIBUTES, |
| 109 | access_mask: ACCESS_MASK, | 110 | access_mask: ACCESS_MASK, |
| 111 | share_access_opt: ?ULONG, | ||
| 112 | share_access_nonblocking: bool, | ||
| 110 | creation: ULONG, | 113 | creation: ULONG, |
| 111 | ) OpenError!HANDLE { | 114 | ) OpenError!HANDLE { |
| 112 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { | 115 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { |
| ... | @@ -135,32 +138,46 @@ pub fn OpenFileW( | ... | @@ -135,32 +138,46 @@ pub fn OpenFileW( |
| 135 | .SecurityQualityOfService = null, | 138 | .SecurityQualityOfService = null, |
| 136 | }; | 139 | }; |
| 137 | var io: IO_STATUS_BLOCK = undefined; | 140 | var io: IO_STATUS_BLOCK = undefined; |
| 138 | const rc = ntdll.NtCreateFile( | 141 | const share_access = share_access_opt orelse (FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE); |
| 139 | &result, | 142 | |
| 140 | access_mask, | 143 | var delay: usize = 1; |
| 141 | &attr, | 144 | while (true) { |
| 142 | &io, | 145 | const rc = ntdll.NtCreateFile( |
| 143 | null, | 146 | &result, |
| 144 | FILE_ATTRIBUTE_NORMAL, | 147 | access_mask, |
| 145 | FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, | 148 | &attr, |
| 146 | creation, | 149 | &io, |
| 147 | FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, | 150 | null, |
| 148 | null, | 151 | FILE_ATTRIBUTE_NORMAL, |
| 149 | 0, | 152 | share_access, |
| 150 | ); | 153 | creation, |
| 151 | switch (rc) { | 154 | FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, |
| 152 | .SUCCESS => return result, | 155 | null, |
| 153 | .OBJECT_NAME_INVALID => unreachable, | 156 | 0, |
| 154 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, | 157 | ); |
| 155 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, | 158 | switch (rc) { |
| 156 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, | 159 | .SUCCESS => return result, |
| 157 | .INVALID_PARAMETER => unreachable, | 160 | .OBJECT_NAME_INVALID => unreachable, |
| 158 | .SHARING_VIOLATION => return error.SharingViolation, | 161 | .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, |
| 159 | .ACCESS_DENIED => return error.AccessDenied, | 162 | .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, |
| 160 | .PIPE_BUSY => return error.PipeBusy, | 163 | .NO_MEDIA_IN_DEVICE => return error.NoDevice, |
| 161 | .OBJECT_PATH_SYNTAX_BAD => unreachable, | 164 | .INVALID_PARAMETER => unreachable, |
| 162 | .OBJECT_NAME_COLLISION => return error.PathAlreadyExists, | 165 | .SHARING_VIOLATION => { |
| 163 | else => return unexpectedStatus(rc), | 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 | } | ||
| 164 | } | 181 | } |
| 165 | } | 182 | } |
| 166 | 183 |
lib/std/zig/system.zig+2| ... | @@ -468,6 +468,8 @@ pub const NativeTargetInfo = struct { | ... | @@ -468,6 +468,8 @@ pub const NativeTargetInfo = struct { |
| 468 | error.InvalidUtf8 => unreachable, | 468 | error.InvalidUtf8 => unreachable, |
| 469 | error.BadPathName => unreachable, | 469 | error.BadPathName => unreachable, |
| 470 | error.PipeBusy => unreachable, | 470 | error.PipeBusy => unreachable, |
| 471 | error.FileLocksNotSupported => unreachable, | ||
| 472 | error.WouldBlock => unreachable, | ||
| 471 | 473 | ||
| 472 | error.IsDir, | 474 | error.IsDir, |
| 473 | error.NotDir, | 475 | error.NotDir, |
src-self-hosted/stage2.zig+3| ... | @@ -116,6 +116,8 @@ const Error = extern enum { | ... | @@ -116,6 +116,8 @@ const Error = extern enum { |
| 116 | UnknownClangOption, | 116 | UnknownClangOption, |
| 117 | NestedResponseFile, | 117 | NestedResponseFile, |
| 118 | ZigIsTheCCompiler, | 118 | ZigIsTheCCompiler, |
| 119 | FileBusy, | ||
| 120 | Locked, | ||
| 119 | }; | 121 | }; |
| 120 | 122 | ||
| 121 | const FILE = std.c.FILE; | 123 | const FILE = std.c.FILE; |
| ... | @@ -847,6 +849,7 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [ | ... | @@ -847,6 +849,7 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [ |
| 847 | error.NoDevice => return .NoDevice, | 849 | error.NoDevice => return .NoDevice, |
| 848 | error.NotDir => return .NotDir, | 850 | error.NotDir => return .NotDir, |
| 849 | error.DeviceBusy => return .DeviceBusy, | 851 | error.DeviceBusy => return .DeviceBusy, |
| 852 | error.FileLocksNotSupported => unreachable, | ||
| 850 | }; | 853 | }; |
| 851 | stage1_libc.initFromStage2(libc); | 854 | stage1_libc.initFromStage2(libc); |
| 852 | return .None; | 855 | return .None; |
src/error.cpp+2| ... | @@ -86,6 +86,8 @@ const char *err_str(Error err) { | ... | @@ -86,6 +86,8 @@ const char *err_str(Error err) { |
| 86 | case ErrorUnknownClangOption: return "unknown Clang option"; | 86 | case ErrorUnknownClangOption: return "unknown Clang option"; |
| 87 | case ErrorNestedResponseFile: return "nested response file"; | 87 | case ErrorNestedResponseFile: return "nested response file"; |
| 88 | 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."; | 88 | 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"; | ||
| 89 | } | 91 | } |
| 90 | return "(invalid error)"; | 92 | return "(invalid error)"; |
| 91 | } | 93 | } |
src/stage2.h+2| ... | @@ -108,6 +108,8 @@ enum Error { | ... | @@ -108,6 +108,8 @@ enum Error { |
| 108 | ErrorUnknownClangOption, | 108 | ErrorUnknownClangOption, |
| 109 | ErrorNestedResponseFile, | 109 | ErrorNestedResponseFile, |
| 110 | ErrorZigIsTheCCompiler, | 110 | ErrorZigIsTheCCompiler, |
| 111 | ErrorFileBusy, | ||
| 112 | ErrorLocked, | ||
| 111 | }; | 113 | }; |
| 112 | 114 | ||
| 113 | // ABI warning | 115 | // ABI warning |