| ... | @@ -1720,3 +1720,69 @@ test "" { | ... | @@ -1720,3 +1720,69 @@ test "" { |
| 1720 | _ = @import("fs/get_app_data_dir.zig"); | 1720 | _ = @import("fs/get_app_data_dir.zig"); |
| 1721 | _ = @import("fs/watch.zig"); | 1721 | _ = @import("fs/watch.zig"); |
| 1722 | } | 1722 | } |
| | 1723 | |
| | 1724 | const FILE_LOCK_TEST_SLEEP_TIME = 1 * std.time.ns_per_s; |
| | 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), |
| | 1737 | try std.Thread.spawn(&ctxs[1], lock_file), |
| | 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 | var status: u32 = 0; |
| | 1758 | _ = std.os.linux.waitpid(childpid, &status, 0); |
| | 1759 | |
| | 1760 | std.debug.assert(!ctxs[0].overlaps(&ctxs[1])); |
| | 1761 | } |
| | 1762 | } |
| | 1763 | |
| | 1764 | cwd().deleteFile(filename) catch |err| switch (err) { |
| | 1765 | error.FileNotFound => {}, |
| | 1766 | else => return err, |
| | 1767 | }; |
| | 1768 | } |
| | 1769 | |
| | 1770 | const FileLockTestContext = struct { |
| | 1771 | filename: []const u8, |
| | 1772 | |
| | 1773 | // Output variables |
| | 1774 | start_time: u64 = 0, |
| | 1775 | end_time: u64 = 0, |
| | 1776 | |
| | 1777 | fn overlaps(self: *const @This(), other: *const @This()) bool { |
| | 1778 | return (self.start_time < other.end_time) and (self.end_time > other.start_time); |
| | 1779 | } |
| | 1780 | }; |
| | 1781 | |
| | 1782 | fn 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 | } |