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