| ... | @@ -1723,51 +1723,59 @@ test "" { | ... | @@ -1723,51 +1723,59 @@ test "" { |
| 1723 | | 1723 | |
| 1724 | const FILE_LOCK_TEST_SLEEP_TIME = 1 * std.time.ns_per_s; | 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" { | 1726 | //test "open file with lock twice, make sure it wasn't open at the same time" { |
| 1727 | const filename = "file_lock_test.txt"; | 1727 | // const filename = "file_lock_test.txt"; |
| 1728 | | 1728 | // |
| 1729 | if (builtin.os.tag == .windows) { | 1729 | // if (builtin.os.tag == .windows) { |
| 1730 | var ctxs = [_]FileLockTestContext{ | 1730 | // var ctxs = [_]FileLockTestContext{ |
| 1731 | .{ .filename = filename }, | 1731 | // .{ .filename = filename }, |
| 1732 | .{ .filename = filename }, | 1732 | // .{ .filename = filename }, |
| 1733 | }; | 1733 | // }; |
| 1734 | | 1734 | // |
| 1735 | const threads = [_]*std.Thread{ | 1735 | // const threads = [_]*std.Thread{ |
| 1736 | try std.Thread.spawn(&ctxs[0], lock_file_for_test), | 1736 | // try std.Thread.spawn(&ctxs[0], lock_file_for_test), |
| 1737 | try std.Thread.spawn(&ctxs[1], lock_file_for_test), | 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| { |
| 1741 | thread.wait(); | 1741 | // thread.wait(); |
| 1742 | } | 1742 | // } |
| 1743 | | 1743 | // |
| 1744 | std.debug.assert(!ctxs[0].overlaps(&ctxs[1])); | 1744 | // std.debug.assert(!ctxs[0].overlaps(&ctxs[1])); |
| 1745 | } else { | 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); | 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); | 1747 | // defer std.os.munmap(shared_mem); |
| 1748 | const ctxs = @ptrCast([*]FileLockTestContext, shared_mem.ptr); | 1748 | // const ctxs = @ptrCast([*]FileLockTestContext, shared_mem.ptr); |
| 1749 | | 1749 | // |
| 1750 | const childpid = try std.os.fork(); | 1750 | // const childpid = try std.os.fork(); |
| 1751 | const ctx_idx: usize = if (childpid != 0) 0 else 1; | 1751 | // const ctx_idx: usize = if (childpid != 0) 0 else 1; |
| 1752 | | 1752 | // |
| 1753 | ctxs[ctx_idx].filename = filename; | 1753 | // ctxs[ctx_idx].filename = filename; |
| 1754 | lock_file_for_test(&ctxs[ctx_idx]); | 1754 | // lock_file_for_test(&ctxs[ctx_idx]); |
| 1755 | | 1755 | // |
| 1756 | if (childpid != 0) { | 1756 | // if (childpid != 0) { |
| 1757 | _ = std.os.waitpid(childpid, 0); | 1757 | // _ = std.os.waitpid(childpid, 0); |
| 1758 | | 1758 | // |
| 1759 | std.debug.assert(!ctxs[0].overlaps(&ctxs[1])); | 1759 | // std.debug.assert(!ctxs[0].overlaps(&ctxs[1])); |
| 1760 | } | 1760 | // } else { |
| 1761 | } | 1761 | // std.os.exit(0); |
| 1762 | | 1762 | // } |
| 1763 | cwd().deleteFile(filename) catch |err| switch (err) { | 1763 | // } |
| 1764 | error.FileNotFound => {}, | 1764 | // |
| 1765 | else => return err, | 1765 | // cwd().deleteFile(filename) catch |err| switch (err) { |
| 1766 | }; | 1766 | // error.FileNotFound => {}, |
| 1767 | } | 1767 | // else => return err, |
| | 1768 | // }; |
| | 1769 | //} |
| 1768 | | 1770 | |
| 1769 | const FileLockTestContext = struct { | 1771 | const FileLockTestContext = struct { |
| 1770 | filename: []const u8, | 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 | // Output variables | 1780 | // Output variables |
| 1773 | start_time: u64 = 0, | 1781 | start_time: u64 = 0, |
| ... | @@ -1777,15 +1785,30 @@ const FileLockTestContext = struct { | ... | @@ -1777,15 +1785,30 @@ const FileLockTestContext = struct { |
| 1777 | fn overlaps(self: *const @This(), other: *const @This()) bool { | 1785 | fn overlaps(self: *const @This(), other: *const @This()) bool { |
| 1778 | return (self.start_time < other.end_time) and (self.end_time > other.start_time); | 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 { | 1789 | fn run(ctx: *@This()) void { |
| 1783 | const file = cwd().createFile(ctx.filename, .{ .lock = true }) catch unreachable; | 1790 | var file: File = undefined; |
| 1784 | ctx.start_time = std.time.milliTimestamp(); | 1791 | if (ctx.create) { |
| 1785 | std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME); | 1792 | file = cwd().createFile(ctx.filename, .{ .lock = true }) catch unreachable; |
| 1786 | ctx.end_time = std.time.milliTimestamp(); | 1793 | } else { |
| 1787 | file.close(); | 1794 | file = cwd().openFile(ctx.filename, .{ .lock = true, .write = ctx.exclusive }) catch unreachable; |
| 1788 | } | 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 | test "create file, lock and read from multiple process at once" { | 1813 | test "create file, lock and read from multiple process at once" { |
| 1791 | const filename = "file_read_lock_test.txt"; | 1814 | const filename = "file_read_lock_test.txt"; |
| ... | @@ -1793,42 +1816,76 @@ test "create file, lock and read from multiple process at once" { | ... | @@ -1793,42 +1816,76 @@ test "create file, lock and read from multiple process at once" { |
| 1793 | | 1816 | |
| 1794 | try std.fs.cwd().writeFile(filename, filedata); | 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 | if (builtin.os.tag == .windows) { | 1823 | if (builtin.os.tag == .windows) { |
| 1797 | var ctxs = [_]FileLockTestContext{ | 1824 | ctxs = shared_mem[0..NUM_PROCESSES]; |
| 1798 | .{ .filename = filename }, | 1825 | } else { |
| 1799 | .{ .filename = filename }, | 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); |
| 1800 | }; | 1827 | const ctxs_ptr = @ptrCast([*]FileLockTestContext, shared_mem.ptr); |
| | 1828 | ctxs = ctxs_ptr[0..NUM_PROCESSES]; |
| | 1829 | } |
| 1801 | | 1830 | |
| 1802 | const threads = [_]*std.Thread{ | 1831 | ctxs[0] = .{ |
| 1803 | try std.Thread.spawn(&ctxs[0], lock_file_for_read_test), | 1832 | .filename = filename, |
| 1804 | try std.Thread.spawn(&ctxs[1], lock_file_for_read_test), | 1833 | .create = false, |
| 1805 | }; | 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 | for (threads[0..]) |thread| { | 1853 | for (threads[0..]) |thread| { |
| 1808 | thread.wait(); | 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 | } else { | 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); | 1857 | var ctx_opt: ?*FileLockTestContext = null; |
| 1816 | defer std.os.munmap(shared_mem); | 1858 | for (ctxs) |*ctx| { |
| 1817 | const ctxs = @ptrCast([*]FileLockTestContext, shared_mem.ptr); | 1859 | const childpid = try std.os.fork(); |
| 1818 | | 1860 | if (childpid == 0) { |
| 1819 | const childpid = try std.os.fork(); | 1861 | ctx_opt = ctx; |
| 1820 | const ctx_idx: usize = if (childpid != 0) 0 else 1; | 1862 | break; |
| | 1863 | } |
| | 1864 | ctx.pid = childpid; |
| | 1865 | } |
| 1821 | | 1866 | |
| 1822 | ctxs[ctx_idx].filename = filename; | 1867 | if (ctx_opt) |ctx| { |
| 1823 | lock_file_for_read_test(&ctxs[ctx_idx]); | 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) { | 1878 | std.debug.assert(ctxs[0].overlaps(&ctxs[1])); |
| 1826 | _ = std.os.waitpid(childpid, 0); | 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])); | 1887 | if (builtin.os.tag != .windows) { |
| 1829 | std.debug.assert(ctxs[0].bytes_read.? == filedata.len); | 1888 | std.os.munmap(shared_mem); |
| 1830 | std.debug.assert(ctxs[1].bytes_read.? == filedata.len); | | |
| 1831 | } | | |
| 1832 | } | 1889 | } |
| 1833 | | 1890 | |
| 1834 | cwd().deleteFile(filename) catch |err| switch (err) { | 1891 | cwd().deleteFile(filename) catch |err| switch (err) { |
| ... | @@ -1836,20 +1893,3 @@ test "create file, lock and read from multiple process at once" { | ... | @@ -1836,20 +1893,3 @@ test "create file, lock and read from multiple process at once" { |
| 1836 | else => return err, | 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 | } | | |