| author | |
| committer | |
| log | ea6525797d0db83a8560179b317837cb58634049 |
| tree | 48a190000f83ba3303a9a3cae797430303c883b9 |
| parent | e7cf3f92a98cd2bd94d9d94fb361819b6ab89e8d |
`flock` locks based on the file handle, instead of the process id.
This brings the file locking on unix based systems closer to file
locking on Windows.12 files changed, 73 insertions(+), 72 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 | 122 | pub extern "c" fn tcgetattr(fd: fd_t, termios_p: *termios) c_int; |
| 123 | 123 | pub extern "c" fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) c_int; |
| 124 | 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 | 126 | pub extern "c" fn uname(buf: *utsname) c_int; |
| 126 | 127 | |
| 127 | 128 | pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int; |
lib/std/fs.zig+9-62| ... | ... | @@ -617,14 +617,9 @@ pub const Dir = struct { |
| 617 | 617 | else |
| 618 | 618 | try os.openatZ(self.fd, sub_path, os_flags, 0); |
| 619 | 619 | |
| 620 | // use fcntl file locking if no lock flag was given | |
| 621 | 620 | if (flags.lock and lock_flag == 0) { |
| 622 | 621 | // TODO: integrate async I/O |
| 623 | // mem.zeroes is used here because flock's structure can vary across architectures and systems | |
| 624 | var flock = mem.zeroes(os.Flock); | |
| 625 | flock.l_type = if (flags.write) os.F_WRLCK else os.F_RDLCK; | |
| 626 | flock.l_whence = os.SEEK_SET; | |
| 627 | _ = try os.fcntl(fd, os.F_SETLKW, @ptrToInt(&flock)); | |
| 622 | _ = try os.flock(fd, if (flags.write) os.LOCK_EX else os.LOCK_SH); | |
| 628 | 623 | } |
| 629 | 624 | |
| 630 | 625 | return File{ |
| ... | ... | @@ -695,10 +690,7 @@ pub const Dir = struct { |
| 695 | 690 | if (flags.lock and lock_flag == 0) { |
| 696 | 691 | // TODO: integrate async I/O |
| 697 | 692 | // mem.zeroes is used here because flock's structure can vary across architectures and systems |
| 698 | var flock = mem.zeroes(os.Flock); | |
| 699 | flock.l_type = os.F_WRLCK; | |
| 700 | flock.l_whence = os.SEEK_SET; | |
| 701 | _ = try os.fcntl(fd, os.F_SETLKW, @ptrToInt(&flock)); | |
| 693 | _ = try os.flock(fd, os.LOCK_EX); | |
| 702 | 694 | } |
| 703 | 695 | |
| 704 | 696 | return File{ .handle = fd, .io_mode = .blocking }; |
| ... | ... | @@ -1801,59 +1793,14 @@ const FileLockTestContext = struct { |
| 1801 | 1793 | }; |
| 1802 | 1794 | |
| 1803 | 1795 | fn run_lock_file_test(contexts: []FileLockTestContext) !void { |
| 1804 | var shared_mem: if (builtin.os.tag == .windows) void else []align(mem.page_size) u8 = undefined; | |
| 1805 | ||
| 1806 | var ctxs: []FileLockTestContext = undefined; | |
| 1807 | if (builtin.os.tag == .windows) { | |
| 1808 | ctxs = contexts; | |
| 1809 | } else { | |
| 1810 | shared_mem = try std.os.mmap(null, contexts.len * @sizeOf(FileLockTestContext), std.os.PROT_READ | std.os.PROT_WRITE, std.os.MAP_SHARED | std.os.MAP_ANONYMOUS, -1, 0); | |
| 1811 | const ctxs_ptr = @ptrCast([*]FileLockTestContext, shared_mem.ptr); | |
| 1812 | ctxs = ctxs_ptr[0..contexts.len]; | |
| 1813 | ||
| 1814 | for (contexts) |context, idx| { | |
| 1815 | ctxs[idx] = context; | |
| 1816 | } | |
| 1817 | } | |
| 1818 | ||
| 1819 | if (builtin.os.tag == .windows) { | |
| 1820 | var threads = std.ArrayList(*std.Thread).init(std.testing.allocator); | |
| 1821 | defer { | |
| 1822 | for (threads.toSlice()) |thread| { | |
| 1823 | thread.wait(); | |
| 1824 | } | |
| 1825 | threads.deinit(); | |
| 1826 | } | |
| 1827 | for (ctxs) |*ctx, idx| { | |
| 1828 | try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run)); | |
| 1829 | } | |
| 1830 | } else { | |
| 1831 | var ctx_opt: ?*FileLockTestContext = null; | |
| 1832 | for (ctxs) |*ctx| { | |
| 1833 | const childpid = try std.os.fork(); | |
| 1834 | if (childpid == 0) { | |
| 1835 | ctx_opt = ctx; | |
| 1836 | break; | |
| 1837 | } | |
| 1838 | ctx.pid = childpid; | |
| 1839 | } | |
| 1840 | ||
| 1841 | if (ctx_opt) |ctx| { | |
| 1842 | ctx.run(); | |
| 1843 | // Exit so we don't have duplicate test processes | |
| 1844 | std.os.exit(0); | |
| 1845 | } else { | |
| 1846 | for (ctxs) |ctx| { | |
| 1847 | _ = std.os.waitpid(ctx.pid.?, 0); | |
| 1848 | } | |
| 1796 | var threads = std.ArrayList(*std.Thread).init(std.testing.allocator); | |
| 1797 | defer { | |
| 1798 | for (threads.toSlice()) |thread| { | |
| 1799 | thread.wait(); | |
| 1849 | 1800 | } |
| 1801 | threads.deinit(); | |
| 1850 | 1802 | } |
| 1851 | ||
| 1852 | if (builtin.os.tag != .windows) { | |
| 1853 | // Copy contexts out of shared memory | |
| 1854 | for (ctxs) |ctx, idx| { | |
| 1855 | contexts[idx] = ctx; | |
| 1856 | } | |
| 1857 | std.os.munmap(shared_mem); | |
| 1803 | for (contexts) |*ctx, idx| { | |
| 1804 | try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run)); | |
| 1858 | 1805 | } |
| 1859 | 1806 | } |
lib/std/fs/file.zig+1-1| ... | ... | @@ -34,7 +34,7 @@ pub const File = struct { |
| 34 | 34 | else => 0o666, |
| 35 | 35 | }; |
| 36 | 36 | |
| 37 | pub const OpenError = windows.CreateFileError || os.OpenError || os.FcntlError; | |
| 37 | pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError; | |
| 38 | 38 | |
| 39 | 39 | /// TODO https://github.com/ziglang/zig/issues/3802 |
| 40 | 40 | pub const OpenFlags = struct { |
lib/std/os.zig+23-9| ... | ... | @@ -819,36 +819,28 @@ pub const OpenError = error{ |
| 819 | 819 | SystemFdQuotaExceeded, |
| 820 | 820 | NoDevice, |
| 821 | 821 | FileNotFound, |
| 822 | ||
| 823 | 822 | /// The path exceeded `MAX_PATH_BYTES` bytes. |
| 824 | 823 | NameTooLong, |
| 825 | ||
| 826 | 824 | /// Insufficient kernel memory was available, or |
| 827 | 825 | /// the named file is a FIFO and per-user hard limit on |
| 828 | 826 | /// memory allocation for pipes has been reached. |
| 829 | 827 | SystemResources, |
| 830 | ||
| 831 | 828 | /// The file is too large to be opened. This error is unreachable |
| 832 | 829 | /// for 64-bit targets, as well as when opening directories. |
| 833 | 830 | FileTooBig, |
| 834 | ||
| 835 | 831 | /// The path refers to directory but the `O_DIRECTORY` flag was not provided. |
| 836 | 832 | IsDir, |
| 837 | ||
| 838 | 833 | /// A new path cannot be created because the device has no room for the new file. |
| 839 | 834 | /// This error is only reachable when the `O_CREAT` flag is provided. |
| 840 | 835 | NoSpaceLeft, |
| 841 | ||
| 842 | 836 | /// A component used as a directory in the path was not, in fact, a directory, or |
| 843 | 837 | /// `O_DIRECTORY` was specified and the path was not a directory. |
| 844 | 838 | NotDir, |
| 845 | ||
| 846 | 839 | /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided. |
| 847 | 840 | PathAlreadyExists, |
| 848 | 841 | DeviceBusy, |
| 849 | ||
| 850 | 842 | /// The underlying filesystem does not support file locks |
| 851 | FileLocksNotSupported | |
| 843 | FileLocksNotSupported, | |
| 852 | 844 | } || UnexpectedError; |
| 853 | 845 | |
| 854 | 846 | /// Open and possibly create a file. Keeps trying if it gets interrupted. |
| ... | ... | @@ -3222,6 +3214,28 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize { |
| 3222 | 3214 | } |
| 3223 | 3215 | } |
| 3224 | 3216 | |
| 3217 | pub const FlockError = error{ | |
| 3218 | WouldBlock, | |
| 3219 | ||
| 3220 | /// The kernel ran out of memory for allocating file locks | |
| 3221 | SystemResources, | |
| 3222 | } || UnexpectedError; | |
| 3223 | ||
| 3224 | pub fn flock(fd: fd_t, operation: i32) FlockError!usize { | |
| 3225 | while (true) { | |
| 3226 | const rc = system.flock(fd, operation); | |
| 3227 | switch (errno(rc)) { | |
| 3228 | 0 => return @intCast(usize, rc), | |
| 3229 | EBADF => unreachable, | |
| 3230 | EINTR => continue, | |
| 3231 | EINVAL => unreachable, // invalid parameters | |
| 3232 | ENOLCK => return error.SystemResources, | |
| 3233 | EWOULDBLOCK => return error.WouldBlock, // TODO: integrate with async instead of just returning an error | |
| 3234 | else => |err| return unexpectedErrno(err), | |
| 3235 | } | |
| 3236 | } | |
| 3237 | } | |
| 3238 | ||
| 3225 | 3239 | pub const RealPathError = error{ |
| 3226 | 3240 | FileNotFound, |
| 3227 | 3241 | AccessDenied, |
lib/std/os/bits/darwin.zig+5| ... | ... | @@ -1394,3 +1394,8 @@ pub const F_UNLCK = 2; |
| 1394 | 1394 | |
| 1395 | 1395 | /// exclusive or write lock |
| 1396 | 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/linux/arm-eabi.zig+5| ... | ... | @@ -462,6 +462,11 @@ pub const F_GETOWN_EX = 16; |
| 462 | 462 | |
| 463 | 463 | pub const F_GETOWNER_UIDS = 17; |
| 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 | ||
| 465 | 470 | /// stack-like segment |
| 466 | 471 | pub const MAP_GROWSDOWN = 0x0100; |
| 467 | 472 |
lib/std/os/bits/linux/arm64.zig+5| ... | ... | @@ -349,6 +349,11 @@ pub const F_RDLCK = 0; |
| 349 | 349 | pub const F_WRLCK = 1; |
| 350 | 350 | pub const F_UNLCK = 2; |
| 351 | 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 | ||
| 352 | 357 | pub const F_SETOWN_EX = 15; |
| 353 | 358 | pub const F_GETOWN_EX = 16; |
| 354 | 359 |
lib/std/os/bits/linux/i386.zig+5| ... | ... | @@ -482,6 +482,11 @@ pub const F_RDLCK = 0; |
| 482 | 482 | pub const F_WRLCK = 1; |
| 483 | 483 | pub const F_UNLCK = 2; |
| 484 | 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 | ||
| 485 | 490 | pub const F_SETOWN_EX = 15; |
| 486 | 491 | pub const F_GETOWN_EX = 16; |
| 487 | 492 |
lib/std/os/bits/linux/mipsel.zig+5| ... | ... | @@ -424,6 +424,11 @@ pub const F_RDLCK = 0; |
| 424 | 424 | pub const F_WRLCK = 1; |
| 425 | 425 | pub const F_UNLCK = 2; |
| 426 | 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 | ||
| 427 | 432 | pub const F_SETOWN_EX = 15; |
| 428 | 433 | pub const F_GETOWN_EX = 16; |
| 429 | 434 |
lib/std/os/bits/linux/riscv64.zig+5| ... | ... | @@ -343,6 +343,11 @@ pub const F_RDLCK = 0; |
| 343 | 343 | pub const F_WRLCK = 1; |
| 344 | 344 | pub const F_UNLCK = 2; |
| 345 | 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 | ||
| 346 | 351 | pub const F_SETOWN_EX = 15; |
| 347 | 352 | pub const F_GETOWN_EX = 16; |
| 348 | 353 |
lib/std/os/bits/linux/x86_64.zig+5| ... | ... | @@ -462,6 +462,11 @@ pub const REG_TRAPNO = 20; |
| 462 | 462 | pub const REG_OLDMASK = 21; |
| 463 | 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 | ||
| 465 | 470 | pub const F_RDLCK = 0; |
| 466 | 471 | pub const F_WRLCK = 1; |
| 467 | 472 | pub const F_UNLCK = 2; |
lib/std/os/linux.zig+4| ... | ... | @@ -592,6 +592,10 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) usize { |
| 592 | 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 | 599 | var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime); |
| 596 | 600 | |
| 597 | 601 | // We must follow the C calling convention when we call into the VDSO |