| ... | ... | @@ -1723,50 +1723,70 @@ 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 | | // } 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 | | //} |
| 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 | var contexts = [_]FileLockTestContext{ |
| 1730 | .{ .filename = filename, .create = true, .exclusive = true }, |
| 1731 | .{ .filename = filename, .create = true, .exclusive = true }, |
| 1732 | }; |
| 1733 | try run_lock_file_test(&contexts); |
| 1734 | |
| 1735 | // Check for an error |
| 1736 | var was_error = false; |
| 1737 | for (contexts) |context, idx| { |
| 1738 | if (context.err) |err| { |
| 1739 | was_error = true; |
| 1740 | std.debug.warn("\nError in context {}: {}\n", .{ idx, err }); |
| 1741 | } |
| 1742 | } |
| 1743 | if (was_error) builtin.panic("There was an error in contexts", null); |
| 1744 | |
| 1745 | std.debug.assert(!contexts[0].overlaps(&contexts[1])); |
| 1746 | |
| 1747 | cwd().deleteFile(filename) catch |err| switch (err) { |
| 1748 | error.FileNotFound => {}, |
| 1749 | else => return err, |
| 1750 | }; |
| 1751 | } |
| 1752 | |
| 1753 | test "create file, lock and read from multiple process at once" { |
| 1754 | const filename = "file_read_lock_test.txt"; |
| 1755 | const filedata = "Hello, world!\n"; |
| 1756 | |
| 1757 | try std.fs.cwd().writeFile(filename, filedata); |
| 1758 | |
| 1759 | var contexts = [_]FileLockTestContext{ |
| 1760 | .{ .filename = filename, .create = false, .exclusive = false }, |
| 1761 | .{ .filename = filename, .create = false, .exclusive = false }, |
| 1762 | .{ .filename = filename, .create = false, .exclusive = true }, |
| 1763 | }; |
| 1764 | |
| 1765 | try run_lock_file_test(&contexts); |
| 1766 | |
| 1767 | var was_error = false; |
| 1768 | for (contexts) |context, idx| { |
| 1769 | if (context.err) |err| { |
| 1770 | was_error = true; |
| 1771 | std.debug.warn("\nError in context {}: {}\n", .{ idx, err }); |
| 1772 | } |
| 1773 | } |
| 1774 | if (was_error) builtin.panic("There was an error in contexts", null); |
| 1775 | |
| 1776 | std.debug.assert(contexts[0].overlaps(&contexts[1])); |
| 1777 | std.debug.assert(!contexts[2].overlaps(&contexts[0])); |
| 1778 | std.debug.assert(!contexts[2].overlaps(&contexts[1])); |
| 1779 | if (contexts[0].bytes_read.? != filedata.len) { |
| 1780 | std.debug.warn("\n bytes_read: {}, expected: {} \n", .{ contexts[0].bytes_read, filedata.len }); |
| 1781 | } |
| 1782 | std.debug.assert(contexts[0].bytes_read.? == filedata.len); |
| 1783 | std.debug.assert(contexts[1].bytes_read.? == filedata.len); |
| 1784 | |
| 1785 | cwd().deleteFile(filename) catch |err| switch (err) { |
| 1786 | error.FileNotFound => {}, |
| 1787 | else => return err, |
| 1788 | }; |
| 1789 | } |
| 1770 | 1790 | |
| 1771 | 1791 | const FileLockTestContext = struct { |
| 1772 | 1792 | filename: []const u8, |
| ... | ... | @@ -1778,6 +1798,7 @@ const FileLockTestContext = struct { |
| 1778 | 1798 | exclusive: bool, |
| 1779 | 1799 | |
| 1780 | 1800 | // Output variables |
| 1801 | err: ?(File.OpenError || std.os.ReadError) = null, |
| 1781 | 1802 | start_time: u64 = 0, |
| 1782 | 1803 | end_time: u64 = 0, |
| 1783 | 1804 | bytes_read: ?usize = null, |
| ... | ... | @@ -1789,69 +1810,65 @@ const FileLockTestContext = struct { |
| 1789 | 1810 | fn run(ctx: *@This()) void { |
| 1790 | 1811 | var file: File = undefined; |
| 1791 | 1812 | if (ctx.create) { |
| 1792 | | file = cwd().createFile(ctx.filename, .{ .lock = true }) catch unreachable; |
| 1813 | file = cwd().createFile(ctx.filename, .{ .lock = true }) catch |err| { |
| 1814 | ctx.err = err; |
| 1815 | return; |
| 1816 | }; |
| 1793 | 1817 | } else { |
| 1794 | | file = cwd().openFile(ctx.filename, .{ .lock = true, .write = ctx.exclusive }) catch unreachable; |
| 1818 | file = cwd().openFile(ctx.filename, .{ .lock = true, .write = ctx.exclusive }) catch |err| { |
| 1819 | ctx.err = err; |
| 1820 | return; |
| 1821 | }; |
| 1795 | 1822 | } |
| 1823 | defer file.close(); |
| 1796 | 1824 | |
| 1797 | 1825 | ctx.start_time = std.time.milliTimestamp(); |
| 1798 | 1826 | |
| 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; |
| 1827 | if (!ctx.create) { |
| 1828 | var buffer: [100]u8 = undefined; |
| 1829 | ctx.bytes_read = 0; |
| 1830 | while (true) { |
| 1831 | const amt = file.read(buffer[0..]) catch |err| { |
| 1832 | ctx.err = err; |
| 1833 | return; |
| 1834 | }; |
| 1835 | if (amt == 0) break; |
| 1836 | ctx.bytes_read.? += amt; |
| 1837 | } |
| 1805 | 1838 | } |
| 1839 | |
| 1806 | 1840 | std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME); |
| 1807 | 1841 | |
| 1808 | 1842 | ctx.end_time = std.time.milliTimestamp(); |
| 1809 | | file.close(); |
| 1810 | 1843 | } |
| 1811 | 1844 | }; |
| 1812 | 1845 | |
| 1813 | | test "create file, lock and read from multiple process at once" { |
| 1814 | | const filename = "file_read_lock_test.txt"; |
| 1815 | | const filedata = "Hello, world!\n"; |
| 1816 | | |
| 1817 | | try std.fs.cwd().writeFile(filename, filedata); |
| 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; |
| 1846 | fn run_lock_file_test(contexts: []FileLockTestContext) !void { |
| 1847 | var shared_mem: if (builtin.os.tag == .windows) void else []align(mem.page_size) u8 = undefined; |
| 1821 | 1848 | |
| 1822 | 1849 | var ctxs: []FileLockTestContext = undefined; |
| 1823 | 1850 | if (builtin.os.tag == .windows) { |
| 1824 | | ctxs = shared_mem[0..NUM_PROCESSES]; |
| 1851 | ctxs = contexts; |
| 1825 | 1852 | } 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); |
| 1853 | shared_mem = try std.os.mmap(null, contexts.len * @sizeOf(FileLockTestContext), std.os.PROT_READ | std.os.PROT_WRITE, std.os.MAP_SHARED | std.os.MAP_ANONYMOUS, -1, 0); |
| 1827 | 1854 | const ctxs_ptr = @ptrCast([*]FileLockTestContext, shared_mem.ptr); |
| 1828 | | ctxs = ctxs_ptr[0..NUM_PROCESSES]; |
| 1829 | | } |
| 1855 | ctxs = ctxs_ptr[0..contexts.len]; |
| 1830 | 1856 | |
| 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 | | }; |
| 1857 | for (contexts) |context, idx| { |
| 1858 | ctxs[idx] = context; |
| 1859 | } |
| 1860 | } |
| 1846 | 1861 | |
| 1847 | 1862 | 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); |
| 1863 | const threads = std.ArrayList(*std.Thread).init(testing.allocator); |
| 1864 | defer { |
| 1865 | for (threads.toSlice()) |thread| { |
| 1866 | thread.wait(); |
| 1867 | } |
| 1868 | threads.deinit(); |
| 1851 | 1869 | } |
| 1852 | | |
| 1853 | | for (threads[0..]) |thread| { |
| 1854 | | thread.wait(); |
| 1870 | for (ctxs) |*ctx, idx| { |
| 1871 | threads.append(try std.Thread.spawn(ctx, Context.run)); |
| 1855 | 1872 | } |
| 1856 | 1873 | } else { |
| 1857 | 1874 | var ctx_opt: ?*FileLockTestContext = null; |
| ... | ... | @@ -1875,21 +1892,11 @@ test "create file, lock and read from multiple process at once" { |
| 1875 | 1892 | } |
| 1876 | 1893 | } |
| 1877 | 1894 | |
| 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); |
| 1886 | | |
| 1887 | 1895 | if (builtin.os.tag != .windows) { |
| 1896 | // Copy contexts out of shared memory |
| 1897 | for (ctxs) |ctx, idx| { |
| 1898 | contexts[idx] = ctx; |
| 1899 | } |
| 1888 | 1900 | std.os.munmap(shared_mem); |
| 1889 | 1901 | } |
| 1890 | | |
| 1891 | | cwd().deleteFile(filename) catch |err| switch (err) { |
| 1892 | | error.FileNotFound => {}, |
| 1893 | | else => return err, |
| 1894 | | }; |
| 1895 | 1902 | } |