| ... | ... | @@ -1723,51 +1723,59 @@ test "" { |
| 1723 | 1723 | |
| 1724 | 1724 | const FILE_LOCK_TEST_SLEEP_TIME = 1 * std.time.ns_per_s; |
| 1725 | 1725 | |
| 1726 | | test "open file with lock twice, make sure it wasn't open at the same time" { |
| 1727 | | const filename = "file_lock_test.txt"; |
| 1728 | | |
| 1729 | | if (builtin.os.tag == .windows) { |
| 1730 | | var ctxs = [_]FileLockTestContext{ |
| 1731 | | .{ .filename = filename }, |
| 1732 | | .{ .filename = filename }, |
| 1733 | | }; |
| 1734 | | |
| 1735 | | const threads = [_]*std.Thread{ |
| 1736 | | try std.Thread.spawn(&ctxs[0], lock_file_for_test), |
| 1737 | | try std.Thread.spawn(&ctxs[1], lock_file_for_test), |
| 1738 | | }; |
| 1739 | | |
| 1740 | | for (threads[0..]) |thread| { |
| 1741 | | thread.wait(); |
| 1742 | | } |
| 1743 | | |
| 1744 | | std.debug.assert(!ctxs[0].overlaps(&ctxs[1])); |
| 1745 | | } else { |
| 1746 | | 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); |
| 1747 | | defer std.os.munmap(shared_mem); |
| 1748 | | const ctxs = @ptrCast([*]FileLockTestContext, shared_mem.ptr); |
| 1749 | | |
| 1750 | | const childpid = try std.os.fork(); |
| 1751 | | const ctx_idx: usize = if (childpid != 0) 0 else 1; |
| 1752 | | |
| 1753 | | ctxs[ctx_idx].filename = filename; |
| 1754 | | lock_file_for_test(&ctxs[ctx_idx]); |
| 1755 | | |
| 1756 | | if (childpid != 0) { |
| 1757 | | _ = std.os.waitpid(childpid, 0); |
| 1758 | | |
| 1759 | | std.debug.assert(!ctxs[0].overlaps(&ctxs[1])); |
| 1760 | | } |
| 1761 | | } |
| 1762 | | |
| 1763 | | cwd().deleteFile(filename) catch |err| switch (err) { |
| 1764 | | error.FileNotFound => {}, |
| 1765 | | else => return err, |
| 1766 | | }; |
| 1767 | | } |
| 1726 | //test "open file with lock twice, make sure it wasn't open at the same time" { |
| 1727 | // const filename = "file_lock_test.txt"; |
| 1728 | // |
| 1729 | // if (builtin.os.tag == .windows) { |
| 1730 | // var ctxs = [_]FileLockTestContext{ |
| 1731 | // .{ .filename = filename }, |
| 1732 | // .{ .filename = filename }, |
| 1733 | // }; |
| 1734 | // |
| 1735 | // const threads = [_]*std.Thread{ |
| 1736 | // try std.Thread.spawn(&ctxs[0], lock_file_for_test), |
| 1737 | // try std.Thread.spawn(&ctxs[1], lock_file_for_test), |
| 1738 | // }; |
| 1739 | // |
| 1740 | // for (threads[0..]) |thread| { |
| 1741 | // thread.wait(); |
| 1742 | // } |
| 1743 | // |
| 1744 | // std.debug.assert(!ctxs[0].overlaps(&ctxs[1])); |
| 1745 | // } else { |
| 1746 | // 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); |
| 1747 | // defer std.os.munmap(shared_mem); |
| 1748 | // const ctxs = @ptrCast([*]FileLockTestContext, shared_mem.ptr); |
| 1749 | // |
| 1750 | // const childpid = try std.os.fork(); |
| 1751 | // const ctx_idx: usize = if (childpid != 0) 0 else 1; |
| 1752 | // |
| 1753 | // ctxs[ctx_idx].filename = filename; |
| 1754 | // lock_file_for_test(&ctxs[ctx_idx]); |
| 1755 | // |
| 1756 | // if (childpid != 0) { |
| 1757 | // _ = std.os.waitpid(childpid, 0); |
| 1758 | // |
| 1759 | // std.debug.assert(!ctxs[0].overlaps(&ctxs[1])); |
| 1760 | // } else { |
| 1761 | // std.os.exit(0); |
| 1762 | // } |
| 1763 | // } |
| 1764 | // |
| 1765 | // cwd().deleteFile(filename) catch |err| switch (err) { |
| 1766 | // error.FileNotFound => {}, |
| 1767 | // else => return err, |
| 1768 | // }; |
| 1769 | //} |
| 1768 | 1770 | |
| 1769 | 1771 | const FileLockTestContext = struct { |
| 1770 | 1772 | filename: []const u8, |
| 1773 | pid: if (builtin.os.tag == .windows) ?void else ?std.os.pid_t = null, |
| 1774 | |
| 1775 | // use file.createFile |
| 1776 | create: bool, |
| 1777 | // get a read/write lock, instead of just a read lock |
| 1778 | exclusive: bool, |
| 1771 | 1779 | |
| 1772 | 1780 | // Output variables |
| 1773 | 1781 | start_time: u64 = 0, |
| ... | ... | @@ -1777,15 +1785,30 @@ const FileLockTestContext = struct { |
| 1777 | 1785 | fn overlaps(self: *const @This(), other: *const @This()) bool { |
| 1778 | 1786 | return (self.start_time < other.end_time) and (self.end_time > other.start_time); |
| 1779 | 1787 | } |
| 1780 | | }; |
| 1781 | 1788 | |
| 1782 | | fn lock_file_for_test(ctx: *FileLockTestContext) void { |
| 1783 | | const file = cwd().createFile(ctx.filename, .{ .lock = true }) catch unreachable; |
| 1784 | | ctx.start_time = std.time.milliTimestamp(); |
| 1785 | | std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME); |
| 1786 | | ctx.end_time = std.time.milliTimestamp(); |
| 1787 | | file.close(); |
| 1788 | | } |
| 1789 | fn run(ctx: *@This()) void { |
| 1790 | var file: File = undefined; |
| 1791 | if (ctx.create) { |
| 1792 | file = cwd().createFile(ctx.filename, .{ .lock = true }) catch unreachable; |
| 1793 | } else { |
| 1794 | file = cwd().openFile(ctx.filename, .{ .lock = true, .write = ctx.exclusive }) catch unreachable; |
| 1795 | } |
| 1796 | |
| 1797 | ctx.start_time = std.time.milliTimestamp(); |
| 1798 | |
| 1799 | var buffer: [100]u8 = undefined; |
| 1800 | ctx.bytes_read = 0; |
| 1801 | while (true) { |
| 1802 | const amt = file.read(buffer[0..]) catch unreachable; |
| 1803 | if (amt == 0) break; |
| 1804 | ctx.bytes_read.? += amt; |
| 1805 | } |
| 1806 | std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME); |
| 1807 | |
| 1808 | ctx.end_time = std.time.milliTimestamp(); |
| 1809 | file.close(); |
| 1810 | } |
| 1811 | }; |
| 1789 | 1812 | |
| 1790 | 1813 | test "create file, lock and read from multiple process at once" { |
| 1791 | 1814 | const filename = "file_read_lock_test.txt"; |
| ... | ... | @@ -1793,42 +1816,76 @@ test "create file, lock and read from multiple process at once" { |
| 1793 | 1816 | |
| 1794 | 1817 | try std.fs.cwd().writeFile(filename, filedata); |
| 1795 | 1818 | |
| 1819 | const NUM_PROCESSES = 3; |
| 1820 | var shared_mem: if (builtin.os.tag == .windows) [NUM_PROCESSES]FileLockTestContext else []align(mem.page_size) u8 = undefined; |
| 1821 | |
| 1822 | var ctxs: []FileLockTestContext = undefined; |
| 1796 | 1823 | if (builtin.os.tag == .windows) { |
| 1797 | | var ctxs = [_]FileLockTestContext{ |
| 1798 | | .{ .filename = filename }, |
| 1799 | | .{ .filename = filename }, |
| 1800 | | }; |
| 1824 | ctxs = shared_mem[0..NUM_PROCESSES]; |
| 1825 | } else { |
| 1826 | shared_mem = try std.os.mmap(null, NUM_PROCESSES * @sizeOf(FileLockTestContext), std.os.PROT_READ | std.os.PROT_WRITE, std.os.MAP_SHARED | std.os.MAP_ANONYMOUS, -1, 0); |
| 1827 | const ctxs_ptr = @ptrCast([*]FileLockTestContext, shared_mem.ptr); |
| 1828 | ctxs = ctxs_ptr[0..NUM_PROCESSES]; |
| 1829 | } |
| 1801 | 1830 | |
| 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 | | }; |
| 1831 | ctxs[0] = .{ |
| 1832 | .filename = filename, |
| 1833 | .create = false, |
| 1834 | .exclusive = false, |
| 1835 | }; |
| 1836 | ctxs[1] = .{ |
| 1837 | .filename = filename, |
| 1838 | .create = false, |
| 1839 | .exclusive = false, |
| 1840 | }; |
| 1841 | ctxs[2] = .{ |
| 1842 | .filename = filename, |
| 1843 | .create = false, |
| 1844 | .exclusive = true, |
| 1845 | }; |
| 1846 | |
| 1847 | if (builtin.os.tag == .windows) { |
| 1848 | const threads: [NUM_PROCESSES]*std.Thread = undefined; |
| 1849 | for (ctxs) |*ctx, idx| { |
| 1850 | threads[idx] = try std.Thread.spawn(ctx, Context.run); |
| 1851 | } |
| 1806 | 1852 | |
| 1807 | 1853 | for (threads[0..]) |thread| { |
| 1808 | 1854 | thread.wait(); |
| 1809 | 1855 | } |
| 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 | 1856 | } 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; |
| 1857 | var ctx_opt: ?*FileLockTestContext = null; |
| 1858 | for (ctxs) |*ctx| { |
| 1859 | const childpid = try std.os.fork(); |
| 1860 | if (childpid == 0) { |
| 1861 | ctx_opt = ctx; |
| 1862 | break; |
| 1863 | } |
| 1864 | ctx.pid = childpid; |
| 1865 | } |
| 1821 | 1866 | |
| 1822 | | ctxs[ctx_idx].filename = filename; |
| 1823 | | lock_file_for_read_test(&ctxs[ctx_idx]); |
| 1867 | if (ctx_opt) |ctx| { |
| 1868 | ctx.run(); |
| 1869 | // Exit so we don't have duplicate test processes |
| 1870 | std.os.exit(0); |
| 1871 | } else { |
| 1872 | for (ctxs) |ctx| { |
| 1873 | _ = std.os.waitpid(ctx.pid.?, 0); |
| 1874 | } |
| 1875 | } |
| 1876 | } |
| 1824 | 1877 | |
| 1825 | | if (childpid != 0) { |
| 1826 | | _ = std.os.waitpid(childpid, 0); |
| 1878 | std.debug.assert(ctxs[0].overlaps(&ctxs[1])); |
| 1879 | std.debug.assert(!ctxs[2].overlaps(&ctxs[0])); |
| 1880 | std.debug.assert(!ctxs[2].overlaps(&ctxs[1])); |
| 1881 | if (ctxs[0].bytes_read.? != filedata.len) { |
| 1882 | std.debug.warn("\n bytes_read: {}, expected: {} \n", .{ ctxs[0].bytes_read, filedata.len }); |
| 1883 | } |
| 1884 | std.debug.assert(ctxs[0].bytes_read.? == filedata.len); |
| 1885 | std.debug.assert(ctxs[1].bytes_read.? == filedata.len); |
| 1827 | 1886 | |
| 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 | | } |
| 1887 | if (builtin.os.tag != .windows) { |
| 1888 | std.os.munmap(shared_mem); |
| 1832 | 1889 | } |
| 1833 | 1890 | |
| 1834 | 1891 | cwd().deleteFile(filename) catch |err| switch (err) { |
| ... | ... | @@ -1836,20 +1893,3 @@ test "create file, lock and read from multiple process at once" { |
| 1836 | 1893 | else => return err, |
| 1837 | 1894 | }; |
| 1838 | 1895 | } |
| 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 | | } |