authorgravatar for leroycepearson@geemili.xyzLeRoyce Pearson <leroycepearson@geemili.xyz> 2020-04-02 22:57:02-06:00
committergravatar for leroycepearson@geemili.xyzLeRoyce Pearson <leroycepearson@geemili.xyz> 2020-04-02 22:57:02-06:00
logea6525797d0db83a8560179b317837cb58634049
tree48a190000f83ba3303a9a3cae797430303c883b9
parente7cf3f92a98cd2bd94d9d94fb361819b6ab89e8d

Use `flock` instead of `fcntl` to lock files

`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,6 +122,7 @@ pub extern "c" fn sysctlnametomib(name: [*:0]const u8, mibp: ?*c_int, sizep: ?*u
122pub extern "c" fn tcgetattr(fd: fd_t, termios_p: *termios) c_int;122pub extern "c" fn tcgetattr(fd: fd_t, termios_p: *termios) c_int;
123pub extern "c" fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) c_int;123pub extern "c" fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) c_int;
124pub extern "c" fn fcntl(fd: fd_t, cmd: c_int, ...) c_int;124pub 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;
125pub extern "c" fn uname(buf: *utsname) c_int;126pub extern "c" fn uname(buf: *utsname) c_int;
126127
127pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int;128pub 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,14 +617,9 @@ pub const Dir = struct {
617 else617 else
618 try os.openatZ(self.fd, sub_path, os_flags, 0);618 try os.openatZ(self.fd, sub_path, os_flags, 0);
619619
620 // use fcntl file locking if no lock flag was given
621 if (flags.lock and lock_flag == 0) {620 if (flags.lock and lock_flag == 0) {
622 // TODO: integrate async I/O621 // TODO: integrate async I/O
623 // mem.zeroes is used here because flock's structure can vary across architectures and systems622 _ = try os.flock(fd, if (flags.write) os.LOCK_EX else os.LOCK_SH);
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));
628 }623 }
629624
630 return File{625 return File{
...@@ -695,10 +690,7 @@ pub const Dir = struct {...@@ -695,10 +690,7 @@ pub const Dir = struct {
695 if (flags.lock and lock_flag == 0) {690 if (flags.lock and lock_flag == 0) {
696 // TODO: integrate async I/O691 // TODO: integrate async I/O
697 // mem.zeroes is used here because flock's structure can vary across architectures and systems692 // mem.zeroes is used here because flock's structure can vary across architectures and systems
698 var flock = mem.zeroes(os.Flock);693 _ = try os.flock(fd, os.LOCK_EX);
699 flock.l_type = os.F_WRLCK;
700 flock.l_whence = os.SEEK_SET;
701 _ = try os.fcntl(fd, os.F_SETLKW, @ptrToInt(&flock));
702 }694 }
703695
704 return File{ .handle = fd, .io_mode = .blocking };696 return File{ .handle = fd, .io_mode = .blocking };
...@@ -1801,59 +1793,14 @@ const FileLockTestContext = struct {...@@ -1801,59 +1793,14 @@ const FileLockTestContext = struct {
1801};1793};
18021794
1803fn run_lock_file_test(contexts: []FileLockTestContext) !void {1795fn run_lock_file_test(contexts: []FileLockTestContext) !void {
1804 var shared_mem: if (builtin.os.tag == .windows) void else []align(mem.page_size) u8 = undefined;1796 var threads = std.ArrayList(*std.Thread).init(std.testing.allocator);
18051797 defer {
1806 var ctxs: []FileLockTestContext = undefined;1798 for (threads.toSlice()) |thread| {
1807 if (builtin.os.tag == .windows) {1799 thread.wait();
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 }
1849 }1800 }
1801 threads.deinit();
1850 }1802 }
18511803 for (contexts) |*ctx, idx| {
1852 if (builtin.os.tag != .windows) {1804 try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run));
1853 // Copy contexts out of shared memory
1854 for (ctxs) |ctx, idx| {
1855 contexts[idx] = ctx;
1856 }
1857 std.os.munmap(shared_mem);
1858 }1805 }
1859}1806}
lib/std/fs/file.zig+1-1
...@@ -34,7 +34,7 @@ pub const File = struct {...@@ -34,7 +34,7 @@ pub const File = struct {
34 else => 0o666,34 else => 0o666,
35 };35 };
3636
37 pub const OpenError = windows.CreateFileError || os.OpenError || os.FcntlError;37 pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError;
3838
39 /// TODO https://github.com/ziglang/zig/issues/380239 /// TODO https://github.com/ziglang/zig/issues/3802
40 pub const OpenFlags = struct {40 pub const OpenFlags = struct {
lib/std/os.zig+23-9
...@@ -819,36 +819,28 @@ pub const OpenError = error{...@@ -819,36 +819,28 @@ pub const OpenError = error{
819 SystemFdQuotaExceeded,819 SystemFdQuotaExceeded,
820 NoDevice,820 NoDevice,
821 FileNotFound,821 FileNotFound,
822
823 /// The path exceeded `MAX_PATH_BYTES` bytes.822 /// The path exceeded `MAX_PATH_BYTES` bytes.
824 NameTooLong,823 NameTooLong,
825
826 /// Insufficient kernel memory was available, or824 /// Insufficient kernel memory was available, or
827 /// the named file is a FIFO and per-user hard limit on825 /// the named file is a FIFO and per-user hard limit on
828 /// memory allocation for pipes has been reached.826 /// memory allocation for pipes has been reached.
829 SystemResources,827 SystemResources,
830
831 /// The file is too large to be opened. This error is unreachable828 /// The file is too large to be opened. This error is unreachable
832 /// for 64-bit targets, as well as when opening directories.829 /// for 64-bit targets, as well as when opening directories.
833 FileTooBig,830 FileTooBig,
834
835 /// The path refers to directory but the `O_DIRECTORY` flag was not provided.831 /// The path refers to directory but the `O_DIRECTORY` flag was not provided.
836 IsDir,832 IsDir,
837
838 /// A new path cannot be created because the device has no room for the new file.833 /// A new path cannot be created because the device has no room for the new file.
839 /// This error is only reachable when the `O_CREAT` flag is provided.834 /// This error is only reachable when the `O_CREAT` flag is provided.
840 NoSpaceLeft,835 NoSpaceLeft,
841
842 /// A component used as a directory in the path was not, in fact, a directory, or836 /// A component used as a directory in the path was not, in fact, a directory, or
843 /// `O_DIRECTORY` was specified and the path was not a directory.837 /// `O_DIRECTORY` was specified and the path was not a directory.
844 NotDir,838 NotDir,
845
846 /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided.839 /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided.
847 PathAlreadyExists,840 PathAlreadyExists,
848 DeviceBusy,841 DeviceBusy,
849
850 /// The underlying filesystem does not support file locks842 /// The underlying filesystem does not support file locks
851 FileLocksNotSupported843 FileLocksNotSupported,
852} || UnexpectedError;844} || UnexpectedError;
853845
854/// Open and possibly create a file. Keeps trying if it gets interrupted.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,6 +3214,28 @@ pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
3222 }3214 }
3223}3215}
32243216
3217pub const FlockError = error{
3218 WouldBlock,
3219
3220 /// The kernel ran out of memory for allocating file locks
3221 SystemResources,
3222} || UnexpectedError;
3223
3224pub 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
3225pub const RealPathError = error{3239pub const RealPathError = error{
3226 FileNotFound,3240 FileNotFound,
3227 AccessDenied,3241 AccessDenied,
lib/std/os/bits/darwin.zig+5
...@@ -1394,3 +1394,8 @@ pub const F_UNLCK = 2;...@@ -1394,3 +1394,8 @@ pub const F_UNLCK = 2;
13941394
1395/// exclusive or write lock1395/// exclusive or write lock
1396pub const F_WRLCK = 3;1396pub 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/linux/arm-eabi.zig+5
...@@ -462,6 +462,11 @@ pub const F_GETOWN_EX = 16;...@@ -462,6 +462,11 @@ pub const F_GETOWN_EX = 16;
462462
463pub const F_GETOWNER_UIDS = 17;463pub const F_GETOWNER_UIDS = 17;
464464
465pub const LOCK_SH = 1;
466pub const LOCK_EX = 2;
467pub const LOCK_UN = 8;
468pub const LOCK_NB = 4;
469
465/// stack-like segment470/// stack-like segment
466pub const MAP_GROWSDOWN = 0x0100;471pub const MAP_GROWSDOWN = 0x0100;
467472
lib/std/os/bits/linux/arm64.zig+5
...@@ -349,6 +349,11 @@ pub const F_RDLCK = 0;...@@ -349,6 +349,11 @@ pub const F_RDLCK = 0;
349pub const F_WRLCK = 1;349pub const F_WRLCK = 1;
350pub const F_UNLCK = 2;350pub const F_UNLCK = 2;
351351
352pub const LOCK_SH = 1;
353pub const LOCK_EX = 2;
354pub const LOCK_UN = 8;
355pub const LOCK_NB = 4;
356
352pub const F_SETOWN_EX = 15;357pub const F_SETOWN_EX = 15;
353pub const F_GETOWN_EX = 16;358pub const F_GETOWN_EX = 16;
354359
lib/std/os/bits/linux/i386.zig+5
...@@ -482,6 +482,11 @@ pub const F_RDLCK = 0;...@@ -482,6 +482,11 @@ pub const F_RDLCK = 0;
482pub const F_WRLCK = 1;482pub const F_WRLCK = 1;
483pub const F_UNLCK = 2;483pub const F_UNLCK = 2;
484484
485pub const LOCK_SH = 1;
486pub const LOCK_EX = 2;
487pub const LOCK_UN = 8;
488pub const LOCK_NB = 4;
489
485pub const F_SETOWN_EX = 15;490pub const F_SETOWN_EX = 15;
486pub const F_GETOWN_EX = 16;491pub const F_GETOWN_EX = 16;
487492
lib/std/os/bits/linux/mipsel.zig+5
...@@ -424,6 +424,11 @@ pub const F_RDLCK = 0;...@@ -424,6 +424,11 @@ pub const F_RDLCK = 0;
424pub const F_WRLCK = 1;424pub const F_WRLCK = 1;
425pub const F_UNLCK = 2;425pub const F_UNLCK = 2;
426426
427pub const LOCK_SH = 1;
428pub const LOCK_EX = 2;
429pub const LOCK_UN = 8;
430pub const LOCK_NB = 4;
431
427pub const F_SETOWN_EX = 15;432pub const F_SETOWN_EX = 15;
428pub const F_GETOWN_EX = 16;433pub const F_GETOWN_EX = 16;
429434
lib/std/os/bits/linux/riscv64.zig+5
...@@ -343,6 +343,11 @@ pub const F_RDLCK = 0;...@@ -343,6 +343,11 @@ pub const F_RDLCK = 0;
343pub const F_WRLCK = 1;343pub const F_WRLCK = 1;
344pub const F_UNLCK = 2;344pub const F_UNLCK = 2;
345345
346pub const LOCK_SH = 1;
347pub const LOCK_EX = 2;
348pub const LOCK_UN = 8;
349pub const LOCK_NB = 4;
350
346pub const F_SETOWN_EX = 15;351pub const F_SETOWN_EX = 15;
347pub const F_GETOWN_EX = 16;352pub const F_GETOWN_EX = 16;
348353
lib/std/os/bits/linux/x86_64.zig+5
...@@ -462,6 +462,11 @@ pub const REG_TRAPNO = 20;...@@ -462,6 +462,11 @@ pub const REG_TRAPNO = 20;
462pub const REG_OLDMASK = 21;462pub const REG_OLDMASK = 21;
463pub const REG_CR2 = 22;463pub 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
465pub const F_RDLCK = 0;470pub const F_RDLCK = 0;
466pub const F_WRLCK = 1;471pub const F_WRLCK = 1;
467pub const F_UNLCK = 2;472pub 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,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}
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
595var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime);599var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime);
596600
597// We must follow the C calling convention when we call into the VDSO601// We must follow the C calling convention when we call into the VDSO