| ... | ... | @@ -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 | 1735 | const threads = [_]*std.Thread{ |
| 1736 | | try std.Thread.spawn(&ctxs[0], lock_file), |
| 1737 | | try std.Thread.spawn(&ctxs[1], lock_file), |
| 1736 | try std.Thread.spawn(&ctxs[0], lock_file_for_test), |
| 1737 | try std.Thread.spawn(&ctxs[1], lock_file_for_test), |
| 1738 | 1738 | }; |
| 1739 | 1739 | |
| 1740 | 1740 | for (threads[0..]) |thread| { |
| ... | ... | @@ -1772,6 +1772,7 @@ const FileLockTestContext = struct { |
| 1772 | 1772 | // Output variables |
| 1773 | 1773 | start_time: u64 = 0, |
| 1774 | 1774 | end_time: u64 = 0, |
| 1775 | bytes_read: ?usize = null, |
| 1775 | 1776 | |
| 1776 | 1777 | fn overlaps(self: *const @This(), other: *const @This()) bool { |
| 1777 | 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 | 1786 | ctx.end_time = std.time.milliTimestamp(); |
| 1786 | 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 | } |