authorgravatar for leroycepearson@geemili.xyzLeRoyce Pearson <leroycepearson@geemili.xyz> 2020-03-14 14:57:56-06:00
committergravatar for leroycepearson@geemili.xyzLeRoyce Pearson <leroycepearson@geemili.xyz> 2020-03-14 14:57:56-06:00
log7ab77685a8eb69293fc0229f7b01a6a67332740a
tree7c5e69d3351df7e966fd40ce9ac171ffb85b8b9f
parent873c7a59e9943a944cf157345e9e27275b9069ba

Make lock tests more flexible


1 files changed, 133 insertions(+), 93 deletions(-)

lib/std/fs.zig+133-93
......@@ -1723,51 +1723,59 @@ test "" {
17231723
17241724const FILE_LOCK_TEST_SLEEP_TIME = 1 * std.time.ns_per_s;
17251725
1726test "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//}
17681770
17691771const FileLockTestContext = struct {
17701772 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,
17711779
17721780 // Output variables
17731781 start_time: u64 = 0,
......@@ -1777,15 +1785,30 @@ const FileLockTestContext = struct {
17771785 fn overlaps(self: *const @This(), other: *const @This()) bool {
17781786 return (self.start_time < other.end_time) and (self.end_time > other.start_time);
17791787 }
1780};
17811788
1782fn 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};
17891812
17901813test "create file, lock and read from multiple process at once" {
17911814 const filename = "file_read_lock_test.txt";
......@@ -1793,42 +1816,76 @@ test "create file, lock and read from multiple process at once" {
17931816
17941817 try std.fs.cwd().writeFile(filename, filedata);
17951818
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;
17961823 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 }
18011830
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 }
18061852
18071853 for (threads[0..]) |thread| {
18081854 thread.wait();
18091855 }
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);
18141856 } 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 }
18211866
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 }
18241877
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);
18271886
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);
18321889 }
18331890
18341891 cwd().deleteFile(filename) catch |err| switch (err) {
......@@ -1836,20 +1893,3 @@ test "create file, lock and read from multiple process at once" {
18361893 else => return err,
18371894 };
18381895}
1839
1840fn 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}