| ... | @@ -1733,8 +1733,8 @@ test "open file with lock twice, make sure it wasn't open at the same time" { | ... | @@ -1733,8 +1733,8 @@ test "open file with lock twice, make sure it wasn't open at the same time" { |
| 1733 | }; | 1733 | }; |
| 1734 | | 1734 | |
| 1735 | const threads = [_]*std.Thread{ | 1735 | const threads = [_]*std.Thread{ |
| 1736 | try std.Thread.spawn(&ctxs[0], lock_file), | 1736 | try std.Thread.spawn(&ctxs[0], lock_file_for_test), |
| 1737 | try std.Thread.spawn(&ctxs[1], lock_file), | 1737 | try std.Thread.spawn(&ctxs[1], lock_file_for_test), |
| 1738 | }; | 1738 | }; |
| 1739 | | 1739 | |
| 1740 | for (threads[0..]) |thread| { | 1740 | for (threads[0..]) |thread| { |
| ... | @@ -1772,6 +1772,7 @@ const FileLockTestContext = struct { | ... | @@ -1772,6 +1772,7 @@ const FileLockTestContext = struct { |
| 1772 | // Output variables | 1772 | // Output variables |
| 1773 | start_time: u64 = 0, | 1773 | start_time: u64 = 0, |
| 1774 | end_time: u64 = 0, | 1774 | end_time: u64 = 0, |
| | 1775 | bytes_read: ?usize = null, |
| 1775 | | 1776 | |
| 1776 | fn overlaps(self: *const @This(), other: *const @This()) bool { | 1777 | fn overlaps(self: *const @This(), other: *const @This()) bool { |
| 1777 | return (self.start_time < other.end_time) and (self.end_time > other.start_time); | 1778 | return (self.start_time < other.end_time) and (self.end_time > other.start_time); |
| ... | @@ -1785,3 +1786,70 @@ fn lock_file_for_test(ctx: *FileLockTestContext) void { | ... | @@ -1785,3 +1786,70 @@ fn lock_file_for_test(ctx: *FileLockTestContext) void { |
| 1785 | ctx.end_time = std.time.milliTimestamp(); | 1786 | ctx.end_time = std.time.milliTimestamp(); |
| 1786 | file.close(); | 1787 | file.close(); |
| 1787 | } | 1788 | } |
| | 1789 | |
| | 1790 | test "create file, lock and read from multiple process at once" { |
| | 1791 | const filename = "file_read_lock_test.txt"; |
| | 1792 | const filedata = "Hello, world!\n"; |
| | 1793 | |
| | 1794 | try std.fs.cwd().writeFile(filename, filedata); |
| | 1795 | |
| | 1796 | if (builtin.os.tag == .windows) { |
| | 1797 | var ctxs = [_]FileLockTestContext{ |
| | 1798 | .{ .filename = filename }, |
| | 1799 | .{ .filename = filename }, |
| | 1800 | }; |
| | 1801 | |
| | 1802 | const threads = [_]*std.Thread{ |
| | 1803 | try std.Thread.spawn(&ctxs[0], lock_file_for_read_test), |
| | 1804 | try std.Thread.spawn(&ctxs[1], lock_file_for_read_test), |
| | 1805 | }; |
| | 1806 | |
| | 1807 | for (threads[0..]) |thread| { |
| | 1808 | thread.wait(); |
| | 1809 | } |
| | 1810 | |
| | 1811 | std.debug.assert(ctxs[0].overlaps(&ctxs[1])); |
| | 1812 | std.debug.assert(ctxs[0].bytes_read.? == filedata.len); |
| | 1813 | std.debug.assert(ctxs[1].bytes_read.? == filedata.len); |
| | 1814 | } else { |
| | 1815 | const shared_mem = try std.os.mmap(null, 2 * @sizeOf(FileLockTestContext), std.os.PROT_READ | std.os.PROT_WRITE, std.os.MAP_SHARED | std.os.MAP_ANONYMOUS, -1, 0); |
| | 1816 | defer std.os.munmap(shared_mem); |
| | 1817 | const ctxs = @ptrCast([*]FileLockTestContext, shared_mem.ptr); |
| | 1818 | |
| | 1819 | const childpid = try std.os.fork(); |
| | 1820 | const ctx_idx: usize = if (childpid != 0) 0 else 1; |
| | 1821 | |
| | 1822 | ctxs[ctx_idx].filename = filename; |
| | 1823 | lock_file_for_read_test(&ctxs[ctx_idx]); |
| | 1824 | |
| | 1825 | if (childpid != 0) { |
| | 1826 | _ = std.os.waitpid(childpid, 0); |
| | 1827 | |
| | 1828 | std.debug.assert(ctxs[0].overlaps(&ctxs[1])); |
| | 1829 | std.debug.assert(ctxs[0].bytes_read.? == filedata.len); |
| | 1830 | std.debug.assert(ctxs[1].bytes_read.? == filedata.len); |
| | 1831 | } |
| | 1832 | } |
| | 1833 | |
| | 1834 | cwd().deleteFile(filename) catch |err| switch (err) { |
| | 1835 | error.FileNotFound => {}, |
| | 1836 | else => return err, |
| | 1837 | }; |
| | 1838 | } |
| | 1839 | |
| | 1840 | fn lock_file_for_read_test(ctx: *FileLockTestContext) void { |
| | 1841 | const file = cwd().openFile(ctx.filename, .{ .lock = true }) catch unreachable; |
| | 1842 | ctx.start_time = std.time.milliTimestamp(); |
| | 1843 | |
| | 1844 | var buffer: [100]u8 = undefined; |
| | 1845 | ctx.bytes_read = 0; |
| | 1846 | while (true) { |
| | 1847 | const amt = file.read(buffer[0..]) catch unreachable; |
| | 1848 | if (amt == 0) break; |
| | 1849 | ctx.bytes_read.? += amt; |
| | 1850 | } |
| | 1851 | std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME); |
| | 1852 | |
| | 1853 | ctx.end_time = std.time.milliTimestamp(); |
| | 1854 | file.close(); |
| | 1855 | } |