| ... | ... | @@ -654,63 +654,61 @@ test "realpath" { |
| 654 | 654 | testing.expectError(error.FileNotFound, fs.realpath("definitely_bogus_does_not_exist1234", &buf)); |
| 655 | 655 | } |
| 656 | 656 | |
| 657 | | const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.ns_per_ms; |
| 658 | | |
| 659 | 657 | test "open file with exclusive nonblocking lock twice" { |
| 660 | 658 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 661 | 659 | |
| 662 | 660 | // TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759 |
| 663 | 661 | if (builtin.os.tag == .freebsd) return error.SkipZigTest; |
| 664 | 662 | |
| 665 | | const dir = fs.cwd(); |
| 666 | 663 | const filename = "file_nonblocking_lock_test.txt"; |
| 667 | 664 | |
| 668 | | const file1 = try dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); |
| 665 | var tmp = tmpDir(.{}); |
| 666 | defer tmp.cleanup(); |
| 667 | |
| 668 | const file1 = try tmp.dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); |
| 669 | 669 | defer file1.close(); |
| 670 | 670 | |
| 671 | | const file2 = dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); |
| 672 | | std.debug.assert(std.meta.eql(file2, error.WouldBlock)); |
| 671 | const file2 = tmp.dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); |
| 672 | testing.expectError(error.WouldBlock, file2); |
| 673 | } |
| 673 | 674 | |
| 674 | | dir.deleteFile(filename) catch |err| switch (err) { |
| 675 | | error.FileNotFound => {}, |
| 676 | | else => return err, |
| 677 | | }; |
| 675 | test "open file with shared and exclusive nonblocking lock" { |
| 676 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 677 | |
| 678 | // TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759 |
| 679 | if (builtin.os.tag == .freebsd) return error.SkipZigTest; |
| 680 | |
| 681 | const filename = "file_nonblocking_lock_test.txt"; |
| 682 | |
| 683 | var tmp = tmpDir(.{}); |
| 684 | defer tmp.cleanup(); |
| 685 | |
| 686 | const file1 = try tmp.dir.createFile(filename, .{ .lock = .Shared, .lock_nonblocking = true }); |
| 687 | defer file1.close(); |
| 688 | |
| 689 | const file2 = tmp.dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); |
| 690 | testing.expectError(error.WouldBlock, file2); |
| 678 | 691 | } |
| 679 | 692 | |
| 680 | | test "open file with lock twice, make sure it wasn't open at the same time" { |
| 681 | | if (builtin.single_threaded) return error.SkipZigTest; |
| 693 | test "open file with exclusive and shared nonblocking lock" { |
| 694 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 682 | 695 | |
| 683 | | if (std.io.is_async) { |
| 684 | | // This test starts its own threads and is not compatible with async I/O. |
| 685 | | return error.SkipZigTest; |
| 686 | | } |
| 696 | // TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759 |
| 697 | if (builtin.os.tag == .freebsd) return error.SkipZigTest; |
| 687 | 698 | |
| 688 | | const filename = "file_lock_test.txt"; |
| 689 | | var contexts = [_]FileLockTestContext{ |
| 690 | | .{ .filename = filename, .create = true, .lock = .Exclusive }, |
| 691 | | .{ .filename = filename, .create = true, .lock = .Exclusive }, |
| 692 | | }; |
| 693 | | try run_lock_file_test(&contexts); |
| 694 | | |
| 695 | | // Check for an error |
| 696 | | var was_error = false; |
| 697 | | for (contexts) |context, idx| { |
| 698 | | if (context.err) |err| { |
| 699 | | was_error = true; |
| 700 | | std.debug.warn("\nError in context {}: {}\n", .{ idx, err }); |
| 701 | | } |
| 702 | | } |
| 703 | | if (was_error) builtin.panic("There was an error in contexts", null); |
| 699 | const filename = "file_nonblocking_lock_test.txt"; |
| 704 | 700 | |
| 705 | | std.debug.assert(!contexts[0].overlaps(&contexts[1])); |
| 701 | var tmp = tmpDir(.{}); |
| 702 | defer tmp.cleanup(); |
| 706 | 703 | |
| 707 | | fs.cwd().deleteFile(filename) catch |err| switch (err) { |
| 708 | | error.FileNotFound => {}, |
| 709 | | else => return err, |
| 710 | | }; |
| 704 | const file1 = try tmp.dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); |
| 705 | defer file1.close(); |
| 706 | |
| 707 | const file2 = tmp.dir.createFile(filename, .{ .lock = .Shared, .lock_nonblocking = true }); |
| 708 | testing.expectError(error.WouldBlock, file2); |
| 711 | 709 | } |
| 712 | 710 | |
| 713 | | test "create file, lock and read from multiple process at once" { |
| 711 | test "open file with exclusive lock twice, make sure it waits" { |
| 714 | 712 | if (builtin.single_threaded) return error.SkipZigTest; |
| 715 | 713 | |
| 716 | 714 | if (std.io.is_async) { |
| ... | ... | @@ -718,46 +716,37 @@ test "create file, lock and read from multiple process at once" { |
| 718 | 716 | return error.SkipZigTest; |
| 719 | 717 | } |
| 720 | 718 | |
| 721 | | if (true) { |
| 722 | | // https://github.com/ziglang/zig/issues/5006 |
| 723 | | return error.SkipZigTest; |
| 724 | | } |
| 719 | const filename = "file_lock_test.txt"; |
| 725 | 720 | |
| 726 | | const filename = "file_read_lock_test.txt"; |
| 727 | | const filedata = "Hello, world!\n"; |
| 721 | var tmp = tmpDir(.{}); |
| 722 | defer tmp.cleanup(); |
| 728 | 723 | |
| 729 | | try fs.cwd().writeFile(filename, filedata); |
| 724 | const file = try tmp.dir.createFile(filename, .{ .lock = .Exclusive }); |
| 725 | errdefer file.close(); |
| 730 | 726 | |
| 731 | | var contexts = [_]FileLockTestContext{ |
| 732 | | .{ .filename = filename, .create = false, .lock = .Shared }, |
| 733 | | .{ .filename = filename, .create = false, .lock = .Shared }, |
| 734 | | .{ .filename = filename, .create = false, .lock = .Exclusive }, |
| 727 | const S = struct { |
| 728 | const C = struct { dir: *fs.Dir, evt: *std.ResetEvent }; |
| 729 | fn checkFn(ctx: C) !void { |
| 730 | const file1 = try ctx.dir.createFile(filename, .{ .lock = .Exclusive }); |
| 731 | defer file1.close(); |
| 732 | ctx.evt.set(); |
| 733 | } |
| 735 | 734 | }; |
| 736 | 735 | |
| 737 | | try run_lock_file_test(&contexts); |
| 736 | var evt = std.ResetEvent.init(); |
| 737 | defer evt.deinit(); |
| 738 | 738 | |
| 739 | | var was_error = false; |
| 740 | | for (contexts) |context, idx| { |
| 741 | | if (context.err) |err| { |
| 742 | | was_error = true; |
| 743 | | std.debug.warn("\nError in context {}: {}\n", .{ idx, err }); |
| 744 | | } |
| 745 | | } |
| 746 | | if (was_error) builtin.panic("There was an error in contexts", null); |
| 739 | const t = try std.Thread.spawn(S.C{ .dir = &tmp.dir, .evt = &evt }, S.checkFn); |
| 740 | defer t.wait(); |
| 747 | 741 | |
| 748 | | std.debug.assert(contexts[0].overlaps(&contexts[1])); |
| 749 | | std.debug.assert(!contexts[2].overlaps(&contexts[0])); |
| 750 | | std.debug.assert(!contexts[2].overlaps(&contexts[1])); |
| 751 | | if (contexts[0].bytes_read.? != filedata.len) { |
| 752 | | std.debug.warn("\n bytes_read: {}, expected: {} \n", .{ contexts[0].bytes_read, filedata.len }); |
| 753 | | } |
| 754 | | std.debug.assert(contexts[0].bytes_read.? == filedata.len); |
| 755 | | std.debug.assert(contexts[1].bytes_read.? == filedata.len); |
| 742 | const SLEEP_TIMEOUT_NS = 10 * std.time.ns_per_ms; |
| 756 | 743 | |
| 757 | | fs.cwd().deleteFile(filename) catch |err| switch (err) { |
| 758 | | error.FileNotFound => {}, |
| 759 | | else => return err, |
| 760 | | }; |
| 744 | std.time.sleep(SLEEP_TIMEOUT_NS); |
| 745 | // Check that createFile is still waiting for the lock to be released. |
| 746 | testing.expect(!evt.isSet()); |
| 747 | file.close(); |
| 748 | // Generous timeout to avoid failures on heavily loaded systems. |
| 749 | try evt.timedWait(SLEEP_TIMEOUT_NS); |
| 761 | 750 | } |
| 762 | 751 | |
| 763 | 752 | test "open file with exclusive nonblocking lock twice (absolute paths)" { |
| ... | ... | @@ -780,71 +769,3 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" { |
| 780 | 769 | |
| 781 | 770 | try fs.deleteFileAbsolute(filename); |
| 782 | 771 | } |
| 783 | | |
| 784 | | const FileLockTestContext = struct { |
| 785 | | filename: []const u8, |
| 786 | | pid: if (builtin.os.tag == .windows) ?void else ?std.os.pid_t = null, |
| 787 | | |
| 788 | | // use file.createFile |
| 789 | | create: bool, |
| 790 | | // the type of lock to use |
| 791 | | lock: File.Lock, |
| 792 | | |
| 793 | | // Output variables |
| 794 | | err: ?(File.OpenError || std.os.ReadError) = null, |
| 795 | | start_time: i64 = 0, |
| 796 | | end_time: i64 = 0, |
| 797 | | bytes_read: ?usize = null, |
| 798 | | |
| 799 | | fn overlaps(self: *const @This(), other: *const @This()) bool { |
| 800 | | return (self.start_time < other.end_time) and (self.end_time > other.start_time); |
| 801 | | } |
| 802 | | |
| 803 | | fn run(ctx: *@This()) void { |
| 804 | | var file: File = undefined; |
| 805 | | if (ctx.create) { |
| 806 | | file = fs.cwd().createFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| { |
| 807 | | ctx.err = err; |
| 808 | | return; |
| 809 | | }; |
| 810 | | } else { |
| 811 | | file = fs.cwd().openFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| { |
| 812 | | ctx.err = err; |
| 813 | | return; |
| 814 | | }; |
| 815 | | } |
| 816 | | defer file.close(); |
| 817 | | |
| 818 | | ctx.start_time = std.time.milliTimestamp(); |
| 819 | | |
| 820 | | if (!ctx.create) { |
| 821 | | var buffer: [100]u8 = undefined; |
| 822 | | ctx.bytes_read = 0; |
| 823 | | while (true) { |
| 824 | | const amt = file.read(buffer[0..]) catch |err| { |
| 825 | | ctx.err = err; |
| 826 | | return; |
| 827 | | }; |
| 828 | | if (amt == 0) break; |
| 829 | | ctx.bytes_read.? += amt; |
| 830 | | } |
| 831 | | } |
| 832 | | |
| 833 | | std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME); |
| 834 | | |
| 835 | | ctx.end_time = std.time.milliTimestamp(); |
| 836 | | } |
| 837 | | }; |
| 838 | | |
| 839 | | fn run_lock_file_test(contexts: []FileLockTestContext) !void { |
| 840 | | var threads = std.ArrayList(*std.Thread).init(testing.allocator); |
| 841 | | defer { |
| 842 | | for (threads.items) |thread| { |
| 843 | | thread.wait(); |
| 844 | | } |
| 845 | | threads.deinit(); |
| 846 | | } |
| 847 | | for (contexts) |*ctx, idx| { |
| 848 | | try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run)); |
| 849 | | } |
| 850 | | } |