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 "" {...@@ -1723,51 +1723,59 @@ test "" {
17231723
1724const FILE_LOCK_TEST_SLEEP_TIME = 1 * std.time.ns_per_s;1724const 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" {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";
17281728//
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// };
17341734//
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// };
17391739//
1740 for (threads[0..]) |thread| {1740// for (threads[0..]) |thread| {
1741 thread.wait();1741// thread.wait();
1742 }1742// }
17431743//
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);
17491749//
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;
17521752//
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]);
17551755//
1756 if (childpid != 0) {1756// if (childpid != 0) {
1757 _ = std.os.waitpid(childpid, 0);1757// _ = std.os.waitpid(childpid, 0);
17581758//
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);
17621762// }
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//}
17681770
1769const FileLockTestContext = struct {1771const 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,
17711779
1772 // Output variables1780 // 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};
17811788
1782fn 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};
17891812
1790test "create file, lock and read from multiple process at once" {1813test "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" {
17931816
1794 try std.fs.cwd().writeFile(filename, filedata);1817 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;
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 }
18011830
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 }
18061852
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();
18181860 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 }
18211866
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 }
18241877
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);
18271886
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 }
18331890
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
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}